@liwe3/webcomponents 1.0.14 → 1.1.10
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/AIMarkdownEditor.d.ts +35 -0
- package/dist/AIMarkdownEditor.d.ts.map +1 -0
- package/dist/AIMarkdownEditor.js +412 -0
- package/dist/AIMarkdownEditor.js.map +1 -0
- package/dist/AITextEditor.d.ts +183 -0
- package/dist/AITextEditor.d.ts.map +1 -0
- package/dist/AITextEditor.js +63 -27
- package/dist/AITextEditor.js.map +1 -1
- package/dist/ButtonToolbar.d.ts +35 -0
- package/dist/ButtonToolbar.d.ts.map +1 -0
- package/dist/ButtonToolbar.js +220 -0
- package/dist/ButtonToolbar.js.map +1 -0
- package/dist/CheckList.d.ts +31 -0
- package/dist/CheckList.d.ts.map +1 -0
- package/dist/CheckList.js +336 -0
- package/dist/CheckList.js.map +1 -0
- package/dist/ChunkUploader.d.ts +125 -0
- package/dist/ChunkUploader.d.ts.map +1 -0
- package/dist/ChunkUploader.js +756 -0
- package/dist/ChunkUploader.js.map +1 -0
- package/dist/ComicBalloon.d.ts +82 -0
- package/dist/ComicBalloon.d.ts.map +1 -0
- package/dist/ComicBalloon.js +346 -0
- package/dist/ComicBalloon.js.map +1 -0
- package/dist/ContainerBox.d.ts +112 -0
- package/dist/ContainerBox.d.ts.map +1 -0
- package/dist/ContainerBox.js +359 -0
- package/dist/ContainerBox.js.map +1 -0
- package/dist/DateSelector.d.ts +103 -0
- package/dist/DateSelector.d.ts.map +1 -0
- package/dist/Dialog.d.ts +102 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/Dialog.js +299 -0
- package/dist/Dialog.js.map +1 -0
- package/dist/Drawer.d.ts +63 -0
- package/dist/Drawer.d.ts.map +1 -0
- package/dist/Drawer.js +340 -0
- package/dist/Drawer.js.map +1 -0
- package/dist/ImageView.d.ts +42 -0
- package/dist/ImageView.d.ts.map +1 -0
- package/dist/ImageView.js +209 -0
- package/dist/ImageView.js.map +1 -0
- package/dist/MarkdownPreview.d.ts +25 -0
- package/dist/MarkdownPreview.d.ts.map +1 -0
- package/dist/MarkdownPreview.js +147 -0
- package/dist/MarkdownPreview.js.map +1 -0
- package/dist/PopoverMenu.d.ts +103 -0
- package/dist/PopoverMenu.d.ts.map +1 -0
- package/dist/ResizableCropper.d.ts +158 -0
- package/dist/ResizableCropper.d.ts.map +1 -0
- package/dist/ResizableCropper.js +562 -0
- package/dist/ResizableCropper.js.map +1 -0
- package/dist/SmartSelect.d.ts +100 -0
- package/dist/SmartSelect.d.ts.map +1 -0
- package/dist/SmartSelect.js +45 -2
- package/dist/SmartSelect.js.map +1 -1
- package/dist/Toast.d.ts +127 -0
- package/dist/Toast.d.ts.map +1 -0
- package/dist/Toast.js +79 -49
- package/dist/Toast.js.map +1 -1
- package/dist/TreeView.d.ts +84 -0
- package/dist/TreeView.d.ts.map +1 -0
- package/dist/TreeView.js +478 -0
- package/dist/TreeView.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -14
- package/dist/index.js.map +1 -1
- package/package.json +60 -5
- package/src/AIMarkdownEditor.ts +568 -0
- package/src/AITextEditor.ts +97 -2
- package/src/ButtonToolbar.ts +302 -0
- package/src/CheckList.ts +438 -0
- package/src/ChunkUploader.ts +1135 -0
- package/src/ComicBalloon.ts +709 -0
- package/src/ContainerBox.ts +570 -0
- package/src/Dialog.ts +510 -0
- package/src/Drawer.ts +435 -0
- package/src/ImageView.ts +265 -0
- package/src/MarkdownPreview.ts +213 -0
- package/src/ResizableCropper.ts +1099 -0
- package/src/SmartSelect.ts +48 -2
- package/src/Toast.ts +96 -32
- package/src/TreeView.ts +673 -0
- package/src/index.ts +129 -27
package/src/Drawer.ts
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawer Web Component
|
|
3
|
+
* A container that can be expanded, shrunk, or closed with smooth animations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type DrawerDirection = 'horizontal' | 'vertical';
|
|
7
|
+
export type DrawerState = 'expanded' | 'shrunk' | 'closed';
|
|
8
|
+
|
|
9
|
+
export interface DrawerConfig {
|
|
10
|
+
direction?: DrawerDirection;
|
|
11
|
+
duration?: number;
|
|
12
|
+
showTitleWhenShrunk?: boolean;
|
|
13
|
+
closable?: boolean;
|
|
14
|
+
title?: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
showToggleButton?: boolean;
|
|
17
|
+
contentPadding?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class DrawerElement extends HTMLElement {
|
|
21
|
+
declare shadowRoot: ShadowRoot;
|
|
22
|
+
private currentState: DrawerState = 'expanded';
|
|
23
|
+
private config: DrawerConfig = {
|
|
24
|
+
direction: 'horizontal',
|
|
25
|
+
duration: 300,
|
|
26
|
+
showTitleWhenShrunk: false,
|
|
27
|
+
closable: true,
|
|
28
|
+
title: '',
|
|
29
|
+
icon: '☰',
|
|
30
|
+
showToggleButton: true,
|
|
31
|
+
contentPadding: '16px'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
constructor () {
|
|
35
|
+
super();
|
|
36
|
+
this.attachShadow( { mode: 'open' } );
|
|
37
|
+
this.render();
|
|
38
|
+
this.bindEvents();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static get observedAttributes (): string[] {
|
|
42
|
+
return [ 'direction', 'duration', 'show-title-when-shrunk', 'closable', 'title', 'icon', 'state', 'show-toggle-button', 'content-padding' ];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
attributeChangedCallback ( name: string, oldValue: string | null, newValue: string | null ): void {
|
|
46
|
+
if ( oldValue !== newValue ) {
|
|
47
|
+
switch ( name ) {
|
|
48
|
+
case 'direction':
|
|
49
|
+
this.config.direction = ( newValue as DrawerDirection ) || 'horizontal';
|
|
50
|
+
break;
|
|
51
|
+
case 'duration':
|
|
52
|
+
this.config.duration = parseInt( newValue || '300', 10 );
|
|
53
|
+
break;
|
|
54
|
+
case 'show-title-when-shrunk':
|
|
55
|
+
this.config.showTitleWhenShrunk = newValue === 'true';
|
|
56
|
+
break;
|
|
57
|
+
case 'closable':
|
|
58
|
+
this.config.closable = newValue !== 'false';
|
|
59
|
+
break;
|
|
60
|
+
case 'title':
|
|
61
|
+
this.config.title = newValue || '';
|
|
62
|
+
break;
|
|
63
|
+
case 'icon':
|
|
64
|
+
this.config.icon = newValue || '☰';
|
|
65
|
+
break;
|
|
66
|
+
case 'state':
|
|
67
|
+
this.currentState = ( newValue as DrawerState ) || 'expanded';
|
|
68
|
+
break;
|
|
69
|
+
case 'show-toggle-button':
|
|
70
|
+
this.config.showToggleButton = newValue !== 'false';
|
|
71
|
+
break;
|
|
72
|
+
case 'content-padding':
|
|
73
|
+
this.config.contentPadding = newValue ?? undefined;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
this.render();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get direction (): DrawerDirection {
|
|
81
|
+
return this.config.direction!;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
set direction ( value: DrawerDirection ) {
|
|
85
|
+
this.config.direction = value;
|
|
86
|
+
this.setAttribute( 'direction', value );
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get duration (): number {
|
|
90
|
+
return this.config.duration!;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
set duration ( value: number ) {
|
|
94
|
+
this.config.duration = value;
|
|
95
|
+
this.setAttribute( 'duration', value.toString() );
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get showTitleWhenShrunk (): boolean {
|
|
99
|
+
return this.config.showTitleWhenShrunk!;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
set showTitleWhenShrunk ( value: boolean ) {
|
|
103
|
+
this.config.showTitleWhenShrunk = value;
|
|
104
|
+
this.setAttribute( 'show-title-when-shrunk', value.toString() );
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get closable (): boolean {
|
|
108
|
+
return this.config.closable!;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
set closable ( value: boolean ) {
|
|
112
|
+
this.config.closable = value;
|
|
113
|
+
this.setAttribute( 'closable', value.toString() );
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get title (): string {
|
|
117
|
+
return this.config.title!;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
set title ( value: string ) {
|
|
121
|
+
this.config.title = value;
|
|
122
|
+
this.setAttribute( 'title', value );
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
get icon (): string {
|
|
126
|
+
return this.config.icon!;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
set icon ( value: string ) {
|
|
130
|
+
this.config.icon = value;
|
|
131
|
+
this.setAttribute( 'icon', value );
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get showToggleButton (): boolean {
|
|
135
|
+
return this.config.showToggleButton!;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
set showToggleButton ( value: boolean ) {
|
|
139
|
+
this.config.showToggleButton = value;
|
|
140
|
+
this.setAttribute( 'show-toggle-button', value.toString() );
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get contentPadding (): string {
|
|
144
|
+
return this.config.contentPadding ?? '16px';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
set contentPadding ( value: string ) {
|
|
148
|
+
this.config.contentPadding = value;
|
|
149
|
+
this.setAttribute( 'content-padding', value );
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
get state (): DrawerState {
|
|
153
|
+
return this.currentState;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
set state ( value: DrawerState ) {
|
|
157
|
+
this.setState( value );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Expand the drawer
|
|
162
|
+
*/
|
|
163
|
+
expand (): void {
|
|
164
|
+
this.setState( 'expanded' );
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Shrink the drawer
|
|
169
|
+
*/
|
|
170
|
+
shrink (): void {
|
|
171
|
+
this.setState( 'shrunk' );
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Close the drawer (removes from DOM)
|
|
176
|
+
*/
|
|
177
|
+
close (): void {
|
|
178
|
+
this.setState( 'closed' );
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Toggle between expanded and shrunk states
|
|
183
|
+
*/
|
|
184
|
+
toggle (): void {
|
|
185
|
+
if ( this.currentState === 'expanded' ) {
|
|
186
|
+
this.shrink();
|
|
187
|
+
} else if ( this.currentState === 'shrunk' ) {
|
|
188
|
+
this.expand();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private setState ( newState: DrawerState ): void {
|
|
193
|
+
const oldState = this.currentState;
|
|
194
|
+
this.currentState = newState;
|
|
195
|
+
this.setAttribute( 'state', newState );
|
|
196
|
+
|
|
197
|
+
const container = this.shadowRoot.querySelector( '.drawer-container' ) as HTMLElement;
|
|
198
|
+
if ( !container ) return;
|
|
199
|
+
|
|
200
|
+
// Dispatch state change event
|
|
201
|
+
this.dispatchEvent( new CustomEvent( 'drawer-state-change', {
|
|
202
|
+
detail: { oldState, newState },
|
|
203
|
+
bubbles: true,
|
|
204
|
+
composed: true
|
|
205
|
+
} ) );
|
|
206
|
+
|
|
207
|
+
if ( newState === 'closed' ) {
|
|
208
|
+
// Immediately remove from DOM (no animation) without visual jump
|
|
209
|
+
container.style.display = 'none';
|
|
210
|
+
this.dispatchEvent( new CustomEvent( 'drawer-closed', { bubbles: true, composed: true } ) );
|
|
211
|
+
this.remove();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if ( newState === 'shrunk' ) {
|
|
216
|
+
container.style.display = 'block';
|
|
217
|
+
container.classList.add( 'shrunk' );
|
|
218
|
+
container.classList.remove( 'expanded' );
|
|
219
|
+
this.dispatchEvent( new CustomEvent( 'drawer-shrunk', { bubbles: true, composed: true } ) );
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if ( newState === 'expanded' ) {
|
|
224
|
+
container.style.display = 'block';
|
|
225
|
+
container.classList.remove( 'shrunk' );
|
|
226
|
+
container.classList.add( 'expanded' );
|
|
227
|
+
this.dispatchEvent( new CustomEvent( 'drawer-expanded', { bubbles: true, composed: true } ) );
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private bindEvents (): void {
|
|
233
|
+
this.shadowRoot.addEventListener( 'click', ( e ) => {
|
|
234
|
+
const target = e.target as HTMLElement;
|
|
235
|
+
|
|
236
|
+
if ( target.closest( '.drawer-toggle' ) ) {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
this.toggle();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if ( target.closest( '.drawer-close' ) && this.config.closable ) {
|
|
242
|
+
e.preventDefault();
|
|
243
|
+
this.close();
|
|
244
|
+
}
|
|
245
|
+
} );
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private render (): void {
|
|
249
|
+
const isHorizontal = this.config.direction === 'horizontal';
|
|
250
|
+
const showTitle = this.config.showTitleWhenShrunk;
|
|
251
|
+
const showToggleButton = this.config.showToggleButton !== false;
|
|
252
|
+
const contentPadding = this.config.contentPadding ?? '16px';
|
|
253
|
+
|
|
254
|
+
this.shadowRoot.innerHTML = `
|
|
255
|
+
<style>
|
|
256
|
+
:host {
|
|
257
|
+
display: block;
|
|
258
|
+
--drawer-duration: ${ this.config.duration }ms;
|
|
259
|
+
--drawer-bg: #ffffff;
|
|
260
|
+
--drawer-border: #e5e7eb;
|
|
261
|
+
--drawer-text: #1f2937;
|
|
262
|
+
--drawer-icon-bg: #f3f4f6;
|
|
263
|
+
--drawer-icon-hover: #e5e7eb;
|
|
264
|
+
--drawer-button-hover: #f9fafb;
|
|
265
|
+
--drawer-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Container when expanded */
|
|
269
|
+
.drawer-container {
|
|
270
|
+
background: var(--drawer-bg);
|
|
271
|
+
border: 1px solid var(--drawer-border);
|
|
272
|
+
border-radius: 8px;
|
|
273
|
+
box-shadow: var(--drawer-shadow);
|
|
274
|
+
overflow: hidden;
|
|
275
|
+
position: relative;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.drawer-container.horizontal {
|
|
279
|
+
width: var(--drawer-horizontal-size, 300px);
|
|
280
|
+
height: auto;
|
|
281
|
+
transition: none;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.drawer-container.vertical {
|
|
285
|
+
width: auto;
|
|
286
|
+
height: var(--drawer-vertical-size, 300px);
|
|
287
|
+
transition: none;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.drawer-container.expanded {
|
|
291
|
+
opacity: 1;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.drawer-container.shrunk.horizontal {
|
|
295
|
+
width: var(--drawer-horizontal-shrunk-size, 48px);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.drawer-container.shrunk.vertical {
|
|
299
|
+
height: var(--drawer-vertical-shrunk-size, 48px);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* Header area with title and buttons */
|
|
303
|
+
.drawer-header {
|
|
304
|
+
display: flex;
|
|
305
|
+
align-items: center;
|
|
306
|
+
gap: 12px;
|
|
307
|
+
padding: 12px;
|
|
308
|
+
border-bottom: 1px solid var(--drawer-border);
|
|
309
|
+
transition: none;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.drawer-container.shrunk .drawer-header {
|
|
313
|
+
border-bottom: none;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.drawer-container.shrunk .drawer-title:not(.keep-visible),
|
|
317
|
+
.drawer-container.shrunk .drawer-close {
|
|
318
|
+
display: none;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.drawer-container.shrunk .drawer-title.keep-visible {
|
|
322
|
+
display: block;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.drawer-toggle {
|
|
326
|
+
display: flex;
|
|
327
|
+
align-items: center;
|
|
328
|
+
justify-content: center;
|
|
329
|
+
width: 28px;
|
|
330
|
+
height: 28px;
|
|
331
|
+
background: var(--drawer-icon-bg);
|
|
332
|
+
border: none;
|
|
333
|
+
border-radius: 6px;
|
|
334
|
+
cursor: pointer;
|
|
335
|
+
font-size: 16px;
|
|
336
|
+
transition: background 0.2s;
|
|
337
|
+
flex-shrink: 0;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.drawer-toggle:hover {
|
|
341
|
+
background: var(--drawer-icon-hover);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.drawer-title {
|
|
345
|
+
flex: 1;
|
|
346
|
+
font-size: 16px;
|
|
347
|
+
font-weight: 600;
|
|
348
|
+
color: var(--drawer-text);
|
|
349
|
+
transition: none;
|
|
350
|
+
white-space: nowrap;
|
|
351
|
+
overflow: hidden;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.drawer-close {
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: center;
|
|
357
|
+
justify-content: center;
|
|
358
|
+
width: 24px;
|
|
359
|
+
height: 24px;
|
|
360
|
+
background: transparent;
|
|
361
|
+
border: none;
|
|
362
|
+
border-radius: 4px;
|
|
363
|
+
cursor: pointer;
|
|
364
|
+
font-size: 18px;
|
|
365
|
+
color: #6b7280;
|
|
366
|
+
transition: none;
|
|
367
|
+
flex-shrink: 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.drawer-close:hover {
|
|
371
|
+
background: var(--drawer-button-hover);
|
|
372
|
+
color: #ef4444;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* Content area */
|
|
376
|
+
.drawer-content {
|
|
377
|
+
padding: var(--drawer-content-padding, 16px);
|
|
378
|
+
transition: none;
|
|
379
|
+
overflow: auto;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.drawer-container.shrunk .drawer-content {
|
|
383
|
+
opacity: 0;
|
|
384
|
+
height: 0;
|
|
385
|
+
padding: 0;
|
|
386
|
+
overflow: hidden;
|
|
387
|
+
position: absolute;
|
|
388
|
+
z-index: -1;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* Vertical specific styles */
|
|
392
|
+
.drawer-container.vertical {
|
|
393
|
+
display: flex;
|
|
394
|
+
flex-direction: column;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.drawer-container.vertical .drawer-content {
|
|
398
|
+
flex: 1;
|
|
399
|
+
overflow: auto;
|
|
400
|
+
}
|
|
401
|
+
</style>
|
|
402
|
+
|
|
403
|
+
<div class="drawer-container ${ isHorizontal ? 'horizontal' : 'vertical' } ${ this.currentState }" style="display:block; --drawer-content-padding:${ contentPadding };">
|
|
404
|
+
<div class="drawer-header">
|
|
405
|
+
${ showToggleButton ? `
|
|
406
|
+
<button class="drawer-toggle" aria-label="Toggle drawer">
|
|
407
|
+
${ this.config.icon }
|
|
408
|
+
</button>
|
|
409
|
+
` : '' }
|
|
410
|
+
<div class="drawer-title ${ showTitle ? 'keep-visible' : '' }">
|
|
411
|
+
${ this.config.title }
|
|
412
|
+
</div>
|
|
413
|
+
${ this.config.closable ? `
|
|
414
|
+
<button class=\"drawer-close\" aria-label=\"Close drawer\">\n ×\n </button>
|
|
415
|
+
` : '' }
|
|
416
|
+
</div>
|
|
417
|
+
<div class="drawer-content">
|
|
418
|
+
<slot></slot>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Auto-register the custom element
|
|
426
|
+
if ( !customElements.get( 'liwe3-drawer' ) ) {
|
|
427
|
+
customElements.define( 'liwe3-drawer', DrawerElement );
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Export a function to manually register with a custom tag name
|
|
431
|
+
export function defineDrawer ( tagName: string = 'liwe3-drawer' ): void {
|
|
432
|
+
if ( !customElements.get( tagName ) ) {
|
|
433
|
+
customElements.define( tagName, DrawerElement );
|
|
434
|
+
}
|
|
435
|
+
}
|
package/src/ImageView.ts
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
export type ImageMode = 'stretch' | '1:1' | 'cover' | 'contain';
|
|
2
|
+
export type ImagePosition = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
3
|
+
export type ImageFX = 'none' | 'bokeh' | 'pan-left' | 'pan-right' | 'zoom-in' | 'zoom-out';
|
|
4
|
+
|
|
5
|
+
export class ImageView extends HTMLElement {
|
|
6
|
+
private _src: string = '';
|
|
7
|
+
private _width: string = '100%';
|
|
8
|
+
private _height: string = 'auto';
|
|
9
|
+
private _mode: ImageMode = 'cover';
|
|
10
|
+
private _position: ImagePosition = 'center';
|
|
11
|
+
private _fx: ImageFX = 'none';
|
|
12
|
+
private _fxHover: ImageFX = 'none';
|
|
13
|
+
private _alt: string = '';
|
|
14
|
+
private _isHovering: boolean = false;
|
|
15
|
+
|
|
16
|
+
private container: HTMLDivElement;
|
|
17
|
+
private img: HTMLImageElement;
|
|
18
|
+
|
|
19
|
+
constructor () {
|
|
20
|
+
super();
|
|
21
|
+
this.attachShadow( { mode: 'open' } );
|
|
22
|
+
|
|
23
|
+
const style = document.createElement( 'style' );
|
|
24
|
+
style.textContent = `
|
|
25
|
+
:host {
|
|
26
|
+
display: block;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
position: relative;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.container {
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 100%;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
position: relative;
|
|
36
|
+
display: flex;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
img {
|
|
40
|
+
display: block;
|
|
41
|
+
transition: transform 0.5s ease, filter 0.5s ease;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Modes */
|
|
45
|
+
.mode-stretch img {
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 100%;
|
|
48
|
+
object-fit: fill;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.mode-cover img {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
object-fit: cover;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.mode-contain img {
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 100%;
|
|
60
|
+
object-fit: contain;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.mode-1-1 img {
|
|
64
|
+
max-width: none;
|
|
65
|
+
max-height: none;
|
|
66
|
+
/* Default to auto, controlled by position */
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Positions for 1:1 and others if applicable */
|
|
70
|
+
.pos-center { justify-content: center; align-items: center; }
|
|
71
|
+
.pos-top { justify-content: center; align-items: flex-start; }
|
|
72
|
+
.pos-bottom { justify-content: center; align-items: flex-end; }
|
|
73
|
+
.pos-left { justify-content: flex-start; align-items: center; }
|
|
74
|
+
.pos-right { justify-content: flex-end; align-items: center; }
|
|
75
|
+
.pos-top-left { justify-content: flex-start; align-items: flex-start; }
|
|
76
|
+
.pos-top-right { justify-content: flex-end; align-items: flex-start; }
|
|
77
|
+
.pos-bottom-left { justify-content: flex-start; align-items: flex-end; }
|
|
78
|
+
.pos-bottom-right { justify-content: flex-end; align-items: flex-end; }
|
|
79
|
+
|
|
80
|
+
/* FX */
|
|
81
|
+
.fx-bokeh img {
|
|
82
|
+
filter: blur(3px);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@keyframes pan-left {
|
|
86
|
+
0% { transform: scale(1.2) translateX(0); }
|
|
87
|
+
100% { transform: scale(1.2) translateX(-10%); }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@keyframes pan-right {
|
|
91
|
+
0% { transform: scale(1.2) translateX(0); }
|
|
92
|
+
100% { transform: scale(1.2) translateX(10%); }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@keyframes zoom-in {
|
|
96
|
+
0% { transform: scale(1); }
|
|
97
|
+
100% { transform: scale(1.2); }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@keyframes zoom-out {
|
|
101
|
+
0% { transform: scale(1.2); }
|
|
102
|
+
100% { transform: scale(1); }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.fx-pan-left img {
|
|
106
|
+
animation: pan-left 10s linear infinite alternate;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.fx-pan-right img {
|
|
110
|
+
animation: pan-right 10s linear infinite alternate;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.fx-zoom-in img {
|
|
114
|
+
animation: zoom-in 10s linear infinite alternate;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.fx-zoom-out img {
|
|
118
|
+
animation: zoom-out 10s linear infinite alternate;
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
this.container = document.createElement( 'div' );
|
|
123
|
+
this.container.classList.add( 'container' );
|
|
124
|
+
|
|
125
|
+
this.img = document.createElement( 'img' );
|
|
126
|
+
|
|
127
|
+
this.container.appendChild( this.img );
|
|
128
|
+
this.shadowRoot!.appendChild( style );
|
|
129
|
+
this.shadowRoot!.appendChild( this.container );
|
|
130
|
+
|
|
131
|
+
// Add hover listeners
|
|
132
|
+
this.addEventListener( 'mouseenter', this._handleMouseEnter.bind( this ) );
|
|
133
|
+
this.addEventListener( 'mouseleave', this._handleMouseLeave.bind( this ) );
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private _handleMouseEnter () {
|
|
137
|
+
if ( this._fxHover !== 'none' ) {
|
|
138
|
+
this._isHovering = true;
|
|
139
|
+
this.updateClasses();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private _handleMouseLeave () {
|
|
144
|
+
if ( this._fxHover !== 'none' ) {
|
|
145
|
+
this._isHovering = false;
|
|
146
|
+
this.updateClasses();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static get observedAttributes () {
|
|
151
|
+
return [ 'src', 'width', 'height', 'mode', 'position', 'fx', 'fx-hover', 'alt' ];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
attributeChangedCallback ( name: string, oldValue: string, newValue: string ) {
|
|
155
|
+
if ( oldValue === newValue ) return;
|
|
156
|
+
|
|
157
|
+
switch ( name ) {
|
|
158
|
+
case 'src':
|
|
159
|
+
this._src = newValue || '';
|
|
160
|
+
this.render();
|
|
161
|
+
break;
|
|
162
|
+
case 'width':
|
|
163
|
+
this._width = newValue || '100%';
|
|
164
|
+
this.updateDimensions();
|
|
165
|
+
break;
|
|
166
|
+
case 'height':
|
|
167
|
+
this._height = newValue || 'auto';
|
|
168
|
+
this.updateDimensions();
|
|
169
|
+
break;
|
|
170
|
+
case 'mode':
|
|
171
|
+
this._mode = ( newValue as ImageMode ) || 'cover';
|
|
172
|
+
this.updateClasses();
|
|
173
|
+
break;
|
|
174
|
+
case 'position':
|
|
175
|
+
this._position = ( newValue as ImagePosition ) || 'center';
|
|
176
|
+
this.updateClasses();
|
|
177
|
+
break;
|
|
178
|
+
case 'fx':
|
|
179
|
+
this._fx = ( newValue as ImageFX ) || 'none';
|
|
180
|
+
this.updateClasses();
|
|
181
|
+
break;
|
|
182
|
+
case 'fx-hover':
|
|
183
|
+
this._fxHover = ( newValue as ImageFX ) || 'none';
|
|
184
|
+
this.updateClasses();
|
|
185
|
+
break;
|
|
186
|
+
case 'alt':
|
|
187
|
+
this._alt = newValue || '';
|
|
188
|
+
this.render();
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
connectedCallback () {
|
|
194
|
+
this.updateDimensions();
|
|
195
|
+
this.updateClasses();
|
|
196
|
+
this.render();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private updateDimensions () {
|
|
200
|
+
this.style.width = this._width;
|
|
201
|
+
this.style.height = this._height;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private updateClasses () {
|
|
205
|
+
// Reset classes
|
|
206
|
+
this.container.className = 'container';
|
|
207
|
+
|
|
208
|
+
// Add mode class
|
|
209
|
+
this.container.classList.add( `mode-${ this._mode }` );
|
|
210
|
+
|
|
211
|
+
// Add position class
|
|
212
|
+
this.container.classList.add( `pos-${ this._position }` );
|
|
213
|
+
|
|
214
|
+
// Add FX class - use hover fx if hovering and defined, otherwise use regular fx
|
|
215
|
+
const activeFx = ( this._isHovering && this._fxHover !== 'none' ) ? this._fxHover : this._fx;
|
|
216
|
+
this.container.classList.add( `fx-${ activeFx }` );
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private render () {
|
|
220
|
+
const currentSrc = this.img.getAttribute( 'src' );
|
|
221
|
+
if ( currentSrc !== this._src ) {
|
|
222
|
+
if ( this._src ) {
|
|
223
|
+
this.img.setAttribute( 'src', this._src );
|
|
224
|
+
} else {
|
|
225
|
+
this.img.removeAttribute( 'src' );
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const currentAlt = this.img.getAttribute( 'alt' );
|
|
230
|
+
if ( currentAlt !== this._alt ) {
|
|
231
|
+
if ( this._alt ) {
|
|
232
|
+
this.img.setAttribute( 'alt', this._alt );
|
|
233
|
+
} else {
|
|
234
|
+
this.img.removeAttribute( 'alt' );
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Public API
|
|
240
|
+
get src (): string { return this._src; }
|
|
241
|
+
set src ( value: string ) { this.setAttribute( 'src', value ); }
|
|
242
|
+
|
|
243
|
+
get width (): string { return this._width; }
|
|
244
|
+
set width ( value: string ) { this.setAttribute( 'width', value ); }
|
|
245
|
+
|
|
246
|
+
get height (): string { return this._height; }
|
|
247
|
+
set height ( value: string ) { this.setAttribute( 'height', value ); }
|
|
248
|
+
|
|
249
|
+
get mode (): ImageMode { return this._mode; }
|
|
250
|
+
set mode ( value: ImageMode ) { this.setAttribute( 'mode', value ); }
|
|
251
|
+
|
|
252
|
+
get position (): ImagePosition { return this._position; }
|
|
253
|
+
set position ( value: ImagePosition ) { this.setAttribute( 'position', value ); }
|
|
254
|
+
|
|
255
|
+
get fx (): ImageFX { return this._fx; }
|
|
256
|
+
set fx ( value: ImageFX ) { this.setAttribute( 'fx', value ); }
|
|
257
|
+
|
|
258
|
+
get fxHover (): ImageFX { return this._fxHover; }
|
|
259
|
+
set fxHover ( value: ImageFX ) { this.setAttribute( 'fx-hover', value ); }
|
|
260
|
+
|
|
261
|
+
get alt (): string { return this._alt; }
|
|
262
|
+
set alt ( value: string ) { this.setAttribute( 'alt', value ); }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
customElements.define( 'liwe3-image-view', ImageView );
|