vanilla-jui 1.3.11 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2927 -0
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +4 -3
- package/src/components/accordion.ts +522 -0
- package/src/components/drop.ts +497 -0
- package/src/components/flow.ts +1579 -0
- package/src/components/form.ts +953 -0
- package/src/components/{icons.js → icons.ts} +24 -11
- package/src/components/menu.ts +615 -0
- package/src/components/{modal.js → modal.ts} +560 -237
- package/src/components/offcanvas.ts +496 -0
- package/src/components/{pagination.js → pagination.ts} +251 -76
- package/src/components/parabola.ts +335 -0
- package/src/components/{sticky.js → sticky.ts} +123 -98
- package/src/components/{swiper.js → swiper.ts} +510 -232
- package/src/components/tabs.ts +984 -0
- package/src/components/theme.ts +437 -0
- package/src/components/toast.ts +371 -0
- package/src/components/toc.ts +385 -0
- package/src/components/tooltip.ts +179 -0
- package/src/components/validator.ts +596 -0
- package/src/core/{Component.js → Component.ts} +134 -27
- package/src/css/animation.css +31 -1
- package/src/css/card.css +1 -1
- package/src/css/color.css +863 -1013
- package/src/css/color_p3.css +822 -975
- package/src/css/components.css +1 -0
- package/src/css/form.css +1 -1
- package/src/css/icon.css +17 -1
- package/src/css/index.css +2 -2
- package/src/css/menu.css +1 -1
- package/src/css/tabs.css +1 -0
- package/src/css/tokens.css +1 -1
- package/src/index.ts +37 -0
- package/src/locales/{index.js → index.ts} +1 -2
- package/src/types.d.ts +1 -0
- package/src/utilities/browser.ts +131 -0
- package/src/utilities/cache.ts +57 -0
- package/src/utilities/core.ts +614 -0
- package/src/utilities/dom.ts +468 -0
- package/src/utilities/events.ts +106 -0
- package/src/utilities/http.ts +35 -0
- package/src/utilities/storage.ts +46 -0
- package/src/components/accordion.js +0 -373
- package/src/components/drop.js +0 -437
- package/src/components/flow.js +0 -1133
- package/src/components/form.js +0 -585
- package/src/components/menu.js +0 -422
- package/src/components/offcanvas.js +0 -332
- package/src/components/parabola.js +0 -295
- package/src/components/tabs.js +0 -746
- package/src/components/theme.js +0 -353
- package/src/components/toast.js +0 -329
- package/src/components/toc.js +0 -226
- package/src/components/tooltip.js +0 -86
- package/src/components/validator.js +0 -586
- package/src/index.js +0 -45
- package/src/locales/index.min.js +0 -1
- package/src/utilities/browser.js +0 -111
- package/src/utilities/cache.js +0 -159
- package/src/utilities/core.js +0 -502
- package/src/utilities/debug.js +0 -103
- package/src/utilities/dom.js +0 -313
- package/src/utilities/events.js +0 -69
- package/src/utilities/http.js +0 -30
- package/src/utilities/legacy-dom.js +0 -179
- package/src/utilities/storage.js +0 -46
- /package/src/{components/components.js → css/themes/index.css} +0 -0
- /package/src/icons/{index.js → index.ts} +0 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import { createDeepStore, flushSync, jsx } from 'vanilla-signal';
|
|
2
|
+
|
|
3
|
+
import Component, {
|
|
4
|
+
type ComponentDOM,
|
|
5
|
+
type ComponentRuntime,
|
|
6
|
+
} from '../core/Component.ts';
|
|
7
|
+
import {
|
|
8
|
+
type ResolveSchema,
|
|
9
|
+
randomId,
|
|
10
|
+
resolveProps,
|
|
11
|
+
timer,
|
|
12
|
+
} from '../utilities/core.ts';
|
|
13
|
+
import {
|
|
14
|
+
type RenderableContent,
|
|
15
|
+
createLoading,
|
|
16
|
+
isRenderableContent,
|
|
17
|
+
normalizeContentNodes,
|
|
18
|
+
} from '../utilities/dom.ts';
|
|
19
|
+
|
|
20
|
+
export type OffcanvasDirection = 'top' | 'right' | 'bottom' | 'left';
|
|
21
|
+
export type OffcanvasAnimation = 'slide' | 'push' | 'none';
|
|
22
|
+
|
|
23
|
+
export interface OffcanvasClassNames {
|
|
24
|
+
root: string;
|
|
25
|
+
overlay: string;
|
|
26
|
+
content: string;
|
|
27
|
+
active: string;
|
|
28
|
+
top: string;
|
|
29
|
+
right: string;
|
|
30
|
+
bottom: string;
|
|
31
|
+
left: string;
|
|
32
|
+
slide: string;
|
|
33
|
+
push: string;
|
|
34
|
+
none: string;
|
|
35
|
+
pushBody: string;
|
|
36
|
+
pushTop: string;
|
|
37
|
+
pushRight: string;
|
|
38
|
+
pushBottom: string;
|
|
39
|
+
pushLeft: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type OffcanvasClassNameConfig = Partial<OffcanvasClassNames>;
|
|
43
|
+
|
|
44
|
+
export type OffcanvasContent =
|
|
45
|
+
| RenderableContent<Offcanvas>
|
|
46
|
+
| ((offcanvas: Offcanvas) => Promise<RenderableContent<Offcanvas>>);
|
|
47
|
+
|
|
48
|
+
export interface OffcanvasProps extends Record<string, unknown> {
|
|
49
|
+
content?: OffcanvasContent;
|
|
50
|
+
overlay?: boolean;
|
|
51
|
+
filter?: boolean;
|
|
52
|
+
cache?: boolean;
|
|
53
|
+
ttl?: number;
|
|
54
|
+
direction?: OffcanvasDirection;
|
|
55
|
+
animation?: OffcanvasAnimation;
|
|
56
|
+
bgClose?: boolean;
|
|
57
|
+
escClose?: boolean;
|
|
58
|
+
id?: string | null;
|
|
59
|
+
className?: OffcanvasClassNameConfig;
|
|
60
|
+
onShow?: ((offcanvas: Offcanvas) => void | Promise<void>) | null;
|
|
61
|
+
onShown?: ((offcanvas: Offcanvas) => void | Promise<void>) | null;
|
|
62
|
+
onHide?: ((offcanvas: Offcanvas) => void | Promise<void>) | null;
|
|
63
|
+
onHidden?: ((offcanvas: Offcanvas) => void | Promise<void>) | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface ResolvedOffcanvasProps extends Record<string, unknown> {
|
|
67
|
+
content: OffcanvasContent;
|
|
68
|
+
overlay: boolean;
|
|
69
|
+
filter: boolean;
|
|
70
|
+
cache: boolean;
|
|
71
|
+
ttl: number;
|
|
72
|
+
direction: OffcanvasDirection;
|
|
73
|
+
animation: OffcanvasAnimation;
|
|
74
|
+
bgClose: boolean;
|
|
75
|
+
escClose: boolean;
|
|
76
|
+
id: string;
|
|
77
|
+
className: OffcanvasClassNames;
|
|
78
|
+
onShow: NonNullable<OffcanvasProps['onShow']> | null;
|
|
79
|
+
onShown: NonNullable<OffcanvasProps['onShown']> | null;
|
|
80
|
+
onHide: NonNullable<OffcanvasProps['onHide']> | null;
|
|
81
|
+
onHidden: NonNullable<OffcanvasProps['onHidden']> | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface OffcanvasState extends Record<string, unknown> {
|
|
85
|
+
visible: boolean;
|
|
86
|
+
loading: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface OffcanvasDOM extends ComponentDOM {
|
|
90
|
+
root: HTMLElement | null;
|
|
91
|
+
content: HTMLElement | null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface OffcanvasCache {
|
|
95
|
+
content: RenderableContent<Offcanvas>;
|
|
96
|
+
hasContent: boolean;
|
|
97
|
+
updatedAt: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface OffcanvasRuntime extends ComponentRuntime {
|
|
101
|
+
cache: OffcanvasCache;
|
|
102
|
+
contentLoadId: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const DEFAULT_CLASS_NAMES: OffcanvasClassNames = {
|
|
106
|
+
root: 'j-offcanvas',
|
|
107
|
+
overlay: 'j-offcanvas-overlay',
|
|
108
|
+
content: 'offcanvas-content',
|
|
109
|
+
active: 'is-active',
|
|
110
|
+
top: 'is-top',
|
|
111
|
+
right: 'is-right',
|
|
112
|
+
bottom: 'is-bottom',
|
|
113
|
+
left: 'is-left',
|
|
114
|
+
slide: 'is-slide',
|
|
115
|
+
push: 'is-push',
|
|
116
|
+
none: 'is-none',
|
|
117
|
+
pushBody: 'offcanvas-push-body',
|
|
118
|
+
pushTop: 'offcanvas-push-top',
|
|
119
|
+
pushRight: 'offcanvas-push-right',
|
|
120
|
+
pushBottom: 'offcanvas-push-bottom',
|
|
121
|
+
pushLeft: 'offcanvas-push-left',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const OFFCANVAS_PROPS_SCHEMA = {
|
|
125
|
+
content: {
|
|
126
|
+
default: '',
|
|
127
|
+
validate: isRenderableContent,
|
|
128
|
+
message: 'expects string, Node, array, function or null.',
|
|
129
|
+
},
|
|
130
|
+
overlay: { default: true, type: 'boolean' },
|
|
131
|
+
filter: { default: true, type: 'boolean' },
|
|
132
|
+
cache: { default: false, type: 'boolean' },
|
|
133
|
+
ttl: { default: 0, type: 'number' },
|
|
134
|
+
direction: {
|
|
135
|
+
default: 'left',
|
|
136
|
+
type: 'string',
|
|
137
|
+
enum: ['top', 'right', 'bottom', 'left'],
|
|
138
|
+
},
|
|
139
|
+
animation: {
|
|
140
|
+
default: 'slide',
|
|
141
|
+
type: 'string',
|
|
142
|
+
enum: ['slide', 'push', 'none'],
|
|
143
|
+
},
|
|
144
|
+
bgClose: { default: true, type: 'boolean' },
|
|
145
|
+
escClose: { default: true, type: 'boolean' },
|
|
146
|
+
id: {
|
|
147
|
+
default: null,
|
|
148
|
+
types: ['string', 'null'],
|
|
149
|
+
normalize: (value: unknown) => {
|
|
150
|
+
if (typeof value === 'string') {
|
|
151
|
+
const id = value.trim();
|
|
152
|
+
return id || randomId();
|
|
153
|
+
}
|
|
154
|
+
if (value == null) return randomId();
|
|
155
|
+
return value;
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
className: {
|
|
159
|
+
default: DEFAULT_CLASS_NAMES,
|
|
160
|
+
type: 'object',
|
|
161
|
+
normalize: (value: unknown) => ({
|
|
162
|
+
...DEFAULT_CLASS_NAMES,
|
|
163
|
+
...(value && typeof value === 'object' ? value : {}),
|
|
164
|
+
}),
|
|
165
|
+
},
|
|
166
|
+
onShow: { default: null, types: ['function', 'null'] },
|
|
167
|
+
onShown: { default: null, types: ['function', 'null'] },
|
|
168
|
+
onHide: { default: null, types: ['function', 'null'] },
|
|
169
|
+
onHidden: { default: null, types: ['function', 'null'] },
|
|
170
|
+
} satisfies ResolveSchema<OffcanvasProps>;
|
|
171
|
+
|
|
172
|
+
function normalizeTtl(ttl: number): number {
|
|
173
|
+
return ttl > 0 ? ttl : 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function normalizeProps(input: OffcanvasProps): ResolvedOffcanvasProps {
|
|
177
|
+
const props = resolveProps(input, OFFCANVAS_PROPS_SCHEMA, 'Offcanvas');
|
|
178
|
+
return {
|
|
179
|
+
content: props.content as OffcanvasContent,
|
|
180
|
+
overlay: props.overlay as boolean,
|
|
181
|
+
filter: props.filter as boolean,
|
|
182
|
+
cache: props.cache as boolean,
|
|
183
|
+
ttl: props.ttl as number,
|
|
184
|
+
direction: props.direction as OffcanvasDirection,
|
|
185
|
+
animation: props.animation as OffcanvasAnimation,
|
|
186
|
+
bgClose: props.bgClose as boolean,
|
|
187
|
+
escClose: props.escClose as boolean,
|
|
188
|
+
id: props.id as string,
|
|
189
|
+
className: props.className as OffcanvasClassNames,
|
|
190
|
+
onShow: props.onShow as ResolvedOffcanvasProps['onShow'],
|
|
191
|
+
onShown: props.onShown as ResolvedOffcanvasProps['onShown'],
|
|
192
|
+
onHide: props.onHide as ResolvedOffcanvasProps['onHide'],
|
|
193
|
+
onHidden: props.onHidden as ResolvedOffcanvasProps['onHidden'],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function joinClasses(
|
|
198
|
+
...classes: Array<string | null | undefined | false>
|
|
199
|
+
): string {
|
|
200
|
+
return classes.filter(Boolean).join(' ');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 侧滑面板组件,继承 Component。
|
|
205
|
+
*
|
|
206
|
+
* 适用于侧边菜单、筛选面板、移动端抽屉等场景。
|
|
207
|
+
*/
|
|
208
|
+
export class Offcanvas extends Component<
|
|
209
|
+
ResolvedOffcanvasProps,
|
|
210
|
+
OffcanvasState,
|
|
211
|
+
OffcanvasDOM
|
|
212
|
+
> {
|
|
213
|
+
declare runtime: OffcanvasRuntime;
|
|
214
|
+
declare state: OffcanvasState;
|
|
215
|
+
_overlay: HTMLElement | null;
|
|
216
|
+
|
|
217
|
+
constructor(input: OffcanvasProps = {}) {
|
|
218
|
+
const props = normalizeProps(input);
|
|
219
|
+
super(props);
|
|
220
|
+
|
|
221
|
+
this.dom.content = null;
|
|
222
|
+
this._overlay = null;
|
|
223
|
+
this.runtime.cache = {
|
|
224
|
+
content: null,
|
|
225
|
+
hasContent: false,
|
|
226
|
+
updatedAt: 0,
|
|
227
|
+
};
|
|
228
|
+
this.runtime.contentLoadId = 0;
|
|
229
|
+
|
|
230
|
+
this.state = createDeepStore({
|
|
231
|
+
visible: false,
|
|
232
|
+
loading: false,
|
|
233
|
+
}) as OffcanvasState;
|
|
234
|
+
|
|
235
|
+
this.onInit(props);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
protected onInit(props: ResolvedOffcanvasProps): void {
|
|
239
|
+
if (props.overlay) this._overlay = this.buildOverlay(props);
|
|
240
|
+
this.root = this.buildRoot(props);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private buildOverlay(props: ResolvedOffcanvasProps): HTMLElement {
|
|
244
|
+
return jsx('div', {
|
|
245
|
+
className: props.className.overlay,
|
|
246
|
+
'data-offcanvas-overlay': props.id,
|
|
247
|
+
style: props.filter ? { backdropFilter: 'blur(2px)' } : {},
|
|
248
|
+
}) as HTMLElement;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private buildRoot(props: ResolvedOffcanvasProps): HTMLElement {
|
|
252
|
+
const content = jsx('div', {
|
|
253
|
+
className: props.className.content,
|
|
254
|
+
'data-offcanvas-content': props.id,
|
|
255
|
+
}) as HTMLElement;
|
|
256
|
+
this.dom.content = content;
|
|
257
|
+
|
|
258
|
+
if (typeof props.content !== 'function') {
|
|
259
|
+
content.append(...normalizeContentNodes(props.content, this));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return jsx('div', {
|
|
263
|
+
className: joinClasses(
|
|
264
|
+
props.className.root,
|
|
265
|
+
props.className[props.direction],
|
|
266
|
+
props.className[props.animation]
|
|
267
|
+
),
|
|
268
|
+
id: props.id,
|
|
269
|
+
'data-offcanvas': 'root',
|
|
270
|
+
'data-offcanvas-direction': props.direction,
|
|
271
|
+
'data-offcanvas-animation': props.animation,
|
|
272
|
+
children: content,
|
|
273
|
+
}) as HTMLElement;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private isCacheValid(): boolean {
|
|
277
|
+
if (!this.props.cache || !this.runtime.cache.hasContent) return false;
|
|
278
|
+
|
|
279
|
+
const ttl = normalizeTtl(this.props.ttl);
|
|
280
|
+
return !ttl || Date.now() - this.runtime.cache.updatedAt <= ttl;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private renderContent(content: RenderableContent<Offcanvas>): void {
|
|
284
|
+
if (!this.dom.content) return;
|
|
285
|
+
|
|
286
|
+
this.dom.content.textContent = '';
|
|
287
|
+
this.dom.content.append(...normalizeContentNodes(content, this));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private async loadContent(): Promise<void> {
|
|
291
|
+
const { content, cache } = this.props;
|
|
292
|
+
if (typeof content !== 'function') {
|
|
293
|
+
this.runtime.contentLoadId += 1;
|
|
294
|
+
flushSync(() => {
|
|
295
|
+
this.state.loading = false;
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (this.isCacheValid()) {
|
|
301
|
+
this.runtime.contentLoadId += 1;
|
|
302
|
+
this.renderContent(this.runtime.cache.content);
|
|
303
|
+
flushSync(() => {
|
|
304
|
+
this.state.loading = false;
|
|
305
|
+
});
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const loadId = ++this.runtime.contentLoadId;
|
|
310
|
+
flushSync(() => {
|
|
311
|
+
this.state.loading = true;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
if (this.dom.content) {
|
|
315
|
+
this.dom.content.textContent = '';
|
|
316
|
+
this.dom.content.appendChild(createLoading());
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const result = await Promise.resolve(content(this));
|
|
321
|
+
|
|
322
|
+
if (this.runtime.destroyed || loadId !== this.runtime.contentLoadId) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (cache) {
|
|
327
|
+
this.runtime.cache.content = result;
|
|
328
|
+
this.runtime.cache.hasContent = true;
|
|
329
|
+
this.runtime.cache.updatedAt = Date.now();
|
|
330
|
+
}
|
|
331
|
+
this.renderContent(result);
|
|
332
|
+
} finally {
|
|
333
|
+
if (!this.runtime.destroyed && loadId === this.runtime.contentLoadId) {
|
|
334
|
+
flushSync(() => {
|
|
335
|
+
this.state.loading = false;
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private bindEvents(): void {
|
|
342
|
+
this.unbindEvents();
|
|
343
|
+
const { bgClose, escClose } = this.props;
|
|
344
|
+
|
|
345
|
+
if (this._overlay && bgClose) {
|
|
346
|
+
this.cleanup.events.on('overlay', this._overlay, 'click', () => {
|
|
347
|
+
void this.hide();
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
if (escClose) {
|
|
351
|
+
this.cleanup.events.on('esc', document, 'keydown', (event) => {
|
|
352
|
+
if (
|
|
353
|
+
event instanceof KeyboardEvent &&
|
|
354
|
+
event.key === 'Escape' &&
|
|
355
|
+
this.state.visible
|
|
356
|
+
) {
|
|
357
|
+
void this.hide();
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
if (this.dom.root) {
|
|
362
|
+
this.cleanup.events.on('close', this.dom.root, 'click', (event) => {
|
|
363
|
+
if (!(event.target instanceof Element)) return;
|
|
364
|
+
const action = event.target.closest<HTMLElement>('[data-action]');
|
|
365
|
+
const name = action?.dataset.action;
|
|
366
|
+
if (name === 'close' || name === 'cancel') void this.hide();
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
unbindEvents(): void {
|
|
372
|
+
this.cleanup?.events.clear();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private renderPanel(): void {
|
|
376
|
+
const { overlay, animation, direction, id, className } = this.props;
|
|
377
|
+
const body = document.body;
|
|
378
|
+
|
|
379
|
+
if (overlay && this._overlay) body.appendChild(this._overlay);
|
|
380
|
+
if (this.dom.root) body.appendChild(this.dom.root);
|
|
381
|
+
|
|
382
|
+
body.style.overflow = 'hidden';
|
|
383
|
+
if (animation === 'push') {
|
|
384
|
+
body.classList.add(className.pushBody);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
timer.start(`oc-show-${id}`, 10, () => {
|
|
388
|
+
if (!this.dom.root) return;
|
|
389
|
+
this._overlay?.classList.add(className.active);
|
|
390
|
+
if (animation === 'push') {
|
|
391
|
+
body.classList.add(className[`push${capitalizeDirection(direction)}`]);
|
|
392
|
+
}
|
|
393
|
+
this.dom.root.classList.add(className.active);
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private removePanel(): void {
|
|
398
|
+
const { animation, direction, id, className } = this.props;
|
|
399
|
+
const body = document.body;
|
|
400
|
+
|
|
401
|
+
this._overlay?.classList.remove(className.active);
|
|
402
|
+
this.dom.root?.classList.remove(className.active);
|
|
403
|
+
|
|
404
|
+
if (animation === 'push') {
|
|
405
|
+
body.classList.remove(className[`push${capitalizeDirection(direction)}`]);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
timer.start(`oc-remove-${id}`, 100, () => {
|
|
409
|
+
this._overlay?.remove();
|
|
410
|
+
this.dom.root?.remove();
|
|
411
|
+
if (animation === 'push') {
|
|
412
|
+
body.classList.remove(className.pushBody);
|
|
413
|
+
}
|
|
414
|
+
body.style.overflow = '';
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async show(): Promise<void> {
|
|
419
|
+
if (this.runtime.destroyed || this.state.visible) return;
|
|
420
|
+
|
|
421
|
+
const { onShow, onShown, id } = this.props;
|
|
422
|
+
if (onShow) await Promise.resolve(onShow(this));
|
|
423
|
+
|
|
424
|
+
this.renderPanel();
|
|
425
|
+
this.bindEvents();
|
|
426
|
+
|
|
427
|
+
flushSync(() => {
|
|
428
|
+
this.state.visible = true;
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
await this.loadContent();
|
|
432
|
+
|
|
433
|
+
timer.start(`oc-shown-${id}`, 300, () => {
|
|
434
|
+
if (this.runtime.destroyed) return;
|
|
435
|
+
void onShown?.(this);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async hide(): Promise<void> {
|
|
440
|
+
if (this.runtime.destroyed || !this.state.visible) return;
|
|
441
|
+
|
|
442
|
+
const { onHide, onHidden } = this.props;
|
|
443
|
+
if (onHide) await Promise.resolve(onHide(this));
|
|
444
|
+
|
|
445
|
+
this.runtime.contentLoadId += 1;
|
|
446
|
+
flushSync(() => {
|
|
447
|
+
this.state.loading = false;
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
this.unbindEvents();
|
|
451
|
+
this.removePanel();
|
|
452
|
+
|
|
453
|
+
flushSync(() => {
|
|
454
|
+
this.state.visible = false;
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
void onHidden?.(this);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
onDestroy(): void {
|
|
461
|
+
const { id, direction, className } = this.props;
|
|
462
|
+
|
|
463
|
+
timer.cancel(`oc-show-${id}`);
|
|
464
|
+
timer.cancel(`oc-remove-${id}`);
|
|
465
|
+
timer.cancel(`oc-shown-${id}`);
|
|
466
|
+
|
|
467
|
+
if (this.state.visible) this.unbindEvents();
|
|
468
|
+
|
|
469
|
+
this.dom.root?.classList.remove(className.active);
|
|
470
|
+
this.dom.root?.remove();
|
|
471
|
+
this._overlay?.remove();
|
|
472
|
+
|
|
473
|
+
const body = document.body;
|
|
474
|
+
body.style.overflow = '';
|
|
475
|
+
body.classList.remove(
|
|
476
|
+
className.pushBody,
|
|
477
|
+
className[`push${capitalizeDirection(direction)}`]
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
this._overlay = null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function capitalizeDirection(
|
|
485
|
+
direction: OffcanvasDirection
|
|
486
|
+
): 'Top' | 'Right' | 'Bottom' | 'Left' {
|
|
487
|
+
return `${direction[0].toUpperCase()}${direction.slice(1)}` as
|
|
488
|
+
| 'Top'
|
|
489
|
+
| 'Right'
|
|
490
|
+
| 'Bottom'
|
|
491
|
+
| 'Left';
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export function createOffcanvas(options: OffcanvasProps = {}): Offcanvas {
|
|
495
|
+
return new Offcanvas(options);
|
|
496
|
+
}
|