mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 16:26:43 +00:00
Add UI for Schedule Helper (#13375)
This commit is contained in:
parent
9046c0d0bf
commit
38607a6410
@ -46,6 +46,7 @@
|
||||
"@fullcalendar/daygrid": "5.9.0",
|
||||
"@fullcalendar/interaction": "5.9.0",
|
||||
"@fullcalendar/list": "5.9.0",
|
||||
"@fullcalendar/timegrid": "5.9.0",
|
||||
"@lit-labs/motion": "^1.0.2",
|
||||
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.7.0-pre.2#./.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch",
|
||||
"@material/chips": "14.0.0-canary.261f2db59.0",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import memoizeOne from "memoize-one";
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { useAmPm } from "./use_am_pm";
|
||||
import { polyfillsLoaded } from "../translations/localize";
|
||||
import { useAmPm } from "./use_am_pm";
|
||||
|
||||
if (__BUILD__ === "latest" && polyfillsLoaded) {
|
||||
await polyfillsLoaded;
|
||||
@ -64,3 +64,16 @@ const formatTimeWeekdayMem = memoizeOne(
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// 21:15
|
||||
export const formatTime24h = (dateObj: Date) =>
|
||||
formatTime24hMem().format(dateObj);
|
||||
|
||||
const formatTime24hMem = memoizeOne(
|
||||
() =>
|
||||
new Intl.DateTimeFormat(undefined, {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
})
|
||||
);
|
||||
|
@ -1,31 +1,32 @@
|
||||
import { fetchCounter, updateCounter, deleteCounter } from "./counter";
|
||||
import { deleteCounter, fetchCounter, updateCounter } from "./counter";
|
||||
import {
|
||||
deleteInputBoolean,
|
||||
fetchInputBoolean,
|
||||
updateInputBoolean,
|
||||
deleteInputBoolean,
|
||||
} from "./input_boolean";
|
||||
import {
|
||||
deleteInputButton,
|
||||
fetchInputButton,
|
||||
updateInputButton,
|
||||
deleteInputButton,
|
||||
} from "./input_button";
|
||||
import {
|
||||
deleteInputDateTime,
|
||||
fetchInputDateTime,
|
||||
updateInputDateTime,
|
||||
deleteInputDateTime,
|
||||
} from "./input_datetime";
|
||||
import {
|
||||
deleteInputNumber,
|
||||
fetchInputNumber,
|
||||
updateInputNumber,
|
||||
deleteInputNumber,
|
||||
} from "./input_number";
|
||||
import {
|
||||
deleteInputSelect,
|
||||
fetchInputSelect,
|
||||
updateInputSelect,
|
||||
deleteInputSelect,
|
||||
} from "./input_select";
|
||||
import { fetchInputText, updateInputText, deleteInputText } from "./input_text";
|
||||
import { fetchTimer, updateTimer, deleteTimer } from "./timer";
|
||||
import { deleteInputText, fetchInputText, updateInputText } from "./input_text";
|
||||
import { deleteSchedule, fetchSchedule, updateSchedule } from "./schedule";
|
||||
import { deleteTimer, fetchTimer, updateTimer } from "./timer";
|
||||
|
||||
export const HELPERS_CRUD = {
|
||||
input_boolean: {
|
||||
@ -68,4 +69,9 @@ export const HELPERS_CRUD = {
|
||||
update: updateTimer,
|
||||
delete: deleteTimer,
|
||||
},
|
||||
schedule: {
|
||||
fetch: fetchSchedule,
|
||||
update: updateSchedule,
|
||||
delete: deleteSchedule,
|
||||
},
|
||||
};
|
||||
|
61
src/data/schedule.ts
Normal file
61
src/data/schedule.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export const weekdays = [
|
||||
"sunday",
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
] as const;
|
||||
|
||||
export interface ScheduleDay {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
type ScheduleDays = { [K in typeof weekdays[number]]?: ScheduleDay[] };
|
||||
|
||||
export interface Schedule extends ScheduleDays {
|
||||
id: string;
|
||||
name: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface ScheduleMutableParams {
|
||||
name: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export const fetchSchedule = (hass: HomeAssistant) =>
|
||||
hass.callWS<Schedule[]>({ type: "schedule/list" });
|
||||
|
||||
export const createSchedule = (
|
||||
hass: HomeAssistant,
|
||||
values: ScheduleMutableParams
|
||||
) =>
|
||||
hass.callWS<Schedule>({
|
||||
type: "schedule/create",
|
||||
...values,
|
||||
});
|
||||
|
||||
export const updateSchedule = (
|
||||
hass: HomeAssistant,
|
||||
id: string,
|
||||
updates: Partial<ScheduleMutableParams>
|
||||
) =>
|
||||
hass.callWS<Schedule>({
|
||||
type: "schedule/update",
|
||||
schedule_id: id,
|
||||
...updates,
|
||||
});
|
||||
|
||||
export const deleteSchedule = (hass: HomeAssistant, id: string) =>
|
||||
hass.callWS({
|
||||
type: "schedule/delete",
|
||||
schedule_id: id,
|
||||
});
|
||||
|
||||
export const getScheduleTime = (date: Date): string =>
|
||||
`${("0" + date.getHours()).slice(-2)}:${("0" + date.getMinutes()).slice(-2)}`;
|
@ -8,4 +8,5 @@ export const PLATFORMS_WITH_SETTINGS_TAB = {
|
||||
counter: "entity-settings-helper-tab",
|
||||
timer: "entity-settings-helper-tab",
|
||||
input_button: "entity-settings-helper-tab",
|
||||
schedule: "entity-settings-helper-tab",
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
|
||||
import { dynamicElement } from "../../../../../common/dom/dynamic-element-directive";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
@ -26,6 +26,7 @@ import "../../../helpers/forms/ha-input_datetime-form";
|
||||
import "../../../helpers/forms/ha-input_number-form";
|
||||
import "../../../helpers/forms/ha-input_select-form";
|
||||
import "../../../helpers/forms/ha-input_text-form";
|
||||
import "../../../helpers/forms/ha-schedule-form";
|
||||
import "../../../helpers/forms/ha-timer-form";
|
||||
import "../../entity-registry-basic-editor";
|
||||
import type { HaEntityRegistryBasicEditor } from "../../entity-registry-basic-editor";
|
||||
|
@ -5,6 +5,7 @@ import type { InputDateTime } from "../../../data/input_datetime";
|
||||
import type { InputNumber } from "../../../data/input_number";
|
||||
import type { InputSelect } from "../../../data/input_select";
|
||||
import type { InputText } from "../../../data/input_text";
|
||||
import type { Schedule } from "../../../data/schedule";
|
||||
import type { Timer } from "../../../data/timer";
|
||||
|
||||
export const HELPER_DOMAINS = [
|
||||
@ -16,6 +17,7 @@ export const HELPER_DOMAINS = [
|
||||
"input_select",
|
||||
"counter",
|
||||
"timer",
|
||||
"schedule",
|
||||
];
|
||||
|
||||
export type Helper =
|
||||
@ -26,4 +28,5 @@ export type Helper =
|
||||
| InputSelect
|
||||
| InputDateTime
|
||||
| Counter
|
||||
| Timer;
|
||||
| Timer
|
||||
| Schedule;
|
||||
|
@ -19,6 +19,7 @@ import { createInputNumber } from "../../../data/input_number";
|
||||
import { createInputSelect } from "../../../data/input_select";
|
||||
import { createInputText } from "../../../data/input_text";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { createSchedule } from "../../../data/schedule";
|
||||
import { createTimer } from "../../../data/timer";
|
||||
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
@ -32,6 +33,7 @@ import "./forms/ha-input_datetime-form";
|
||||
import "./forms/ha-input_number-form";
|
||||
import "./forms/ha-input_select-form";
|
||||
import "./forms/ha-input_text-form";
|
||||
import "./forms/ha-schedule-form";
|
||||
import "./forms/ha-timer-form";
|
||||
import type { ShowDialogHelperDetailParams } from "./show-dialog-helper-detail";
|
||||
|
||||
@ -44,6 +46,7 @@ const HELPERS = {
|
||||
input_select: createInputSelect,
|
||||
counter: createCounter,
|
||||
timer: createTimer,
|
||||
schedule: createSchedule,
|
||||
};
|
||||
|
||||
@customElement("dialog-helper-detail")
|
||||
|
379
src/panels/config/helpers/forms/ha-schedule-form.ts
Normal file
379
src/panels/config/helpers/forms/ha-schedule-form.ts
Normal file
@ -0,0 +1,379 @@
|
||||
// @ts-ignore
|
||||
import fullcalendarStyle from "@fullcalendar/common/main.css";
|
||||
import { Calendar, CalendarOptions } from "@fullcalendar/core";
|
||||
import allLocales from "@fullcalendar/core/locales-all";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
// @ts-ignore
|
||||
import timegridStyle from "@fullcalendar/timegrid/main.css";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
unsafeCSS,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatTime24h } from "../../../../common/datetime/format_time";
|
||||
import { useAmPm } from "../../../../common/datetime/use_am_pm";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-icon-picker";
|
||||
import "../../../../components/ha-textfield";
|
||||
import { Schedule, ScheduleDay, weekdays } from "../../../../data/schedule";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
||||
const defaultFullCalendarConfig: CalendarOptions = {
|
||||
plugins: [timeGridPlugin, interactionPlugin],
|
||||
headerToolbar: false,
|
||||
initialView: "timeGridWeek",
|
||||
editable: true,
|
||||
selectable: true,
|
||||
selectMirror: true,
|
||||
selectOverlap: false,
|
||||
eventOverlap: false,
|
||||
allDaySlot: false,
|
||||
height: "parent",
|
||||
locales: allLocales,
|
||||
firstDay: 1,
|
||||
dayHeaderFormat: { weekday: "short", month: undefined, day: undefined },
|
||||
slotLabelFormat: { hour: "numeric", minute: undefined, meridiem: "narrow" },
|
||||
};
|
||||
|
||||
@customElement("ha-schedule-form")
|
||||
class HaScheduleForm extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public new?: boolean;
|
||||
|
||||
@state() private _name!: string;
|
||||
|
||||
@state() private _icon!: string;
|
||||
|
||||
@state() private _monday!: ScheduleDay[];
|
||||
|
||||
@state() private _tuesday!: ScheduleDay[];
|
||||
|
||||
@state() private _wednesday!: ScheduleDay[];
|
||||
|
||||
@state() private _thursday!: ScheduleDay[];
|
||||
|
||||
@state() private _friday!: ScheduleDay[];
|
||||
|
||||
@state() private _saturday!: ScheduleDay[];
|
||||
|
||||
@state() private _sunday!: ScheduleDay[];
|
||||
|
||||
@state() private calendar?: Calendar;
|
||||
|
||||
private _item?: Schedule;
|
||||
|
||||
set item(item: Schedule) {
|
||||
this._item = item;
|
||||
if (item) {
|
||||
this._name = item.name || "";
|
||||
this._icon = item.icon || "";
|
||||
this._monday = item.monday || [];
|
||||
this._tuesday = item.tuesday || [];
|
||||
this._wednesday = item.wednesday || [];
|
||||
this._thursday = item.thursday || [];
|
||||
this._friday = item.friday || [];
|
||||
this._saturday = item.saturday || [];
|
||||
this._sunday = item.sunday || [];
|
||||
} else {
|
||||
this._name = "";
|
||||
this._icon = "";
|
||||
this._monday = [];
|
||||
this._tuesday = [];
|
||||
this._wednesday = [];
|
||||
this._thursday = [];
|
||||
this._friday = [];
|
||||
this._saturday = [];
|
||||
this._sunday = [];
|
||||
}
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this.updateComplete.then(() =>
|
||||
(
|
||||
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||
)?.focus()
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
const nameInvalid = !this._name || this._name.trim() === "";
|
||||
|
||||
return html`
|
||||
<div class="form">
|
||||
<ha-textfield
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@input=${this._valueChanged}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.generic.name"
|
||||
)}
|
||||
.errorMessage=${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.required_error_msg"
|
||||
)}
|
||||
.invalid=${nameInvalid}
|
||||
dialogInitialFocus
|
||||
></ha-textfield>
|
||||
<ha-icon-picker
|
||||
.value=${this._icon}
|
||||
.configValue=${"icon"}
|
||||
@value-changed=${this._valueChanged}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.dialogs.helper_settings.generic.icon"
|
||||
)}
|
||||
></ha-icon-picker>
|
||||
<div id="calendar"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this.calendar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
changedProps.has("_sunday") ||
|
||||
changedProps.has("_monday") ||
|
||||
changedProps.has("_tuesday") ||
|
||||
changedProps.has("_wednesday") ||
|
||||
changedProps.has("_thursday") ||
|
||||
changedProps.has("_friday") ||
|
||||
changedProps.has("_saturday") ||
|
||||
changedProps.has("calendar")
|
||||
) {
|
||||
this.calendar.removeAllEventSources();
|
||||
this.calendar.addEventSource(this._events);
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant;
|
||||
|
||||
if (oldHass && oldHass.language !== this.hass.language) {
|
||||
this.calendar.setOption("locale", this.hass.language);
|
||||
}
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
const config: CalendarOptions = {
|
||||
...defaultFullCalendarConfig,
|
||||
locale: this.hass.language,
|
||||
eventTimeFormat: {
|
||||
hour: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
|
||||
minute: useAmPm(this.hass.locale) ? "numeric" : "2-digit",
|
||||
hour12: useAmPm(this.hass.locale),
|
||||
},
|
||||
};
|
||||
|
||||
config.eventClick = (info) => this._handleEventClick(info);
|
||||
config.select = (info) => this._handleSelect(info);
|
||||
config.eventResize = (info) => this._handleEventResize(info);
|
||||
config.eventDrop = (info) => this._handleEventDrop(info);
|
||||
|
||||
this.calendar = new Calendar(
|
||||
this.shadowRoot!.getElementById("calendar")!,
|
||||
config
|
||||
);
|
||||
|
||||
this.calendar!.render();
|
||||
|
||||
// Update size after fully rendered to avoid a bad render in the more info
|
||||
this.updateComplete.then(() =>
|
||||
window.setTimeout(() => {
|
||||
this.calendar!.updateSize();
|
||||
}, 500)
|
||||
);
|
||||
}
|
||||
|
||||
private get _events() {
|
||||
const events: any[] = [];
|
||||
const currentDay = new Date().getDay();
|
||||
|
||||
for (const [i, day] of weekdays.entries()) {
|
||||
if (!this[`_${day}`].length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this[`_${day}`].forEach((item: ScheduleDay, index: number) => {
|
||||
const distance = i - currentDay;
|
||||
|
||||
const start = new Date();
|
||||
start.setDate(start.getDate() + distance);
|
||||
start.setHours(
|
||||
parseInt(item.from.slice(0, 2)),
|
||||
parseInt(item.from.slice(-2))
|
||||
);
|
||||
|
||||
const end = new Date();
|
||||
end.setDate(end.getDate() + distance);
|
||||
end.setHours(
|
||||
parseInt(item.to.slice(0, 2)),
|
||||
parseInt(item.to.slice(-2))
|
||||
);
|
||||
|
||||
events.push({
|
||||
id: `${day}-${index}`,
|
||||
start: start.toISOString(),
|
||||
end: end.toISOString(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
private _handleSelect(info: { start: Date; end: Date }) {
|
||||
const { start, end } = info;
|
||||
|
||||
if (start.getDay() !== end.getDay()) {
|
||||
this.calendar!.unselect();
|
||||
return;
|
||||
}
|
||||
|
||||
const day = weekdays[start.getDay()];
|
||||
const value = [...this[`_${day}`]];
|
||||
const newValue = { ...this._item };
|
||||
|
||||
value.push({
|
||||
from: formatTime24h(start),
|
||||
to: formatTime24h(end),
|
||||
});
|
||||
|
||||
newValue[day] = value;
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleEventResize(info: any) {
|
||||
const { id, start, end } = info.event;
|
||||
|
||||
if (start.getDay() !== end.getDay()) {
|
||||
info.revert();
|
||||
return;
|
||||
}
|
||||
|
||||
const [day, index] = id.split("-");
|
||||
const value = this[`_${day}`][parseInt(index)];
|
||||
const newValue = { ...this._item };
|
||||
|
||||
newValue[day][index] = {
|
||||
from: value.from,
|
||||
to: formatTime24h(end),
|
||||
};
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleEventDrop(info: any) {
|
||||
const { id, start, end } = info.event;
|
||||
|
||||
if (start.getDay() !== end.getDay()) {
|
||||
info.revert();
|
||||
return;
|
||||
}
|
||||
|
||||
const [day, index] = id.split("-");
|
||||
const newDay = weekdays[start.getDay()];
|
||||
const newValue = { ...this._item };
|
||||
|
||||
const event = {
|
||||
from: formatTime24h(start),
|
||||
to: formatTime24h(end),
|
||||
};
|
||||
|
||||
if (newDay === day) {
|
||||
newValue[day][index] = event;
|
||||
} else {
|
||||
newValue[day].splice(index, 1);
|
||||
const value = [...this[`_${newDay}`]];
|
||||
value.push(event);
|
||||
newValue[newDay] = value;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleEventClick(info: any) {
|
||||
const [day, index] = info.event.id.split("-");
|
||||
const value = [...this[`_${day}`]];
|
||||
|
||||
const newValue = { ...this._item };
|
||||
value.splice(parseInt(index), 1);
|
||||
newValue[day] = value;
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
if (!this.new && !this._item) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.stopPropagation();
|
||||
const configValue = (ev.target as any).configValue;
|
||||
const value = ev.detail?.value || (ev.target as any).value;
|
||||
if (this[`_${configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
const newValue = { ...this._item };
|
||||
if (!value) {
|
||||
delete newValue[configValue];
|
||||
} else {
|
||||
newValue[configValue] = value;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
${unsafeCSS(fullcalendarStyle)}
|
||||
${unsafeCSS(timegridStyle)}
|
||||
.form {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
ha-textfield {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
#calendar {
|
||||
margin: 8px 0;
|
||||
height: 450px;
|
||||
width: 100%;
|
||||
}
|
||||
.fc-scroller {
|
||||
overflow-x: visible !important;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-schedule-form": HaScheduleForm;
|
||||
}
|
||||
}
|
@ -1538,7 +1538,8 @@
|
||||
"input_button": "Button",
|
||||
"input_datetime": "Date and/or time",
|
||||
"counter": "Counter",
|
||||
"timer": "Timer"
|
||||
"timer": "Timer",
|
||||
"schedule": "Schedule"
|
||||
},
|
||||
"picker": {
|
||||
"headers": {
|
||||
|
14
yarn.lock
14
yarn.lock
@ -1708,7 +1708,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@fullcalendar/daygrid@npm:5.9.0":
|
||||
"@fullcalendar/daygrid@npm:5.9.0, @fullcalendar/daygrid@npm:~5.9.0":
|
||||
version: 5.9.0
|
||||
resolution: "@fullcalendar/daygrid@npm:5.9.0"
|
||||
dependencies:
|
||||
@ -1738,6 +1738,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@fullcalendar/timegrid@npm:5.9.0":
|
||||
version: 5.9.0
|
||||
resolution: "@fullcalendar/timegrid@npm:5.9.0"
|
||||
dependencies:
|
||||
"@fullcalendar/common": ~5.9.0
|
||||
"@fullcalendar/daygrid": ~5.9.0
|
||||
tslib: ^2.1.0
|
||||
checksum: dedef1e1147cd17aa277b159c806e0f927715d67c513d940bec61cb97bfdf97c71b43c03166d8442e9683e2d7d6f03d81619a694de84e04e5995b9e8ef3585b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@gfx/zopfli@npm:^1.0.9":
|
||||
version: 1.0.11
|
||||
resolution: "@gfx/zopfli@npm:1.0.11"
|
||||
@ -9030,6 +9041,7 @@ fsevents@^1.2.7:
|
||||
"@fullcalendar/daygrid": 5.9.0
|
||||
"@fullcalendar/interaction": 5.9.0
|
||||
"@fullcalendar/list": 5.9.0
|
||||
"@fullcalendar/timegrid": 5.9.0
|
||||
"@koa/cors": ^3.1.0
|
||||
"@lit-labs/motion": ^1.0.2
|
||||
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.7.0-pre.2#./.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch"
|
||||
|
Loading…
x
Reference in New Issue
Block a user