@liwe3/webcomponents-svelte 1.0.14 → 1.1.11

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.
Files changed (41) hide show
  1. package/README.md +46 -0
  2. package/dist/AIMarkdownEditor.svelte +179 -0
  3. package/dist/AIMarkdownEditor.svelte.d.ts +30 -0
  4. package/dist/AIMarkdownEditor.svelte.d.ts.map +1 -0
  5. package/dist/AITextEditor.svelte +28 -27
  6. package/dist/AITextEditor.svelte.d.ts +1 -0
  7. package/dist/AITextEditor.svelte.d.ts.map +1 -1
  8. package/dist/ButtonToolbar.svelte +55 -0
  9. package/dist/ButtonToolbar.svelte.d.ts +15 -0
  10. package/dist/ButtonToolbar.svelte.d.ts.map +1 -0
  11. package/dist/CheckList.svelte +87 -0
  12. package/dist/CheckList.svelte.d.ts +11 -0
  13. package/dist/CheckList.svelte.d.ts.map +1 -0
  14. package/dist/ChunkUploader.svelte +211 -0
  15. package/dist/ChunkUploader.svelte.d.ts +29 -0
  16. package/dist/ChunkUploader.svelte.d.ts.map +1 -0
  17. package/dist/ContainerBox.svelte +58 -0
  18. package/dist/ContainerBox.svelte.d.ts +11 -0
  19. package/dist/ContainerBox.svelte.d.ts.map +1 -0
  20. package/dist/Dialogs.svelte +82 -0
  21. package/dist/Dialogs.svelte.d.ts +65 -0
  22. package/dist/Dialogs.svelte.d.ts.map +1 -0
  23. package/dist/Drawer.svelte +201 -0
  24. package/dist/Drawer.svelte.d.ts +33 -0
  25. package/dist/Drawer.svelte.d.ts.map +1 -0
  26. package/dist/MarkdownPreview.svelte +50 -0
  27. package/dist/MarkdownPreview.svelte.d.ts +9 -0
  28. package/dist/MarkdownPreview.svelte.d.ts.map +1 -0
  29. package/dist/ResizableCropper.svelte +168 -0
  30. package/dist/ResizableCropper.svelte.d.ts +24 -0
  31. package/dist/ResizableCropper.svelte.d.ts.map +1 -0
  32. package/dist/Toasts.svelte +6 -2
  33. package/dist/Toasts.svelte.d.ts +4 -2
  34. package/dist/Toasts.svelte.d.ts.map +1 -1
  35. package/dist/TreeView.svelte +103 -0
  36. package/dist/TreeView.svelte.d.ts +28 -0
  37. package/dist/TreeView.svelte.d.ts.map +1 -0
  38. package/dist/index.d.ts +12 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +15 -0
  41. package/package.json +1 -1
@@ -0,0 +1,211 @@
1
+ <script module lang="ts">
2
+ export type ChunkFileEvent = {
3
+ id : string;
4
+ status : string;
5
+ progress : number;
6
+ uploadId : string;
7
+ key : string;
8
+ originalFileName : string;
9
+ size : number;
10
+ };
11
+ </script>
12
+ <script lang="ts">
13
+ import { onMount } from 'svelte';
14
+ import type { HTMLAttributes } from 'svelte/elements';
15
+ import type {
16
+ ChunkUploaderElement as ChunkUploaderElementType,
17
+ UploadedFile,
18
+ } from '@liwe3/webcomponents';
19
+
20
+ interface Props extends HTMLAttributes<ChunkUploaderElementType> {
21
+ serverURL? : string;
22
+ chunkSize? : number;
23
+ authToken? : string;
24
+ validFiletypes? : string[];
25
+ maxFileSize? : number;
26
+ labelDropFiles? : string;
27
+ labelBrowse? : string;
28
+ folder? : string;
29
+ onfilecomplete? : ( event : ChunkFileEvent ) => void;
30
+ onuploadcomplete? : ( events : ChunkFileEvent[] ) => void;
31
+ onuploadaborted? : ( events : ChunkFileEvent[] ) => void;
32
+ parseResponse? : ( response : any, endpoint : 'initiate' | 'part' | 'complete' ) => any;
33
+ }
34
+
35
+ let {
36
+ serverURL = '',
37
+ chunkSize = 5,
38
+ authToken,
39
+ validFiletypes,
40
+ maxFileSize = 5120,
41
+ labelDropFiles,
42
+ labelBrowse,
43
+ folder,
44
+ onfilecomplete,
45
+ onuploadcomplete,
46
+ onuploadaborted,
47
+ parseResponse,
48
+ ...restProps
49
+ } : Props = $props();
50
+
51
+ let uploaderElement : ChunkUploaderElementType;
52
+ let isReady = $state( false );
53
+ let removeListeners : (() => void) | null = null;
54
+
55
+ /**
56
+ * Syncs the latest prop values down to the underlying web component.
57
+ */
58
+ const syncProps = () => {
59
+ if ( !isReady || !uploaderElement ) return;
60
+
61
+ uploaderElement.serverURL = serverURL ?? '';
62
+ uploaderElement.chunkSize = chunkSize;
63
+ uploaderElement.authToken = authToken;
64
+ uploaderElement.validFiletypes = validFiletypes?.length
65
+ ? [ ...validFiletypes ]
66
+ : undefined;
67
+ uploaderElement.maxFileSize = maxFileSize;
68
+ uploaderElement.labelDropFiles = labelDropFiles;
69
+ uploaderElement.labelBrowse = labelBrowse;
70
+ uploaderElement.folder = folder;
71
+ uploaderElement.parseResponse = parseResponse;
72
+ };
73
+
74
+ const _mapEvent = ( dets : any ) : ChunkFileEvent => {
75
+ return {
76
+ id: dets.id,
77
+ originalFileName: dets.file.name,
78
+ status: dets.status,
79
+ progress: dets.progress,
80
+ size: dets.uploadedBytes,
81
+ uploadId: dets.uploadId,
82
+ key: dets.key,
83
+ };
84
+ };
85
+
86
+ /**
87
+ * Forwards custom events emitted by the web component to Svelte callbacks.
88
+ */
89
+ const bindEvents = () => {
90
+ if ( !uploaderElement ) return;
91
+
92
+ const handleFileComplete = ( event : Event ) => {
93
+ const data : ChunkFileEvent = _mapEvent( event.detail );
94
+ onfilecomplete?.( data );
95
+ };
96
+
97
+ const handleUploadComplete = ( event : Event ) => {
98
+ const res : ChunkFileEvent[] = ( event as any ).detail.map( ( det ) => _mapEvent( det ) );
99
+ onuploadcomplete?.( res );
100
+ };
101
+
102
+ const handleUploadAborted = ( event : Event ) => {
103
+ const res : ChunkFileEvent[] = ( event as any ).detail.map( ( det ) => _mapEvent( det ) );
104
+ onuploadaborted?.( res );
105
+ };
106
+
107
+ uploaderElement.addEventListener(
108
+ 'filecomplete',
109
+ handleFileComplete as EventListener,
110
+ );
111
+ uploaderElement.addEventListener(
112
+ 'uploadcomplete',
113
+ handleUploadComplete as EventListener,
114
+ );
115
+ uploaderElement.addEventListener(
116
+ 'uploadaborted',
117
+ handleUploadAborted as EventListener,
118
+ );
119
+
120
+ removeListeners = () => {
121
+ uploaderElement.removeEventListener(
122
+ 'filecomplete',
123
+ handleFileComplete as EventListener,
124
+ );
125
+ uploaderElement.removeEventListener(
126
+ 'uploadcomplete',
127
+ handleUploadComplete as EventListener,
128
+ );
129
+ uploaderElement.removeEventListener(
130
+ 'uploadaborted',
131
+ handleUploadAborted as EventListener,
132
+ );
133
+ };
134
+ };
135
+
136
+ onMount( () => {
137
+ let isMounted = true;
138
+
139
+ const setup = async () => {
140
+ await import( '@liwe3/webcomponents/chunk-uploader' );
141
+ await customElements.whenDefined( 'liwe3-chunk-uploader' );
142
+
143
+ if ( !isMounted ) return;
144
+ isReady = true;
145
+ syncProps();
146
+ bindEvents();
147
+ };
148
+
149
+ void setup();
150
+
151
+ return () => {
152
+ isMounted = false;
153
+ removeListeners?.();
154
+ removeListeners = null;
155
+ };
156
+ } );
157
+
158
+ $effect( () => {
159
+ if ( !isReady || !uploaderElement ) return;
160
+ uploaderElement.serverURL = serverURL ?? '';
161
+ } );
162
+
163
+ $effect( () => {
164
+ if ( !isReady || !uploaderElement ) return;
165
+ uploaderElement.chunkSize = chunkSize;
166
+ } );
167
+
168
+ $effect( () => {
169
+ if ( !isReady || !uploaderElement ) return;
170
+ uploaderElement.authToken = authToken;
171
+ } );
172
+
173
+ $effect( () => {
174
+ if ( !isReady || !uploaderElement ) return;
175
+ uploaderElement.validFiletypes = validFiletypes?.length
176
+ ? [ ...validFiletypes ]
177
+ : undefined;
178
+ } );
179
+
180
+ $effect( () => {
181
+ if ( !isReady || !uploaderElement ) return;
182
+ uploaderElement.maxFileSize = maxFileSize;
183
+ } );
184
+
185
+ $effect( () => {
186
+ if ( !isReady || !uploaderElement ) return;
187
+ uploaderElement.labelDropFiles = labelDropFiles;
188
+ } );
189
+
190
+ $effect( () => {
191
+ if ( !isReady || !uploaderElement ) return;
192
+ uploaderElement.labelBrowse = labelBrowse;
193
+ } );
194
+
195
+ $effect( () => {
196
+ if ( !isReady || !uploaderElement ) return;
197
+ uploaderElement.folder = folder;
198
+ } );
199
+
200
+ $effect( () => {
201
+ if ( !isReady || !uploaderElement ) return;
202
+ uploaderElement.parseResponse = parseResponse;
203
+ } );
204
+
205
+ $effect( () => {
206
+ if ( !uploaderElement ) return;
207
+ uploaderElement.setAttribute( 'folder', folder! );
208
+ } );
209
+ </script>
210
+
211
+ <liwe3-chunk-uploader bind:this={uploaderElement} {...restProps}></liwe3-chunk-uploader>
@@ -0,0 +1,29 @@
1
+ export type ChunkFileEvent = {
2
+ id: string;
3
+ status: string;
4
+ progress: number;
5
+ uploadId: string;
6
+ key: string;
7
+ originalFileName: string;
8
+ size: number;
9
+ };
10
+ import type { HTMLAttributes } from 'svelte/elements';
11
+ import type { ChunkUploaderElement as ChunkUploaderElementType } from '@liwe3/webcomponents';
12
+ interface Props extends HTMLAttributes<ChunkUploaderElementType> {
13
+ serverURL?: string;
14
+ chunkSize?: number;
15
+ authToken?: string;
16
+ validFiletypes?: string[];
17
+ maxFileSize?: number;
18
+ labelDropFiles?: string;
19
+ labelBrowse?: string;
20
+ folder?: string;
21
+ onfilecomplete?: (event: ChunkFileEvent) => void;
22
+ onuploadcomplete?: (events: ChunkFileEvent[]) => void;
23
+ onuploadaborted?: (events: ChunkFileEvent[]) => void;
24
+ parseResponse?: (response: any, endpoint: 'initiate' | 'part' | 'complete') => any;
25
+ }
26
+ declare const ChunkUploader: import("svelte").Component<Props, {}, "">;
27
+ type ChunkUploader = ReturnType<typeof ChunkUploader>;
28
+ export default ChunkUploader;
29
+ //# sourceMappingURL=ChunkUploader.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChunkUploader.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ChunkUploader.svelte.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC5B,EAAE,EAAG,MAAM,CAAC;IACZ,MAAM,EAAG,MAAM,CAAC;IAChB,QAAQ,EAAG,MAAM,CAAC;IAClB,QAAQ,EAAG,MAAM,CAAC;IAClB,GAAG,EAAG,MAAM,CAAC;IACb,gBAAgB,EAAG,MAAM,CAAC;IAC1B,IAAI,EAAG,MAAM,CAAC;CACd,CAAC;AAIF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EACX,oBAAoB,IAAI,wBAAwB,EAEhD,MAAM,sBAAsB,CAAC;AAG9B,UAAU,KAAM,SAAQ,cAAc,CAAC,wBAAwB,CAAC;IAC/D,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,SAAS,CAAC,EAAG,MAAM,CAAC;IACpB,cAAc,CAAC,EAAG,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAG,MAAM,CAAC;IACtB,cAAc,CAAC,EAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAG,MAAM,CAAC;IACtB,MAAM,CAAC,EAAG,MAAM,CAAC;IACjB,cAAc,CAAC,EAAG,CAAE,KAAK,EAAG,cAAc,KAAM,IAAI,CAAC;IACrD,gBAAgB,CAAC,EAAG,CAAE,MAAM,EAAG,cAAc,EAAE,KAAM,IAAI,CAAC;IAC1D,eAAe,CAAC,EAAG,CAAE,MAAM,EAAG,cAAc,EAAE,KAAM,IAAI,CAAC;IACzD,aAAa,CAAC,EAAG,CAAE,QAAQ,EAAG,GAAG,EAAE,QAAQ,EAAG,UAAU,GAAG,MAAM,GAAG,UAAU,KAAM,GAAG,CAAC;CACxF;AA4LD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,58 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import type { PopoverMenuItem, MenuPosition } from "@liwe3/webcomponents";
4
+
5
+ interface Props {
6
+ menuPosition?: MenuPosition;
7
+ menuItems?: PopoverMenuItem[];
8
+ alwaysShowMenu?: boolean;
9
+ children?: import("svelte").Snippet;
10
+ }
11
+
12
+ let {
13
+ menuPosition = "bottom-left",
14
+ menuItems = [],
15
+ alwaysShowMenu = false,
16
+ children,
17
+ ...restProps
18
+ }: Props = $props();
19
+
20
+ let containerBoxElement: HTMLElement;
21
+ let isReady = $state(false);
22
+
23
+ const updateProps = () => {
24
+ if (!containerBoxElement || !isReady) return;
25
+
26
+ const el = containerBoxElement as any;
27
+
28
+ if (typeof el.setMenuPosition === "function") {
29
+ el.setMenuPosition(menuPosition);
30
+ }
31
+ if (typeof el.setMenuItems === "function") {
32
+ el.setMenuItems(menuItems);
33
+ }
34
+ if (typeof el.setAlwaysShowMenu === "function") {
35
+ el.setAlwaysShowMenu(alwaysShowMenu);
36
+ }
37
+ };
38
+
39
+ onMount(async () => {
40
+ // Dynamically import the web component
41
+ await import("@liwe3/webcomponents/container-box");
42
+ await customElements.whenDefined("liwe3-container-box");
43
+
44
+ isReady = true;
45
+ updateProps();
46
+ });
47
+
48
+ $effect(() => {
49
+ if (isReady) {
50
+ updateProps();
51
+ }
52
+ });
53
+ </script>
54
+
55
+ <!-- svelte-ignore a11y_unknown_aria_attribute -->
56
+ <liwe3-container-box bind:this={containerBoxElement} {...restProps}>
57
+ {@render children?.()}
58
+ </liwe3-container-box>
@@ -0,0 +1,11 @@
1
+ import type { PopoverMenuItem, MenuPosition } from "@liwe3/webcomponents";
2
+ interface Props {
3
+ menuPosition?: MenuPosition;
4
+ menuItems?: PopoverMenuItem[];
5
+ alwaysShowMenu?: boolean;
6
+ children?: import("svelte").Snippet;
7
+ }
8
+ declare const ContainerBox: import("svelte").Component<Props, {}, "">;
9
+ type ContainerBox = ReturnType<typeof ContainerBox>;
10
+ export default ContainerBox;
11
+ //# sourceMappingURL=ContainerBox.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContainerBox.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ContainerBox.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGxE,UAAU,KAAK;IACb,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;CACrC;AAwDH,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,82 @@
1
+ <script module lang="ts">
2
+ import type { DialogConfig, DialogElement } from '@liwe3/webcomponents';
3
+
4
+ // Re-export types for convenience
5
+ export type { DialogConfig, DialogElement };
6
+
7
+ /**
8
+ * Shows a dialog with the given configuration.
9
+ *
10
+ * IMPORTANT: Make sure to add the <Dialogs /> component to your layout first!
11
+ * The <Dialogs /> component initializes the dialog web component system.
12
+ *
13
+ * @param config - The dialog configuration
14
+ * @returns The dialog element instance (or undefined if called during SSR)
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In your +layout.svelte
19
+ * import { Dialogs } from '@liwe3/webcomponents-svelte';
20
+ * <Dialogs />
21
+ *
22
+ * // In any component
23
+ * import { dialogAdd } from '@liwe3/webcomponents-svelte';
24
+ *
25
+ * const dialog = dialogAdd({
26
+ * title: 'Delete File',
27
+ * body: '<p>Are you sure you want to delete this file?</p>',
28
+ * buttons: [
29
+ * {
30
+ * label: 'Delete',
31
+ * backgroundColor: '#dc3545',
32
+ * onclick: () => {
33
+ * console.log('File deleted');
34
+ * dialog?.close();
35
+ * }
36
+ * },
37
+ * {
38
+ * label: 'Cancel',
39
+ * onclick: () => {
40
+ * dialog?.close();
41
+ * }
42
+ * }
43
+ * ],
44
+ * modal: true,
45
+ * escToClose: true,
46
+ * clickToClose: true
47
+ * });
48
+ * ```
49
+ */
50
+ export const dialogAdd = ( config : DialogConfig ) : DialogElement | undefined => {
51
+ // Only run on client side
52
+ if ( typeof window === 'undefined' ) return undefined;
53
+
54
+ // Get the dialogAdd function from window (set by the web component)
55
+ // The <Dialogs /> component should have already loaded the web component
56
+ const globalDialogAdd = ( window as any ).__liwe3_dialogAdd;
57
+
58
+ if ( !globalDialogAdd ) {
59
+ console.error(
60
+ 'dialogAdd: Dialog web component not initialized. Did you forget to add <Dialogs /> to your layout?',
61
+ );
62
+ return undefined;
63
+ }
64
+
65
+ return globalDialogAdd( config );
66
+ };
67
+ </script>
68
+
69
+ <script lang="ts">
70
+ import { onMount } from 'svelte';
71
+
72
+ onMount( async () => {
73
+ // Only run on the client side (onMount only runs in browser)
74
+ // Import and initialize the Dialog web component
75
+ const { dialogAdd: coreDialogAdd } = await import( '@liwe3/webcomponents/dialog' );
76
+
77
+ // Expose dialogAdd on window so the wrapper can access it
78
+ ( window as any ).__liwe3_dialogAdd = coreDialogAdd;
79
+ } );
80
+ </script>
81
+
82
+ <!-- This component doesn't render anything, it just loads the web component -->
@@ -0,0 +1,65 @@
1
+ import type { DialogConfig, DialogElement } from '@liwe3/webcomponents';
2
+ export type { DialogConfig, DialogElement };
3
+ /**
4
+ * Shows a dialog with the given configuration.
5
+ *
6
+ * IMPORTANT: Make sure to add the <Dialogs /> component to your layout first!
7
+ * The <Dialogs /> component initializes the dialog web component system.
8
+ *
9
+ * @param config - The dialog configuration
10
+ * @returns The dialog element instance (or undefined if called during SSR)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // In your +layout.svelte
15
+ * import { Dialogs } from '@liwe3/webcomponents-svelte';
16
+ * <Dialogs />
17
+ *
18
+ * // In any component
19
+ * import { dialogAdd } from '@liwe3/webcomponents-svelte';
20
+ *
21
+ * const dialog = dialogAdd({
22
+ * title: 'Delete File',
23
+ * body: '<p>Are you sure you want to delete this file?</p>',
24
+ * buttons: [
25
+ * {
26
+ * label: 'Delete',
27
+ * backgroundColor: '#dc3545',
28
+ * onclick: () => {
29
+ * console.log('File deleted');
30
+ * dialog?.close();
31
+ * }
32
+ * },
33
+ * {
34
+ * label: 'Cancel',
35
+ * onclick: () => {
36
+ * dialog?.close();
37
+ * }
38
+ * }
39
+ * ],
40
+ * modal: true,
41
+ * escToClose: true,
42
+ * clickToClose: true
43
+ * });
44
+ * ```
45
+ */
46
+ export declare const dialogAdd: (config: DialogConfig) => DialogElement | undefined;
47
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
48
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
49
+ $$bindings?: Bindings;
50
+ } & Exports;
51
+ (internal: unknown, props: {
52
+ $$events?: Events;
53
+ $$slots?: Slots;
54
+ }): Exports & {
55
+ $set?: any;
56
+ $on?: any;
57
+ };
58
+ z_$$bindings?: Bindings;
59
+ }
60
+ declare const Dialogs: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
61
+ [evt: string]: CustomEvent<any>;
62
+ }, {}, {}, string>;
63
+ type Dialogs = InstanceType<typeof Dialogs>;
64
+ export default Dialogs;
65
+ //# sourceMappingURL=Dialogs.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialogs.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Dialogs.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGxE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,SAAS,GAAK,QAAS,YAAY,KAAK,aAAa,GAAG,SAgBpE,CAAC;AAwBF,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,OAAO;;kBAA+E,CAAC;AAC3E,KAAK,OAAO,GAAG,YAAY,CAAC,OAAO,OAAO,CAAC,CAAC;AAC9C,eAAe,OAAO,CAAC"}
@@ -0,0 +1,201 @@
1
+ <script lang="ts">
2
+ import { onDestroy, onMount } from "svelte";
3
+ import type { Snippet } from "svelte";
4
+
5
+ type DrawerDirection = "horizontal" | "vertical";
6
+ type DrawerState = "expanded" | "shrunk" | "closed";
7
+
8
+ interface DrawerElement extends HTMLElement {
9
+ direction: DrawerDirection;
10
+ duration: number;
11
+ showTitleWhenShrunk: boolean;
12
+ closable: boolean;
13
+ title: string;
14
+ icon: string;
15
+ showToggleButton: boolean;
16
+ contentPadding: string;
17
+ state: DrawerState;
18
+ expand: () => void;
19
+ shrink: () => void;
20
+ close: () => void;
21
+ toggle: () => void;
22
+ }
23
+
24
+ interface Props {
25
+ direction?: DrawerDirection;
26
+ duration?: number;
27
+ showTitleWhenShrunk?: boolean;
28
+ closable?: boolean;
29
+ title?: string;
30
+ icon?: string;
31
+ state?: DrawerState;
32
+ showToggleButton?: boolean;
33
+ contentPadding?: string;
34
+ onstatechange?: (
35
+ event: CustomEvent<{ oldState: DrawerState; newState: DrawerState }>
36
+ ) => void;
37
+ onexpanded?: (event: CustomEvent) => void;
38
+ onshrunk?: (event: CustomEvent) => void;
39
+ onclosed?: (event: CustomEvent) => void;
40
+ children?: Snippet;
41
+ }
42
+
43
+ let {
44
+ direction = "horizontal",
45
+ duration = 300,
46
+ showTitleWhenShrunk = false,
47
+ closable = true,
48
+ title = "",
49
+ icon = "☰",
50
+ state = $bindable<DrawerState>("expanded"),
51
+ showToggleButton = true,
52
+ contentPadding = "16px",
53
+ onstatechange,
54
+ onexpanded,
55
+ onshrunk,
56
+ onclosed,
57
+ children,
58
+ ...restProps
59
+ }: Props = $props();
60
+
61
+ let drawerElement: DrawerElement;
62
+ let isReady = false;
63
+ let eventsBound = false;
64
+ let removeListeners: (() => void) | null = null;
65
+
66
+ const updateAttributes = () => {
67
+ if (!drawerElement || !isReady) return;
68
+
69
+ drawerElement.direction = direction;
70
+ drawerElement.duration = duration;
71
+ drawerElement.showTitleWhenShrunk = showTitleWhenShrunk;
72
+ drawerElement.closable = closable;
73
+ drawerElement.title = title;
74
+ drawerElement.icon = icon;
75
+ drawerElement.showToggleButton = showToggleButton;
76
+ drawerElement.contentPadding = contentPadding ?? "16px";
77
+ drawerElement.state = state;
78
+ };
79
+
80
+ const bindEvents = () => {
81
+ if (!drawerElement || eventsBound) return;
82
+
83
+ const handleStateChange = (event: Event) => {
84
+ const customEvent = event as CustomEvent<{
85
+ oldState: DrawerState;
86
+ newState: DrawerState;
87
+ }>;
88
+
89
+ state = customEvent.detail.newState;
90
+ onstatechange?.(customEvent);
91
+ };
92
+
93
+ const handleExpanded = (event: Event) => {
94
+ onexpanded?.(event as CustomEvent);
95
+ };
96
+
97
+ const handleShrunk = (event: Event) => {
98
+ onshrunk?.(event as CustomEvent);
99
+ };
100
+
101
+ const handleClosed = (event: Event) => {
102
+ onclosed?.(event as CustomEvent);
103
+ };
104
+
105
+ drawerElement.addEventListener(
106
+ "drawer-state-change",
107
+ handleStateChange as EventListener
108
+ );
109
+ drawerElement.addEventListener(
110
+ "drawer-expanded",
111
+ handleExpanded as EventListener
112
+ );
113
+ drawerElement.addEventListener(
114
+ "drawer-shrunk",
115
+ handleShrunk as EventListener
116
+ );
117
+ drawerElement.addEventListener(
118
+ "drawer-closed",
119
+ handleClosed as EventListener
120
+ );
121
+
122
+ removeListeners = () => {
123
+ drawerElement.removeEventListener(
124
+ "drawer-state-change",
125
+ handleStateChange as EventListener
126
+ );
127
+ drawerElement.removeEventListener(
128
+ "drawer-expanded",
129
+ handleExpanded as EventListener
130
+ );
131
+ drawerElement.removeEventListener(
132
+ "drawer-shrunk",
133
+ handleShrunk as EventListener
134
+ );
135
+ drawerElement.removeEventListener(
136
+ "drawer-closed",
137
+ handleClosed as EventListener
138
+ );
139
+ };
140
+
141
+ eventsBound = true;
142
+ };
143
+
144
+ onMount(async () => {
145
+ await import("@liwe3/webcomponents/drawer");
146
+ await customElements.whenDefined("liwe3-drawer");
147
+
148
+ isReady = true;
149
+ updateAttributes();
150
+ bindEvents();
151
+ });
152
+
153
+ onDestroy(() => {
154
+ removeListeners?.();
155
+ removeListeners = null;
156
+ eventsBound = false;
157
+ });
158
+
159
+ $effect(() => {
160
+ if (!isReady) return;
161
+ updateAttributes();
162
+ });
163
+
164
+ /** Expand the drawer programmatically. */
165
+ export const expand = () => {
166
+ drawerElement?.expand();
167
+ };
168
+
169
+ /** Shrink the drawer to its collapsed size. */
170
+ export const shrink = () => {
171
+ drawerElement?.shrink();
172
+ };
173
+
174
+ /** Close the drawer and remove it from the DOM. */
175
+ export const close = () => {
176
+ drawerElement?.close();
177
+ };
178
+
179
+ /** Toggle between expanded and shrunk states. */
180
+ export const toggle = () => {
181
+ drawerElement?.toggle();
182
+ };
183
+
184
+ /** Return the current drawer state. */
185
+ export const getState = (): DrawerState => {
186
+ return drawerElement?.state ?? state;
187
+ };
188
+
189
+ /** Imperatively set the drawer state. */
190
+ export const setState = (nextState: DrawerState) => {
191
+ if (!drawerElement) return;
192
+
193
+ drawerElement.state = nextState;
194
+ state = nextState;
195
+ };
196
+ </script>
197
+
198
+ <!-- svelte-ignore a11y_unknown_aria_attribute -->
199
+ <liwe3-drawer bind:this={drawerElement} {...restProps}>
200
+ {@render children?.()}
201
+ </liwe3-drawer>