vanilla-jui 1.4.5 → 1.4.7
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/README.md +6 -0
- package/dist/index.d.ts +1273 -1896
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +21 -2
- package/src/components/accordion.ts +297 -216
- package/src/components/flow.ts +222 -258
- package/src/components/form.ts +44 -102
- package/src/components/menu.ts +268 -356
- package/src/components/modal.ts +181 -641
- package/src/components/offcanvas.ts +225 -106
- package/src/components/pagination.ts +296 -326
- package/src/components/sticky.ts +55 -54
- package/src/components/swiper.ts +237 -247
- package/src/components/tabs.ts +278 -271
- package/src/components/toc.ts +42 -46
- package/src/core/Component.ts +69 -47
- package/src/css/accordion.css +2 -2
- package/src/css/animation.css +1 -5
- package/src/css/avatar.css +1 -1
- package/src/css/badge.css +1 -1
- package/src/css/breadcrumb.css +9 -10
- package/src/css/button.css +1 -1
- package/src/css/card.css +2 -2
- package/src/css/color.css +974 -1002
- package/src/css/color_p3.css +974 -1004
- package/src/css/components.css +1 -1
- package/src/css/content.css +72 -63
- package/src/css/drop.css +1 -1
- package/src/css/editor.css +14 -14
- package/src/css/flow.css +4 -14
- package/src/css/font.css +5 -4
- package/src/css/form.css +76 -52
- package/src/css/gradient.css +299 -222
- package/src/css/icon.css +57 -57
- package/src/css/index.css +1 -1
- package/src/css/layout.css +13 -10
- package/src/css/loading.css +60 -12
- package/src/css/menu.css +262 -188
- package/src/css/modal.css +1 -1
- package/src/css/offcanvas.css +9 -13
- package/src/css/pagination.css +5 -1
- package/src/css/popup.css +1 -1
- package/src/css/skeleton.css +3 -2
- package/src/css/status.css +2 -3
- package/src/css/swiper.css +1 -1
- package/src/css/table.css +1 -1
- package/src/css/tabs.css +12 -11
- package/src/css/tag.css +1 -1
- package/src/css/theme-palette.css +19 -5
- package/src/css/themes/default/accordion.css +61 -61
- package/src/css/themes/default/avatar.css +54 -54
- package/src/css/themes/default/badge.css +62 -62
- package/src/css/themes/default/breadcrumb.css +23 -23
- package/src/css/themes/default/button.css +203 -203
- package/src/css/themes/default/content.css +53 -53
- package/src/css/themes/default/drop.css +10 -10
- package/src/css/themes/default/editor.css +9 -9
- package/src/css/themes/default/index.css +1 -1
- package/src/css/themes/default/loading.css +26 -20
- package/src/css/themes/default/menu.css +75 -11
- package/src/css/themes/default/modal.css +32 -36
- package/src/css/themes/default/pagination.css +7 -7
- package/src/css/themes/default/popup.css +6 -6
- package/src/css/themes/default/swiper.css +32 -32
- package/src/css/themes/default/table.css +16 -16
- package/src/css/themes/default/tabs.css +30 -30
- package/src/css/themes/default/tag.css +71 -71
- package/src/css/themes/default/theme-palette.css +11 -11
- package/src/css/themes/default/tip.css +42 -42
- package/src/css/themes/default/toast.css +60 -60
- package/src/css/themes/default/toolbar.css +23 -29
- package/src/css/themes/default/tooltip.css +43 -13
- package/src/css/themes/index.css +1 -1
- package/src/css/tip.css +2 -2
- package/src/css/toast.css +3 -3
- package/src/css/tokens.css +12 -17
- package/src/css/toolbar.css +9 -10
- package/src/css/tooltip.css +3 -2
- package/src/index.ts +22 -19
- package/src/{components → primitives}/drop.ts +58 -49
- package/src/{components → primitives}/icons.ts +1 -1
- package/src/primitives/loading.ts +23 -0
- package/src/primitives/parabola.ts +388 -0
- package/src/primitives/popup.ts +61 -0
- package/src/{components → primitives}/theme.ts +89 -57
- package/src/{components → primitives}/toast.ts +18 -25
- package/src/{components → primitives}/tooltip.ts +100 -25
- package/src/utilities/browser.ts +23 -49
- package/src/utilities/class-name.ts +5 -0
- package/src/utilities/dom.ts +14 -32
- package/src/utilities/function.ts +4 -0
- package/src/utilities/http.ts +7 -7
- package/src/utilities/id.ts +57 -0
- package/src/utilities/object.ts +5 -0
- package/src/utilities/timer.ts +38 -0
- package/src/utilities/types.ts +420 -0
- package/src/{components → validation}/validator.ts +64 -53
- package/src/components/parabola.ts +0 -335
- package/src/utilities/cache.ts +0 -57
- package/src/utilities/core.ts +0 -614
- package/src/utilities/storage.ts +0 -46
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createDeepStore,
|
|
3
|
+
createEffect,
|
|
4
|
+
createRoot,
|
|
5
|
+
flushSync,
|
|
6
|
+
jsx,
|
|
7
|
+
untrack,
|
|
8
|
+
} from 'vanilla-signal';
|
|
2
9
|
|
|
3
10
|
import Component, {
|
|
4
11
|
type ComponentDOM,
|
|
5
12
|
type ComponentRuntime,
|
|
6
13
|
} from '../core/Component.ts';
|
|
7
|
-
import {
|
|
8
|
-
type ResolveSchema,
|
|
9
|
-
randomId,
|
|
10
|
-
resolveProps,
|
|
11
|
-
timer,
|
|
12
|
-
} from '../utilities/core.ts';
|
|
14
|
+
import { createLoading } from '../primitives/loading.ts';
|
|
13
15
|
import {
|
|
14
16
|
type RenderableContent,
|
|
15
|
-
createLoading,
|
|
16
|
-
isRenderableContent,
|
|
17
17
|
normalizeContentNodes,
|
|
18
18
|
} from '../utilities/dom.ts';
|
|
19
|
+
import { randomId } from '../utilities/id.ts';
|
|
20
|
+
import { timer } from '../utilities/timer.ts';
|
|
21
|
+
import {
|
|
22
|
+
type ResolveSchema,
|
|
23
|
+
resolveProps,
|
|
24
|
+
validateParam,
|
|
25
|
+
} from '../utilities/types.ts';
|
|
19
26
|
|
|
20
27
|
export type OffcanvasDirection = 'top' | 'right' | 'bottom' | 'left';
|
|
21
|
-
export type
|
|
28
|
+
export type OffcanvasAnimate = string;
|
|
22
29
|
|
|
23
30
|
export interface OffcanvasClassNames {
|
|
24
31
|
root: string;
|
|
25
32
|
overlay: string;
|
|
26
33
|
content: string;
|
|
27
|
-
active: string;
|
|
28
|
-
top: string;
|
|
29
|
-
right: string;
|
|
30
|
-
bottom: string;
|
|
31
|
-
left: string;
|
|
32
|
-
slide: string;
|
|
33
|
-
none: string;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export type OffcanvasClassNameConfig = Partial<OffcanvasClassNames>;
|
|
@@ -43,10 +43,11 @@ export interface OffcanvasProps extends Record<string, unknown> {
|
|
|
43
43
|
content?: OffcanvasContent;
|
|
44
44
|
overlay?: boolean;
|
|
45
45
|
filter?: boolean;
|
|
46
|
+
bodyOverflow?: boolean;
|
|
46
47
|
cache?: boolean;
|
|
47
48
|
ttl?: number;
|
|
48
49
|
direction?: OffcanvasDirection;
|
|
49
|
-
|
|
50
|
+
animate?: OffcanvasAnimate;
|
|
50
51
|
bgClose?: boolean;
|
|
51
52
|
escClose?: boolean;
|
|
52
53
|
id?: string | null;
|
|
@@ -61,10 +62,11 @@ interface ResolvedOffcanvasProps extends Record<string, unknown> {
|
|
|
61
62
|
content: OffcanvasContent;
|
|
62
63
|
overlay: boolean;
|
|
63
64
|
filter: boolean;
|
|
65
|
+
bodyOverflow: boolean;
|
|
64
66
|
cache: boolean;
|
|
65
67
|
ttl: number;
|
|
66
68
|
direction: OffcanvasDirection;
|
|
67
|
-
|
|
69
|
+
animate: OffcanvasAnimate;
|
|
68
70
|
bgClose: boolean;
|
|
69
71
|
escClose: boolean;
|
|
70
72
|
id: string;
|
|
@@ -76,12 +78,14 @@ interface ResolvedOffcanvasProps extends Record<string, unknown> {
|
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
interface OffcanvasState extends Record<string, unknown> {
|
|
81
|
+
content: OffcanvasContent;
|
|
79
82
|
visible: boolean;
|
|
80
83
|
loading: boolean;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
interface OffcanvasDOM extends ComponentDOM {
|
|
84
87
|
root: HTMLElement | null;
|
|
88
|
+
overlay: HTMLElement | null;
|
|
85
89
|
content: HTMLElement | null;
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -92,31 +96,29 @@ interface OffcanvasCache {
|
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
interface OffcanvasRuntime extends ComponentRuntime {
|
|
99
|
+
built: boolean;
|
|
95
100
|
cache: OffcanvasCache;
|
|
96
101
|
contentLoadId: number;
|
|
97
102
|
}
|
|
98
103
|
|
|
104
|
+
interface OffcanvasCleanupExtras {
|
|
105
|
+
state?: (() => void) | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
const DEFAULT_CLASS_NAMES: OffcanvasClassNames = {
|
|
100
109
|
root: 'j-offcanvas',
|
|
101
110
|
overlay: 'j-offcanvas-overlay',
|
|
102
111
|
content: 'offcanvas-content',
|
|
103
|
-
active: 'is-active',
|
|
104
|
-
top: 'is-top',
|
|
105
|
-
right: 'is-right',
|
|
106
|
-
bottom: 'is-bottom',
|
|
107
|
-
left: 'is-left',
|
|
108
|
-
slide: 'is-slide',
|
|
109
|
-
none: 'is-none',
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
const OFFCANVAS_PROPS_SCHEMA = {
|
|
113
115
|
content: {
|
|
114
116
|
default: '',
|
|
115
|
-
|
|
116
|
-
message: 'expects string, Node, array, function or null.',
|
|
117
|
+
type: 'renderable',
|
|
117
118
|
},
|
|
118
119
|
overlay: { default: true, type: 'boolean' },
|
|
119
120
|
filter: { default: true, type: 'boolean' },
|
|
121
|
+
bodyOverflow: { default: true, type: 'boolean' },
|
|
120
122
|
cache: { default: false, type: 'boolean' },
|
|
121
123
|
ttl: { default: 0, type: 'number' },
|
|
122
124
|
direction: {
|
|
@@ -124,10 +126,9 @@ const OFFCANVAS_PROPS_SCHEMA = {
|
|
|
124
126
|
type: 'string',
|
|
125
127
|
enum: ['top', 'right', 'bottom', 'left'],
|
|
126
128
|
},
|
|
127
|
-
|
|
129
|
+
animate: {
|
|
128
130
|
default: 'slide',
|
|
129
131
|
type: 'string',
|
|
130
|
-
enum: ['slide', 'none'],
|
|
131
132
|
},
|
|
132
133
|
bgClose: { default: true, type: 'boolean' },
|
|
133
134
|
escClose: { default: true, type: 'boolean' },
|
|
@@ -157,6 +158,10 @@ const OFFCANVAS_PROPS_SCHEMA = {
|
|
|
157
158
|
onHidden: { default: null, types: ['function', 'null'] },
|
|
158
159
|
} satisfies ResolveSchema<OffcanvasProps>;
|
|
159
160
|
|
|
161
|
+
const OFFCANVAS_STATE_SCHEMA = {
|
|
162
|
+
content: OFFCANVAS_PROPS_SCHEMA.content,
|
|
163
|
+
};
|
|
164
|
+
|
|
160
165
|
function normalizeTtl(ttl: number): number {
|
|
161
166
|
return ttl > 0 ? ttl : 0;
|
|
162
167
|
}
|
|
@@ -167,10 +172,11 @@ function normalizeProps(input: OffcanvasProps): ResolvedOffcanvasProps {
|
|
|
167
172
|
content: props.content as OffcanvasContent,
|
|
168
173
|
overlay: props.overlay as boolean,
|
|
169
174
|
filter: props.filter as boolean,
|
|
175
|
+
bodyOverflow: props.bodyOverflow as boolean,
|
|
170
176
|
cache: props.cache as boolean,
|
|
171
177
|
ttl: props.ttl as number,
|
|
172
178
|
direction: props.direction as OffcanvasDirection,
|
|
173
|
-
|
|
179
|
+
animate: props.animate as OffcanvasAnimate,
|
|
174
180
|
bgClose: props.bgClose as boolean,
|
|
175
181
|
escClose: props.escClose as boolean,
|
|
176
182
|
id: props.id as string,
|
|
@@ -182,32 +188,25 @@ function normalizeProps(input: OffcanvasProps): ResolvedOffcanvasProps {
|
|
|
182
188
|
};
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
|
|
186
|
-
...classes: Array<string | null | undefined | false>
|
|
187
|
-
): string {
|
|
188
|
-
return classes.filter(Boolean).join(' ');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* 侧滑面板组件,继承 Component。
|
|
193
|
-
*
|
|
194
|
-
* 适用于侧边菜单、筛选面板、移动端抽屉等场景。
|
|
195
|
-
*/
|
|
196
|
-
export class Offcanvas extends Component<
|
|
191
|
+
class OffcanvasComponent extends Component<
|
|
197
192
|
ResolvedOffcanvasProps,
|
|
198
193
|
OffcanvasState,
|
|
199
194
|
OffcanvasDOM
|
|
200
195
|
> {
|
|
201
196
|
declare runtime: OffcanvasRuntime;
|
|
202
197
|
declare state: OffcanvasState;
|
|
203
|
-
|
|
198
|
+
declare cleanup: Component['cleanup'] & OffcanvasCleanupExtras;
|
|
204
199
|
|
|
205
200
|
constructor(input: OffcanvasProps = {}) {
|
|
206
201
|
const props = normalizeProps(input);
|
|
207
202
|
super(props);
|
|
208
203
|
|
|
204
|
+
this.dom.overlay = null;
|
|
209
205
|
this.dom.content = null;
|
|
210
|
-
|
|
206
|
+
|
|
207
|
+
this.cleanup.state = null;
|
|
208
|
+
|
|
209
|
+
this.runtime.built = false;
|
|
211
210
|
this.runtime.cache = {
|
|
212
211
|
content: null,
|
|
213
212
|
hasContent: false,
|
|
@@ -216,51 +215,128 @@ export class Offcanvas extends Component<
|
|
|
216
215
|
this.runtime.contentLoadId = 0;
|
|
217
216
|
|
|
218
217
|
this.state = createDeepStore({
|
|
218
|
+
content: props.content,
|
|
219
219
|
visible: false,
|
|
220
220
|
loading: false,
|
|
221
221
|
}) as OffcanvasState;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
build(): this {
|
|
225
|
+
if (this.runtime.destroyed) {
|
|
226
|
+
throw new Error('Offcanvas.build: instance has been destroyed.');
|
|
227
|
+
}
|
|
228
|
+
if (this.runtime.built) return this;
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
this.dom.root = this.buildRoot();
|
|
232
|
+
this.runtime.built = true;
|
|
233
|
+
this.bindState();
|
|
234
|
+
} catch (error) {
|
|
235
|
+
this.destroy();
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
222
238
|
|
|
223
|
-
this.
|
|
239
|
+
this.emit('init', this.props);
|
|
240
|
+
return this;
|
|
224
241
|
}
|
|
225
242
|
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
243
|
+
private assertBuilt(method: string): void {
|
|
244
|
+
if (this.runtime.destroyed) {
|
|
245
|
+
throw new Error(`Offcanvas.${method}: instance has been destroyed.`);
|
|
246
|
+
}
|
|
247
|
+
if (!this.runtime.built) {
|
|
248
|
+
throw new Error(`Offcanvas.${method}: call build() first.`);
|
|
249
|
+
}
|
|
229
250
|
}
|
|
230
251
|
|
|
231
|
-
private
|
|
252
|
+
private buildRoot(): HTMLElement {
|
|
253
|
+
if (this.props.overlay) {
|
|
254
|
+
this.dom.overlay = this.buildOverlay();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return this.buildPanel();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private buildOverlay(): HTMLElement {
|
|
261
|
+
const filterStyle = this.props.filter
|
|
262
|
+
? { backdropFilter: 'blur(2px)' }
|
|
263
|
+
: {};
|
|
232
264
|
return jsx('div', {
|
|
233
|
-
className: props.className.overlay,
|
|
234
|
-
|
|
235
|
-
|
|
265
|
+
className: this.props.className.overlay,
|
|
266
|
+
hidden: true,
|
|
267
|
+
'data-offcanvas-overlay': this.props.id,
|
|
268
|
+
style: {
|
|
269
|
+
...filterStyle,
|
|
270
|
+
},
|
|
236
271
|
}) as HTMLElement;
|
|
237
272
|
}
|
|
238
273
|
|
|
239
|
-
private
|
|
274
|
+
private buildPanel(): HTMLElement {
|
|
240
275
|
const content = jsx('div', {
|
|
241
|
-
className: props.className.content,
|
|
242
|
-
'data-offcanvas-content': props.id,
|
|
276
|
+
className: this.props.className.content,
|
|
277
|
+
'data-offcanvas-content': this.props.id,
|
|
243
278
|
}) as HTMLElement;
|
|
244
279
|
this.dom.content = content;
|
|
245
280
|
|
|
246
|
-
if (typeof
|
|
247
|
-
|
|
281
|
+
if (typeof this.state.content !== 'function') {
|
|
282
|
+
this.renderContent(this.state.content);
|
|
248
283
|
}
|
|
249
284
|
|
|
250
285
|
return jsx('div', {
|
|
251
|
-
className:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
id: props.id,
|
|
286
|
+
className: this.props.className.root,
|
|
287
|
+
id: this.props.id,
|
|
288
|
+
role: 'dialog',
|
|
289
|
+
'aria-modal': this.props.overlay ? 'true' : 'false',
|
|
290
|
+
'aria-expanded': 'false',
|
|
257
291
|
'data-offcanvas': 'root',
|
|
258
|
-
'data-
|
|
259
|
-
'data-
|
|
292
|
+
'data-direction': this.props.direction,
|
|
293
|
+
'data-animate': this.props.animate,
|
|
260
294
|
children: content,
|
|
261
295
|
}) as HTMLElement;
|
|
262
296
|
}
|
|
263
297
|
|
|
298
|
+
private bindState(): void {
|
|
299
|
+
if (this.cleanup.state) return;
|
|
300
|
+
|
|
301
|
+
this.cleanup.state = createRoot((dispose) => {
|
|
302
|
+
let initialized = false;
|
|
303
|
+
createEffect(() => {
|
|
304
|
+
const content = this.state.content;
|
|
305
|
+
if (!initialized) {
|
|
306
|
+
initialized = true;
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
void untrack(() => this.syncContent(content));
|
|
310
|
+
});
|
|
311
|
+
return dispose;
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private async syncContent(content: OffcanvasContent): Promise<void> {
|
|
316
|
+
if (!this.runtime.built) return;
|
|
317
|
+
validateParam(
|
|
318
|
+
'content',
|
|
319
|
+
content,
|
|
320
|
+
OFFCANVAS_STATE_SCHEMA.content,
|
|
321
|
+
'Offcanvas.state'
|
|
322
|
+
);
|
|
323
|
+
this.runtime.cache.hasContent = false;
|
|
324
|
+
this.runtime.cache.content = null;
|
|
325
|
+
this.runtime.cache.updatedAt = 0;
|
|
326
|
+
|
|
327
|
+
if (typeof content === 'function') {
|
|
328
|
+
if (this.state.visible) await this.loadContent();
|
|
329
|
+
else this.clearContent();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
this.runtime.contentLoadId += 1;
|
|
334
|
+
flushSync(() => {
|
|
335
|
+
this.state.loading = false;
|
|
336
|
+
});
|
|
337
|
+
this.renderContent(content);
|
|
338
|
+
}
|
|
339
|
+
|
|
264
340
|
private isCacheValid(): boolean {
|
|
265
341
|
if (!this.props.cache || !this.runtime.cache.hasContent) return false;
|
|
266
342
|
|
|
@@ -268,15 +344,20 @@ export class Offcanvas extends Component<
|
|
|
268
344
|
return !ttl || Date.now() - this.runtime.cache.updatedAt <= ttl;
|
|
269
345
|
}
|
|
270
346
|
|
|
347
|
+
private clearContent(): void {
|
|
348
|
+
if (this.dom.content) this.dom.content.textContent = '';
|
|
349
|
+
}
|
|
350
|
+
|
|
271
351
|
private renderContent(content: RenderableContent<Offcanvas>): void {
|
|
272
352
|
if (!this.dom.content) return;
|
|
273
353
|
|
|
274
|
-
this.
|
|
354
|
+
this.clearContent();
|
|
275
355
|
this.dom.content.append(...normalizeContentNodes(content, this));
|
|
276
356
|
}
|
|
277
357
|
|
|
278
358
|
private async loadContent(): Promise<void> {
|
|
279
|
-
const {
|
|
359
|
+
const { cache } = this.props;
|
|
360
|
+
const content = this.state.content;
|
|
280
361
|
if (typeof content !== 'function') {
|
|
281
362
|
this.runtime.contentLoadId += 1;
|
|
282
363
|
flushSync(() => {
|
|
@@ -300,7 +381,7 @@ export class Offcanvas extends Component<
|
|
|
300
381
|
});
|
|
301
382
|
|
|
302
383
|
if (this.dom.content) {
|
|
303
|
-
this.
|
|
384
|
+
this.clearContent();
|
|
304
385
|
this.dom.content.appendChild(createLoading());
|
|
305
386
|
}
|
|
306
387
|
|
|
@@ -330,8 +411,8 @@ export class Offcanvas extends Component<
|
|
|
330
411
|
this.unbindEvents();
|
|
331
412
|
const { bgClose, escClose } = this.props;
|
|
332
413
|
|
|
333
|
-
if (this.
|
|
334
|
-
this.cleanup.events.on('overlay', this.
|
|
414
|
+
if (this.dom.overlay && bgClose) {
|
|
415
|
+
this.cleanup.events.on('overlay', this.dom.overlay, 'click', () => {
|
|
335
416
|
void this.hide();
|
|
336
417
|
});
|
|
337
418
|
}
|
|
@@ -356,47 +437,54 @@ export class Offcanvas extends Component<
|
|
|
356
437
|
}
|
|
357
438
|
}
|
|
358
439
|
|
|
359
|
-
unbindEvents(): void {
|
|
360
|
-
this.cleanup
|
|
440
|
+
private unbindEvents(): void {
|
|
441
|
+
this.cleanup.events.clear();
|
|
361
442
|
}
|
|
362
443
|
|
|
363
|
-
private
|
|
364
|
-
const {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (overlay && this._overlay) body.appendChild(this._overlay);
|
|
368
|
-
if (this.dom.root) body.appendChild(this.dom.root);
|
|
444
|
+
private showPanel(): void {
|
|
445
|
+
const { id } = this.props;
|
|
446
|
+
timer.cancel(`oc-hide-${id}`);
|
|
369
447
|
|
|
370
|
-
|
|
448
|
+
if (this.dom.overlay && !this.dom.overlay.isConnected) {
|
|
449
|
+
this.dom.overlay.hidden = false;
|
|
450
|
+
document.body.appendChild(this.dom.overlay);
|
|
451
|
+
}
|
|
452
|
+
if (this.dom.root && !this.dom.root.isConnected) {
|
|
453
|
+
document.body.appendChild(this.dom.root);
|
|
454
|
+
}
|
|
455
|
+
if (this.props.bodyOverflow) {
|
|
456
|
+
document.body.style.overflow = 'hidden';
|
|
457
|
+
}
|
|
371
458
|
|
|
372
459
|
timer.start(`oc-show-${id}`, 10, () => {
|
|
373
|
-
|
|
374
|
-
this._overlay?.classList.add(className.active);
|
|
375
|
-
this.dom.root.classList.add(className.active);
|
|
460
|
+
this.dom.root?.setAttribute('aria-expanded', 'true');
|
|
376
461
|
});
|
|
377
462
|
}
|
|
378
463
|
|
|
379
|
-
private
|
|
380
|
-
const { id
|
|
381
|
-
|
|
464
|
+
private hidePanel(): void {
|
|
465
|
+
const { id } = this.props;
|
|
466
|
+
timer.cancel(`oc-show-${id}`);
|
|
467
|
+
timer.cancel(`oc-shown-${id}`);
|
|
382
468
|
|
|
383
|
-
this.
|
|
384
|
-
this.dom.root?.classList.remove(className.active);
|
|
469
|
+
this.dom.root?.setAttribute('aria-expanded', 'false');
|
|
385
470
|
|
|
386
|
-
timer.start(`oc-
|
|
387
|
-
this.
|
|
471
|
+
timer.start(`oc-hide-${id}`, 100, () => {
|
|
472
|
+
this.dom.overlay?.remove();
|
|
388
473
|
this.dom.root?.remove();
|
|
389
|
-
|
|
474
|
+
if (this.props.bodyOverflow) {
|
|
475
|
+
document.body.style.overflow = '';
|
|
476
|
+
}
|
|
390
477
|
});
|
|
391
478
|
}
|
|
392
479
|
|
|
393
480
|
async show(): Promise<void> {
|
|
394
|
-
|
|
481
|
+
this.assertBuilt('show');
|
|
482
|
+
if (this.state.visible) return;
|
|
395
483
|
|
|
396
484
|
const { onShow, onShown, id } = this.props;
|
|
397
485
|
if (onShow) await Promise.resolve(onShow(this));
|
|
398
486
|
|
|
399
|
-
this.
|
|
487
|
+
this.showPanel();
|
|
400
488
|
this.bindEvents();
|
|
401
489
|
|
|
402
490
|
flushSync(() => {
|
|
@@ -412,7 +500,8 @@ export class Offcanvas extends Component<
|
|
|
412
500
|
}
|
|
413
501
|
|
|
414
502
|
async hide(): Promise<void> {
|
|
415
|
-
|
|
503
|
+
this.assertBuilt('hide');
|
|
504
|
+
if (!this.state.visible) return;
|
|
416
505
|
|
|
417
506
|
const { onHide, onHidden } = this.props;
|
|
418
507
|
if (onHide) await Promise.resolve(onHide(this));
|
|
@@ -423,7 +512,7 @@ export class Offcanvas extends Component<
|
|
|
423
512
|
});
|
|
424
513
|
|
|
425
514
|
this.unbindEvents();
|
|
426
|
-
this.
|
|
515
|
+
this.hidePanel();
|
|
427
516
|
|
|
428
517
|
flushSync(() => {
|
|
429
518
|
this.state.visible = false;
|
|
@@ -432,26 +521,56 @@ export class Offcanvas extends Component<
|
|
|
432
521
|
void onHidden?.(this);
|
|
433
522
|
}
|
|
434
523
|
|
|
435
|
-
onDestroy(): void {
|
|
436
|
-
const { id
|
|
524
|
+
protected onDestroy(): void {
|
|
525
|
+
const { id } = this.props;
|
|
437
526
|
|
|
438
527
|
timer.cancel(`oc-show-${id}`);
|
|
439
|
-
timer.cancel(`oc-
|
|
528
|
+
timer.cancel(`oc-hide-${id}`);
|
|
440
529
|
timer.cancel(`oc-shown-${id}`);
|
|
441
530
|
|
|
442
|
-
|
|
531
|
+
this.unbindEvents();
|
|
532
|
+
this.cleanup.state?.();
|
|
533
|
+
this.cleanup.state = null;
|
|
443
534
|
|
|
444
|
-
this.dom.root?.
|
|
535
|
+
this.dom.root?.setAttribute('aria-expanded', 'false');
|
|
536
|
+
this.dom.overlay?.remove();
|
|
445
537
|
this.dom.root?.remove();
|
|
446
|
-
this.
|
|
538
|
+
if (this.props.bodyOverflow) {
|
|
539
|
+
document.body.style.overflow = '';
|
|
540
|
+
}
|
|
447
541
|
|
|
448
|
-
|
|
449
|
-
|
|
542
|
+
this.runtime.built = false;
|
|
543
|
+
}
|
|
450
544
|
|
|
451
|
-
|
|
545
|
+
protected override validateStatePatch(patch: Partial<OffcanvasState>): void {
|
|
546
|
+
validateParam(
|
|
547
|
+
'state',
|
|
548
|
+
patch,
|
|
549
|
+
{
|
|
550
|
+
type: 'plainObject',
|
|
551
|
+
},
|
|
552
|
+
'Offcanvas.setState'
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
for (const key of Object.keys(patch)) {
|
|
556
|
+
if (!Object.hasOwn(OFFCANVAS_STATE_SCHEMA, key)) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`Offcanvas.setState: "${key}" is not a supported state key.`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
const stateKey = key as keyof typeof OFFCANVAS_STATE_SCHEMA;
|
|
562
|
+
validateParam(
|
|
563
|
+
key,
|
|
564
|
+
patch[key as keyof OffcanvasState],
|
|
565
|
+
OFFCANVAS_STATE_SCHEMA[stateKey],
|
|
566
|
+
'Offcanvas.setState'
|
|
567
|
+
);
|
|
568
|
+
}
|
|
452
569
|
}
|
|
453
570
|
}
|
|
454
571
|
|
|
455
|
-
export
|
|
456
|
-
|
|
572
|
+
export type Offcanvas = OffcanvasComponent;
|
|
573
|
+
|
|
574
|
+
export function createOffcanvas(input: OffcanvasProps = {}): Offcanvas {
|
|
575
|
+
return new OffcanvasComponent(input);
|
|
457
576
|
}
|