@liwe3/webcomponents 1.1.0 → 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.
Files changed (56) hide show
  1. package/dist/AIMarkdownEditor.d.ts +35 -0
  2. package/dist/AIMarkdownEditor.d.ts.map +1 -0
  3. package/dist/AIMarkdownEditor.js +412 -0
  4. package/dist/AIMarkdownEditor.js.map +1 -0
  5. package/dist/AITextEditor.d.ts +10 -0
  6. package/dist/AITextEditor.d.ts.map +1 -1
  7. package/dist/AITextEditor.js +63 -27
  8. package/dist/AITextEditor.js.map +1 -1
  9. package/dist/ButtonToolbar.d.ts +35 -0
  10. package/dist/ButtonToolbar.d.ts.map +1 -0
  11. package/dist/ButtonToolbar.js +220 -0
  12. package/dist/ButtonToolbar.js.map +1 -0
  13. package/dist/CheckList.d.ts +31 -0
  14. package/dist/CheckList.d.ts.map +1 -0
  15. package/dist/CheckList.js +336 -0
  16. package/dist/CheckList.js.map +1 -0
  17. package/dist/ChunkUploader.d.ts +22 -0
  18. package/dist/ChunkUploader.d.ts.map +1 -1
  19. package/dist/ChunkUploader.js +245 -103
  20. package/dist/ChunkUploader.js.map +1 -1
  21. package/dist/ComicBalloon.d.ts +82 -0
  22. package/dist/ComicBalloon.d.ts.map +1 -0
  23. package/dist/ComicBalloon.js +346 -0
  24. package/dist/ComicBalloon.js.map +1 -0
  25. package/dist/Dialog.d.ts +102 -0
  26. package/dist/Dialog.d.ts.map +1 -0
  27. package/dist/Dialog.js +299 -0
  28. package/dist/Dialog.js.map +1 -0
  29. package/dist/MarkdownPreview.d.ts +25 -0
  30. package/dist/MarkdownPreview.d.ts.map +1 -0
  31. package/dist/MarkdownPreview.js +147 -0
  32. package/dist/MarkdownPreview.js.map +1 -0
  33. package/dist/ResizableCropper.d.ts +158 -0
  34. package/dist/ResizableCropper.d.ts.map +1 -0
  35. package/dist/ResizableCropper.js +562 -0
  36. package/dist/ResizableCropper.js.map +1 -0
  37. package/dist/SmartSelect.d.ts +1 -0
  38. package/dist/SmartSelect.d.ts.map +1 -1
  39. package/dist/SmartSelect.js +45 -2
  40. package/dist/SmartSelect.js.map +1 -1
  41. package/dist/index.d.ts +16 -9
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +52 -29
  44. package/dist/index.js.map +1 -1
  45. package/package.json +33 -3
  46. package/src/AIMarkdownEditor.ts +568 -0
  47. package/src/AITextEditor.ts +97 -2
  48. package/src/ButtonToolbar.ts +302 -0
  49. package/src/CheckList.ts +438 -0
  50. package/src/ChunkUploader.ts +837 -623
  51. package/src/ComicBalloon.ts +709 -0
  52. package/src/Dialog.ts +510 -0
  53. package/src/MarkdownPreview.ts +213 -0
  54. package/src/ResizableCropper.ts +1099 -0
  55. package/src/SmartSelect.ts +48 -2
  56. package/src/index.ts +110 -47
@@ -0,0 +1,438 @@
1
+ /**
2
+ * CheckList Web Component
3
+ * A customizable checklist with progress bar and JSON support
4
+ */
5
+
6
+ export type CheckListItem = {
7
+ label: string;
8
+ checked: boolean;
9
+ };
10
+
11
+ export class CheckListElement extends HTMLElement {
12
+ declare shadowRoot: ShadowRoot;
13
+ private _items: CheckListItem[] = [];
14
+ private _title: string = 'Checklist';
15
+
16
+ constructor () {
17
+ super();
18
+ this.attachShadow( { mode: 'open' } );
19
+ this.render();
20
+ }
21
+
22
+ static get observedAttributes (): string[] {
23
+ return [ 'title', 'items' ];
24
+ }
25
+
26
+ attributeChangedCallback ( name: string, oldValue: string | null, newValue: string | null ): void {
27
+ if ( oldValue !== newValue ) {
28
+ if ( name === 'title' ) {
29
+ this._title = newValue || 'Checklist';
30
+ } else if ( name === 'items' && newValue ) {
31
+ try {
32
+ this._items = JSON.parse( newValue );
33
+ } catch ( e ) {
34
+ console.error( 'Invalid items JSON:', e );
35
+ }
36
+ }
37
+ this.render();
38
+ }
39
+ }
40
+
41
+ get title (): string {
42
+ return this._title;
43
+ }
44
+
45
+ set title ( value: string ) {
46
+ this._title = value;
47
+ this.setAttribute( 'title', value );
48
+ this.render();
49
+ }
50
+
51
+ get items (): CheckListItem[] {
52
+ return [ ...this._items ];
53
+ }
54
+
55
+ set items ( value: CheckListItem[] ) {
56
+ this._items = [ ...value ];
57
+ this.render();
58
+ this.dispatchEvent( new CustomEvent( 'change', { detail: { items: this._items } } ) );
59
+ }
60
+
61
+ get percentage (): number {
62
+ if ( this._items.length === 0 ) return 0;
63
+ const completed = this._items.filter( item => item.checked ).length;
64
+ return Math.round( ( completed / this._items.length ) * 100 );
65
+ }
66
+
67
+ loadJSON ( json: string ): void {
68
+ try {
69
+ const data = JSON.parse( json );
70
+ if ( data.title ) this.title = data.title;
71
+ if ( data.items && Array.isArray( data.items ) ) {
72
+ this.items = data.items;
73
+ }
74
+ } catch ( e ) {
75
+ console.error( 'Error loading JSON:', e );
76
+ }
77
+ }
78
+
79
+ toJSON (): string {
80
+ return JSON.stringify( {
81
+ title: this.title,
82
+ items: this.items
83
+ }, null, 2 );
84
+ }
85
+
86
+ private addItem ( label: string ): void {
87
+ if ( !label.trim() ) return;
88
+ this._items.push( { label, checked: false } );
89
+ this.render();
90
+ this.dispatchEvent( new CustomEvent( 'change', { detail: { items: this._items } } ) );
91
+
92
+ // Focus the new add item input after rendering
93
+ requestAnimationFrame( () => {
94
+ const addInput = this.shadowRoot.querySelector( '.add-item-input' ) as HTMLInputElement;
95
+ if ( addInput ) addInput.focus();
96
+ } );
97
+ }
98
+
99
+ private removeItem ( index: number ): void {
100
+ this._items.splice( index, 1 );
101
+ this.render();
102
+ this.dispatchEvent( new CustomEvent( 'change', { detail: { items: this._items } } ) );
103
+ }
104
+
105
+ private toggleItem ( index: number ): void {
106
+ this._items[ index ].checked = !this._items[ index ].checked;
107
+ this.render();
108
+ this.dispatchEvent( new CustomEvent( 'change', { detail: { items: this._items } } ) );
109
+ }
110
+
111
+ private updateItemLabel ( index: number, label: string ): void {
112
+ this._items[ index ].label = label;
113
+ // We don't re-render here to avoid losing focus, just update the model
114
+ this.dispatchEvent( new CustomEvent( 'change', { detail: { items: this._items } } ) );
115
+ }
116
+
117
+ private render (): void {
118
+ const percentage = this.percentage;
119
+ const completedCount = this._items.filter( i => i.checked ).length;
120
+ const totalCount = this._items.length;
121
+
122
+ // Initial render of structure if needed
123
+ if ( !this.shadowRoot.querySelector( '.list' ) ) {
124
+ this.shadowRoot.innerHTML = `
125
+ <style>
126
+ :host {
127
+ display: block;
128
+ font-family: var(--font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
129
+ background: var(--background, #fff);
130
+ border-radius: var(--border-radius, 8px);
131
+ padding: var(--padding, 16px);
132
+ box-shadow: var(--box-shadow, 0 2px 4px rgba(0,0,0,0.1));
133
+ max-width: var(--max-width, 100%);
134
+ }
135
+
136
+ .header {
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: space-between;
140
+ margin-bottom: 16px;
141
+ }
142
+
143
+ .title {
144
+ font-size: 1.1rem;
145
+ font-weight: 600;
146
+ display: flex;
147
+ align-items: center;
148
+ gap: 8px;
149
+ color: var(--text-color, #333);
150
+ }
151
+
152
+ .title-icon {
153
+ width: 20px;
154
+ height: 20px;
155
+ fill: currentColor;
156
+ }
157
+
158
+ .progress-container {
159
+ display: flex;
160
+ align-items: center;
161
+ gap: 12px;
162
+ margin-bottom: 20px;
163
+ }
164
+
165
+ .progress-text {
166
+ font-size: 0.9rem;
167
+ font-weight: 600;
168
+ color: var(--text-color, #333);
169
+ min-width: 40px;
170
+ }
171
+
172
+ .progress-bar-bg {
173
+ flex: 1;
174
+ height: 6px;
175
+ background: #e0e0e0;
176
+ border-radius: 3px;
177
+ overflow: hidden;
178
+ }
179
+
180
+ .progress-bar-fill {
181
+ height: 100%;
182
+ background: var(--primary-color, #1976d2);
183
+ width: 0%;
184
+ transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
185
+ }
186
+
187
+ .list {
188
+ display: flex;
189
+ flex-direction: column;
190
+ gap: 8px;
191
+ }
192
+
193
+ .list-item {
194
+ display: flex;
195
+ align-items: center;
196
+ gap: 12px;
197
+ padding: 4px 0;
198
+ }
199
+
200
+ .checkbox {
201
+ width: 20px;
202
+ height: 20px;
203
+ border: 2px solid #757575;
204
+ border-radius: 4px;
205
+ cursor: pointer;
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ transition: all 0.2s;
210
+ flex-shrink: 0;
211
+ }
212
+
213
+ .checkbox.checked {
214
+ background: var(--primary-color, #1976d2);
215
+ border-color: var(--primary-color, #1976d2);
216
+ }
217
+
218
+ .checkbox svg {
219
+ width: 14px;
220
+ height: 14px;
221
+ fill: white;
222
+ display: none;
223
+ }
224
+
225
+ .checkbox.checked svg {
226
+ display: block;
227
+ }
228
+
229
+ .item-input {
230
+ flex: 1;
231
+ padding: 8px 12px;
232
+ border: 1px solid #e0e0e0;
233
+ border-radius: 4px;
234
+ font-size: 0.95rem;
235
+ outline: none;
236
+ transition: border-color 0.2s;
237
+ color: var(--text-color, #333);
238
+ }
239
+
240
+ .item-input:focus {
241
+ border-color: var(--primary-color, #1976d2);
242
+ }
243
+
244
+ .item-input.add-item-input {
245
+ color: #666;
246
+ }
247
+
248
+ .item-input.add-item-input::placeholder {
249
+ color: #999;
250
+ }
251
+
252
+ .delete-btn {
253
+ background: none;
254
+ border: none;
255
+ cursor: pointer;
256
+ padding: 4px;
257
+ color: #9e9e9e;
258
+ transition: color 0.2s;
259
+ display: flex;
260
+ align-items: center;
261
+ justify-content: center;
262
+ }
263
+
264
+ .delete-btn:hover {
265
+ color: #f44336;
266
+ }
267
+
268
+ .delete-btn svg {
269
+ width: 18px;
270
+ height: 18px;
271
+ fill: currentColor;
272
+ }
273
+
274
+ .add-btn {
275
+ background: #f5f5f5;
276
+ border: none;
277
+ border-radius: 50%;
278
+ width: 32px;
279
+ height: 32px;
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ cursor: pointer;
284
+ color: #757575;
285
+ transition: background 0.2s;
286
+ }
287
+
288
+ .add-btn:hover {
289
+ background: #e0e0e0;
290
+ }
291
+ </style>
292
+
293
+ <div class="header">
294
+ <div class="title">
295
+ <svg class="title-icon" viewBox="0 0 24 24">
296
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
297
+ </svg>
298
+ <span class="title-text">${ this._title }</span>
299
+ </div>
300
+ <slot name="menu"></slot>
301
+ </div>
302
+
303
+ <div class="progress-container">
304
+ <div class="progress-text"></div>
305
+ <div class="progress-bar-bg">
306
+ <div class="progress-bar-fill"></div>
307
+ </div>
308
+ </div>
309
+
310
+ <div class="list"></div>
311
+ `;
312
+ }
313
+
314
+ // Update Title
315
+ const titleText = this.shadowRoot.querySelector( '.title-text' );
316
+ if ( titleText ) titleText.textContent = this._title;
317
+
318
+ // Update Progress
319
+ const progressText = this.shadowRoot.querySelector( '.progress-text' );
320
+ if ( progressText ) progressText.textContent = `${ completedCount } / ${ totalCount }`;
321
+
322
+ const progressBar = this.shadowRoot.querySelector( '.progress-bar-fill' ) as HTMLElement;
323
+ if ( progressBar ) progressBar.style.width = `${ percentage }%`;
324
+
325
+ // Update List
326
+ const list = this.shadowRoot.querySelector( '.list' );
327
+ if ( list ) {
328
+ list.innerHTML = `
329
+ ${ this._items.map( ( item, index ) => `
330
+ <div class="list-item">
331
+ <div class="checkbox ${ item.checked ? 'checked' : '' }" data-index="${ index }">
332
+ <svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
333
+ </div>
334
+ <input
335
+ type="text"
336
+ class="item-input"
337
+ value="${ item.label }"
338
+ data-index="${ index }"
339
+ >
340
+ <button class="delete-btn" data-index="${ index }" title="Delete item">
341
+ <svg viewBox="0 0 24 24">
342
+ <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
343
+ </svg>
344
+ </button>
345
+ </div>
346
+ `).join( '' ) }
347
+
348
+ <div class="list-item">
349
+ <div class="checkbox" style="border-color: transparent; cursor: default;"></div>
350
+ <input
351
+ type="text"
352
+ class="item-input add-item-input"
353
+ placeholder="Add an item"
354
+ >
355
+ <div class="add-btn" title="Add item">
356
+ <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
357
+ <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
358
+ </svg>
359
+ </div>
360
+ </div>
361
+ `;
362
+ }
363
+
364
+ this.bindEvents();
365
+ }
366
+
367
+ private bindEvents (): void {
368
+ // Checkbox toggle
369
+ this.shadowRoot.querySelectorAll( '.checkbox' ).forEach( checkbox => {
370
+ checkbox.addEventListener( 'click', ( e ) => {
371
+ const index = ( e.currentTarget as HTMLElement ).dataset.index;
372
+ if ( index !== undefined ) {
373
+ this.toggleItem( parseInt( index ) );
374
+ }
375
+ } );
376
+ } );
377
+
378
+ // Delete button
379
+ this.shadowRoot.querySelectorAll( '.delete-btn' ).forEach( btn => {
380
+ btn.addEventListener( 'click', ( e ) => {
381
+ const index = ( e.currentTarget as HTMLElement ).dataset.index;
382
+ if ( index !== undefined ) {
383
+ this.removeItem( parseInt( index ) );
384
+ }
385
+ } );
386
+ } );
387
+
388
+ // Item input update
389
+ this.shadowRoot.querySelectorAll( '.item-input:not(.add-item-input)' ).forEach( input => {
390
+ input.addEventListener( 'input', ( e ) => {
391
+ const target = e.target as HTMLInputElement;
392
+ const index = target.dataset.index;
393
+ if ( index !== undefined ) {
394
+ this.updateItemLabel( parseInt( index ), target.value );
395
+ }
396
+ } );
397
+ } );
398
+
399
+ // Add item input
400
+ const addInput = this.shadowRoot.querySelector( '.add-item-input' ) as HTMLInputElement;
401
+ if ( addInput ) {
402
+ addInput.addEventListener( 'keydown', ( e ) => {
403
+ if ( e.key === 'Enter' ) {
404
+ this.addItem( addInput.value );
405
+ addInput.value = '';
406
+ }
407
+ } );
408
+
409
+ addInput.addEventListener( 'blur', () => {
410
+ if ( addInput.value.trim() ) {
411
+ this.addItem( addInput.value );
412
+ addInput.value = '';
413
+ }
414
+ } );
415
+ }
416
+
417
+ // Add button click
418
+ const addBtn = this.shadowRoot.querySelector( '.add-btn' );
419
+ if ( addBtn && addInput ) {
420
+ addBtn.addEventListener( 'click', () => {
421
+ if ( addInput.value.trim() ) {
422
+ this.addItem( addInput.value );
423
+ addInput.value = '';
424
+ } else {
425
+ addInput.focus();
426
+ }
427
+ } );
428
+ }
429
+ }
430
+ }
431
+
432
+ export const defineCheckList = ( tagName: string = 'liwe3-checklist' ): void => {
433
+ if ( typeof window !== 'undefined' && !window.customElements.get( tagName ) ) {
434
+ customElements.define( tagName, CheckListElement );
435
+ }
436
+ };
437
+
438
+ defineCheckList();