@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.
- 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 +10 -0
- package/dist/AITextEditor.d.ts.map +1 -1
- 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 +22 -0
- package/dist/ChunkUploader.d.ts.map +1 -1
- package/dist/ChunkUploader.js +245 -103
- package/dist/ChunkUploader.js.map +1 -1
- 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/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/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/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 +1 -0
- package/dist/SmartSelect.d.ts.map +1 -1
- package/dist/SmartSelect.js +45 -2
- package/dist/SmartSelect.js.map +1 -1
- package/dist/index.d.ts +16 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -29
- package/dist/index.js.map +1 -1
- package/package.json +33 -3
- 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 +837 -623
- package/src/ComicBalloon.ts +709 -0
- package/src/Dialog.ts +510 -0
- package/src/MarkdownPreview.ts +213 -0
- package/src/ResizableCropper.ts +1099 -0
- package/src/SmartSelect.ts +48 -2
- package/src/index.ts +110 -47
package/src/AITextEditor.ts
CHANGED
|
@@ -7,11 +7,14 @@ const AI_TEXT_EDITOR_API_KEY = 'ai-text-editor-api-key';
|
|
|
7
7
|
|
|
8
8
|
export interface AITextEditorConfig {
|
|
9
9
|
apiKey?: string;
|
|
10
|
-
suggestionDelay?: number;
|
|
10
|
+
suggestionDelay?: number; // Delay in seconds before showing AI suggestions
|
|
11
11
|
systemPrompt?: string;
|
|
12
12
|
apiEndpoint?: string;
|
|
13
13
|
modelName?: string;
|
|
14
14
|
context?: string;
|
|
15
|
+
embedded?: boolean;
|
|
16
|
+
onStatusChange?: (hasApiKey: boolean) => void;
|
|
17
|
+
onLoadingChange?: (isLoading: boolean) => void;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export class AITextEditorElement extends HTMLElement {
|
|
@@ -28,11 +31,15 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
28
31
|
private isShowingSuggestion: boolean = false;
|
|
29
32
|
|
|
30
33
|
private apiKey: string = '';
|
|
31
|
-
private suggestionDelay: number = 1000;
|
|
34
|
+
private suggestionDelay: number = 1000; // Stored in milliseconds (default: 1000ms = 1 second)
|
|
32
35
|
private systemPrompt: string = "You are a helpful writing assistant. Continue the user's text naturally and coherently. Provide 1-3 sentences that would logically follow their writing. Keep the same tone and style. Do not repeat what they've already written.";
|
|
33
36
|
private apiEndpoint: string = 'https://api.openai.com/v1/chat/completions';
|
|
34
37
|
private modelName: string = 'gpt-3.5-turbo';
|
|
35
38
|
private context: string = '';
|
|
39
|
+
|
|
40
|
+
private embedded: boolean = false;
|
|
41
|
+
private onStatusChangeCallback?: (hasApiKey: boolean) => void;
|
|
42
|
+
private onLoadingChangeCallback?: (isLoading: boolean) => void;
|
|
36
43
|
|
|
37
44
|
constructor() {
|
|
38
45
|
super();
|
|
@@ -70,6 +77,10 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
70
77
|
background: #777;
|
|
71
78
|
z-index: 10;
|
|
72
79
|
}
|
|
80
|
+
|
|
81
|
+
:host([embedded]) .editor-status {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
73
84
|
|
|
74
85
|
.editor-wrapper {
|
|
75
86
|
position: relative;
|
|
@@ -97,12 +108,21 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
97
108
|
box-sizing: border-box;
|
|
98
109
|
min-height: auto;
|
|
99
110
|
}
|
|
111
|
+
|
|
112
|
+
:host([embedded]) .editor {
|
|
113
|
+
border: none;
|
|
114
|
+
border-radius: 0;
|
|
115
|
+
}
|
|
100
116
|
|
|
101
117
|
.editor:focus {
|
|
102
118
|
outline: none;
|
|
103
119
|
border-color: #4facfe;
|
|
104
120
|
box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);
|
|
105
121
|
}
|
|
122
|
+
|
|
123
|
+
:host([embedded]) .editor:focus {
|
|
124
|
+
box-shadow: none;
|
|
125
|
+
}
|
|
106
126
|
|
|
107
127
|
.editor-background {
|
|
108
128
|
position: absolute;
|
|
@@ -125,11 +145,20 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
125
145
|
color: transparent;
|
|
126
146
|
box-sizing: border-box;
|
|
127
147
|
}
|
|
148
|
+
|
|
149
|
+
:host([embedded]) .editor-background {
|
|
150
|
+
border: none;
|
|
151
|
+
border-radius: 0;
|
|
152
|
+
}
|
|
128
153
|
|
|
129
154
|
.editor-wrapper:focus-within .editor-background {
|
|
130
155
|
background: white;
|
|
131
156
|
border-color: #4facfe;
|
|
132
157
|
}
|
|
158
|
+
|
|
159
|
+
:host([embedded]) .editor-wrapper:focus-within .editor-background {
|
|
160
|
+
border-color: transparent;
|
|
161
|
+
}
|
|
133
162
|
|
|
134
163
|
.suggestion-text {
|
|
135
164
|
color: #bbb;
|
|
@@ -148,6 +177,10 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
148
177
|
z-index: 10;
|
|
149
178
|
display: none;
|
|
150
179
|
}
|
|
180
|
+
|
|
181
|
+
:host([embedded]) .loading {
|
|
182
|
+
display: none !important;
|
|
183
|
+
}
|
|
151
184
|
|
|
152
185
|
.loading.show {
|
|
153
186
|
display: block;
|
|
@@ -357,6 +390,11 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
357
390
|
} catch (error) {
|
|
358
391
|
this.hideLoading();
|
|
359
392
|
this.showError('Failed to get AI suggestion: ' + (error as Error).message);
|
|
393
|
+
this.dispatchEvent(new CustomEvent('oncompletionerror', {
|
|
394
|
+
detail: { error: (error as Error).message },
|
|
395
|
+
bubbles: true,
|
|
396
|
+
composed: true
|
|
397
|
+
}));
|
|
360
398
|
}
|
|
361
399
|
}
|
|
362
400
|
|
|
@@ -488,6 +526,9 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
488
526
|
*/
|
|
489
527
|
private showLoading(): void {
|
|
490
528
|
this.loading.classList.add('show');
|
|
529
|
+
if (this.onLoadingChangeCallback) {
|
|
530
|
+
this.onLoadingChangeCallback(true);
|
|
531
|
+
}
|
|
491
532
|
}
|
|
492
533
|
|
|
493
534
|
/**
|
|
@@ -495,6 +536,9 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
495
536
|
*/
|
|
496
537
|
private hideLoading(): void {
|
|
497
538
|
this.loading.classList.remove('show');
|
|
539
|
+
if (this.onLoadingChangeCallback) {
|
|
540
|
+
this.onLoadingChangeCallback(false);
|
|
541
|
+
}
|
|
498
542
|
}
|
|
499
543
|
|
|
500
544
|
/**
|
|
@@ -552,6 +596,9 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
552
596
|
this.apiKey = key;
|
|
553
597
|
this._saveApiKey();
|
|
554
598
|
this.editorStatus.style.backgroundColor = this.apiKey ? '#4caf50' : '#777';
|
|
599
|
+
if (this.onStatusChangeCallback) {
|
|
600
|
+
this.onStatusChangeCallback(!!this.apiKey);
|
|
601
|
+
}
|
|
555
602
|
}
|
|
556
603
|
|
|
557
604
|
/**
|
|
@@ -661,6 +708,54 @@ export class AITextEditorElement extends HTMLElement {
|
|
|
661
708
|
return this.context;
|
|
662
709
|
}
|
|
663
710
|
|
|
711
|
+
/**
|
|
712
|
+
* Configure the editor with callbacks and embedded mode
|
|
713
|
+
*/
|
|
714
|
+
configure(config: Partial<AITextEditorConfig>): void {
|
|
715
|
+
if (config.embedded !== undefined) {
|
|
716
|
+
this.embedded = config.embedded;
|
|
717
|
+
if (this.embedded) {
|
|
718
|
+
this.setAttribute('embedded', '');
|
|
719
|
+
} else {
|
|
720
|
+
this.removeAttribute('embedded');
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (config.onStatusChange) {
|
|
725
|
+
this.onStatusChangeCallback = config.onStatusChange;
|
|
726
|
+
// Immediately call with current status
|
|
727
|
+
this.onStatusChangeCallback(!!this.apiKey);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (config.onLoadingChange) {
|
|
731
|
+
this.onLoadingChangeCallback = config.onLoadingChange;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (config.apiKey !== undefined) {
|
|
735
|
+
this.setApiKey(config.apiKey);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (config.suggestionDelay !== undefined) {
|
|
739
|
+
this.setSuggestionDelay(config.suggestionDelay);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (config.systemPrompt !== undefined) {
|
|
743
|
+
this.setSystemPrompt(config.systemPrompt);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (config.apiEndpoint !== undefined) {
|
|
747
|
+
this.setApiEndpoint(config.apiEndpoint);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (config.modelName !== undefined) {
|
|
751
|
+
this.setModelName(config.modelName);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (config.context !== undefined) {
|
|
755
|
+
this.setContext(config.context);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
664
759
|
/**
|
|
665
760
|
* Saves settings to localStorage
|
|
666
761
|
*/
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ButtonToolbar Web Component
|
|
3
|
+
* A customizable toolbar with groups of buttons, supporting horizontal/vertical orientation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type ButtonToolbarItem = {
|
|
7
|
+
id: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
icon?: string; // SVG content or icon class
|
|
10
|
+
image?: string; // Image URL
|
|
11
|
+
type?: 'default' | 'info' | 'error' | 'warn' | 'success';
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
tooltip?: string;
|
|
14
|
+
action?: string; // Optional action name to dispatch
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ButtonToolbarGroup = {
|
|
18
|
+
id?: string;
|
|
19
|
+
items: ButtonToolbarItem[];
|
|
20
|
+
class?: string; // Optional CSS class for the group
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export class ButtonToolbarElement extends HTMLElement {
|
|
24
|
+
declare shadowRoot: ShadowRoot;
|
|
25
|
+
private _groups: ButtonToolbarGroup[] = [];
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this.attachShadow({ mode: 'open' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static get observedAttributes(): string[] {
|
|
33
|
+
return ['orientation', 'groups'];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {
|
|
37
|
+
if (oldValue !== newValue) {
|
|
38
|
+
this.render();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get orientation(): 'horizontal' | 'vertical' {
|
|
43
|
+
return (this.getAttribute('orientation') as 'horizontal' | 'vertical') || 'horizontal';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
set orientation(value: 'horizontal' | 'vertical') {
|
|
47
|
+
this.setAttribute('orientation', value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get groups(): ButtonToolbarGroup[] {
|
|
51
|
+
const groupsAttr = this.getAttribute('groups');
|
|
52
|
+
if (groupsAttr) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(groupsAttr);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error('Invalid groups format:', e);
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return this._groups;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set groups(value: ButtonToolbarGroup[]) {
|
|
64
|
+
this._groups = value;
|
|
65
|
+
// Also update attribute for consistency, but be careful with large data
|
|
66
|
+
// For now, let's just render. If we want to sync property to attribute:
|
|
67
|
+
// this.setAttribute('groups', JSON.stringify(value));
|
|
68
|
+
// But usually for complex data, property is preferred source of truth if set directly.
|
|
69
|
+
|
|
70
|
+
// SmartSelect implementation:
|
|
71
|
+
// set options ( opts: SelectOption[] ) {
|
|
72
|
+
// this.setAttribute( 'options', JSON.stringify( opts ) );
|
|
73
|
+
// }
|
|
74
|
+
|
|
75
|
+
// I'll follow SmartSelect pattern for consistency
|
|
76
|
+
this.setAttribute('groups', JSON.stringify(value));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
connectedCallback(): void {
|
|
80
|
+
this.render();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private handleButtonClick(item: ButtonToolbarItem, event: Event): void {
|
|
84
|
+
if (item.disabled) return;
|
|
85
|
+
|
|
86
|
+
this.dispatchEvent(new CustomEvent('button-click', {
|
|
87
|
+
detail: {
|
|
88
|
+
id: item.id,
|
|
89
|
+
action: item.action || item.id,
|
|
90
|
+
originalEvent: event,
|
|
91
|
+
item
|
|
92
|
+
},
|
|
93
|
+
bubbles: true,
|
|
94
|
+
composed: true
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private render(): void {
|
|
99
|
+
if (!this.shadowRoot) return;
|
|
100
|
+
|
|
101
|
+
const style = `
|
|
102
|
+
:host {
|
|
103
|
+
display: block;
|
|
104
|
+
font-family: var(--liwe3-font-family, system-ui, -apple-system, sans-serif);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.toolbar {
|
|
108
|
+
display: flex;
|
|
109
|
+
gap: var(--liwe3-toolbar-gap, 0.5rem);
|
|
110
|
+
width: 100%;
|
|
111
|
+
box-sizing: border-box;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.toolbar.horizontal {
|
|
115
|
+
flex-direction: row;
|
|
116
|
+
align-items: center;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.toolbar.vertical {
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
align-items: stretch;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.group {
|
|
125
|
+
display: flex;
|
|
126
|
+
gap: 1px; /* Gap between buttons in a group */
|
|
127
|
+
background-color: var(--liwe3-toolbar-group-bg, transparent);
|
|
128
|
+
border-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.toolbar.horizontal .group {
|
|
133
|
+
flex-direction: row;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.toolbar.vertical .group {
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.button {
|
|
141
|
+
display: inline-flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
justify-content: center;
|
|
144
|
+
gap: 0.5rem;
|
|
145
|
+
padding: var(--liwe3-button-padding, 0.5rem 1rem);
|
|
146
|
+
border: none;
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
font-size: var(--liwe3-button-font-size, 0.875rem);
|
|
149
|
+
line-height: 1.25;
|
|
150
|
+
transition: all 0.2s;
|
|
151
|
+
background-color: var(--liwe3-button-bg, #f3f4f6);
|
|
152
|
+
color: var(--liwe3-button-color, #1f2937);
|
|
153
|
+
min-height: var(--liwe3-button-height, 2.5rem);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.button:hover:not(:disabled) {
|
|
157
|
+
filter: brightness(0.95);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.button:active:not(:disabled) {
|
|
161
|
+
filter: brightness(0.9);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.button:disabled {
|
|
165
|
+
opacity: 0.5;
|
|
166
|
+
cursor: not-allowed;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Button Types */
|
|
170
|
+
.button.default {
|
|
171
|
+
background-color: var(--liwe3-button-default-bg, #f3f4f6);
|
|
172
|
+
color: var(--liwe3-button-default-color, #1f2937);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.button.info {
|
|
176
|
+
background-color: var(--liwe3-button-info-bg, #3b82f6);
|
|
177
|
+
color: var(--liwe3-button-info-color, #ffffff);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.button.error {
|
|
181
|
+
background-color: var(--liwe3-button-error-bg, #ef4444);
|
|
182
|
+
color: var(--liwe3-button-error-color, #ffffff);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.button.warn {
|
|
186
|
+
background-color: var(--liwe3-button-warn-bg, #f59e0b);
|
|
187
|
+
color: var(--liwe3-button-warn-color, #ffffff);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.button.success {
|
|
191
|
+
background-color: var(--liwe3-button-success-bg, #10b981);
|
|
192
|
+
color: var(--liwe3-button-success-color, #ffffff);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Content styling */
|
|
196
|
+
.icon {
|
|
197
|
+
width: 1.25em;
|
|
198
|
+
height: 1.25em;
|
|
199
|
+
display: flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
justify-content: center;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.icon svg {
|
|
205
|
+
width: 100%;
|
|
206
|
+
height: 100%;
|
|
207
|
+
fill: currentColor;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.image {
|
|
211
|
+
width: 1.5em;
|
|
212
|
+
height: 1.5em;
|
|
213
|
+
object-fit: cover;
|
|
214
|
+
border-radius: 50%;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Group styling - rounded corners logic */
|
|
218
|
+
.toolbar.horizontal .group .button:first-child {
|
|
219
|
+
border-top-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
220
|
+
border-bottom-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.toolbar.horizontal .group .button:last-child {
|
|
224
|
+
border-top-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
225
|
+
border-bottom-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.toolbar.vertical .group .button:first-child {
|
|
229
|
+
border-top-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
230
|
+
border-top-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.toolbar.vertical .group .button:last-child {
|
|
234
|
+
border-bottom-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
235
|
+
border-bottom-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
|
|
239
|
+
const renderButton = (item: ButtonToolbarItem) => {
|
|
240
|
+
const btn = document.createElement('button');
|
|
241
|
+
btn.className = `button ${item.type || 'default'}`;
|
|
242
|
+
if (item.disabled) btn.disabled = true;
|
|
243
|
+
if (item.tooltip) btn.title = item.tooltip;
|
|
244
|
+
|
|
245
|
+
btn.onclick = (e) => this.handleButtonClick(item, e);
|
|
246
|
+
|
|
247
|
+
if (item.icon) {
|
|
248
|
+
const iconSpan = document.createElement('span');
|
|
249
|
+
iconSpan.className = 'icon';
|
|
250
|
+
// Check if it's an SVG string or just a class name (simple heuristic)
|
|
251
|
+
if (item.icon.trim().startsWith('<')) {
|
|
252
|
+
iconSpan.innerHTML = item.icon;
|
|
253
|
+
} else {
|
|
254
|
+
// Assuming it might be a class name if not SVG
|
|
255
|
+
const i = document.createElement('i');
|
|
256
|
+
i.className = item.icon;
|
|
257
|
+
iconSpan.appendChild(i);
|
|
258
|
+
}
|
|
259
|
+
btn.appendChild(iconSpan);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (item.image) {
|
|
263
|
+
const img = document.createElement('img');
|
|
264
|
+
img.src = item.image;
|
|
265
|
+
img.className = 'image';
|
|
266
|
+
img.alt = item.label || '';
|
|
267
|
+
btn.appendChild(img);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (item.label) {
|
|
271
|
+
const span = document.createElement('span');
|
|
272
|
+
span.textContent = item.label;
|
|
273
|
+
btn.appendChild(span);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return btn;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const container = document.createElement('div');
|
|
280
|
+
container.className = `toolbar ${this.orientation}`;
|
|
281
|
+
|
|
282
|
+
this._groups.forEach(group => {
|
|
283
|
+
const groupDiv = document.createElement('div');
|
|
284
|
+
groupDiv.className = `group ${group.class || ''}`;
|
|
285
|
+
|
|
286
|
+
group.items.forEach(item => {
|
|
287
|
+
groupDiv.appendChild(renderButton(item));
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
container.appendChild(groupDiv);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
this.shadowRoot.innerHTML = `<style>${style}</style>`;
|
|
294
|
+
this.shadowRoot.appendChild(container);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export const defineButtonToolbar = (): void => {
|
|
299
|
+
if (typeof window !== 'undefined' && !customElements.get('liwe3-button-toolbar')) {
|
|
300
|
+
customElements.define('liwe3-button-toolbar', ButtonToolbarElement);
|
|
301
|
+
}
|
|
302
|
+
};
|