@fleetbase/ember-ui 0.3.23 → 0.3.25
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/addon/components/layout/header/smart-nav-menu.js +7 -1
- package/addon/components/template-builder/canvas.hbs +23 -0
- package/addon/components/template-builder/canvas.js +116 -0
- package/addon/components/template-builder/element-renderer.hbs +126 -0
- package/addon/components/template-builder/element-renderer.js +398 -0
- package/addon/components/template-builder/layers-panel.hbs +99 -0
- package/addon/components/template-builder/layers-panel.js +146 -0
- package/addon/components/template-builder/properties-panel/field.hbs +7 -0
- package/addon/components/template-builder/properties-panel/field.js +9 -0
- package/addon/components/template-builder/properties-panel/section.hbs +24 -0
- package/addon/components/template-builder/properties-panel/section.js +19 -0
- package/addon/components/template-builder/properties-panel.hbs +576 -0
- package/addon/components/template-builder/properties-panel.js +413 -0
- package/addon/components/template-builder/queries-panel.hbs +84 -0
- package/addon/components/template-builder/queries-panel.js +88 -0
- package/addon/components/template-builder/query-form.hbs +260 -0
- package/addon/components/template-builder/query-form.js +309 -0
- package/addon/components/template-builder/toolbar.hbs +134 -0
- package/addon/components/template-builder/toolbar.js +106 -0
- package/addon/components/template-builder/variable-picker.hbs +210 -0
- package/addon/components/template-builder/variable-picker.js +181 -0
- package/addon/components/template-builder.hbs +119 -0
- package/addon/components/template-builder.js +567 -0
- package/addon/helpers/string-starts-with.js +14 -0
- package/addon/services/template-builder.js +72 -0
- package/addon/styles/addon.css +1 -0
- package/addon/styles/components/badge.css +66 -12
- package/addon/styles/components/template-builder.css +297 -0
- package/addon/utils/get-currency.js +1 -1
- package/app/components/template-builder/canvas.js +1 -0
- package/app/components/template-builder/element-renderer.js +1 -0
- package/app/components/template-builder/layers-panel.js +1 -0
- package/app/components/template-builder/properties-panel/field.js +1 -0
- package/app/components/template-builder/properties-panel/section.js +1 -0
- package/app/components/template-builder/properties-panel.js +1 -0
- package/app/components/template-builder/queries-panel.js +1 -0
- package/app/components/template-builder/query-form.js +1 -0
- package/app/components/template-builder/toolbar.js +1 -0
- package/app/components/template-builder/variable-picker.js +1 -0
- package/app/components/template-builder.js +1 -0
- package/app/helpers/string-starts-with.js +1 -0
- package/app/services/template-builder.js +1 -0
- package/package.json +3 -2
- package/tsconfig.declarations.json +8 -8
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
{{! Template Builder Properties Panel }}
|
|
2
|
+
<div class="tb-properties-panel flex flex-col h-full overflow-y-auto" ...attributes>
|
|
3
|
+
|
|
4
|
+
{{#if this.hasSelection}}
|
|
5
|
+
{{! ============================================================ }}
|
|
6
|
+
{{! ELEMENT PROPERTIES }}
|
|
7
|
+
{{! ============================================================ }}
|
|
8
|
+
|
|
9
|
+
{{! Header }}
|
|
10
|
+
<div class="flex items-center justify-between px-3 py-2 border-b border-gray-200 dark:border-gray-700 sticky top-0 bg-white dark:bg-gray-900 z-10">
|
|
11
|
+
<div class="flex items-center space-x-2">
|
|
12
|
+
<FaIcon @icon={{this.elementType}} class="w-3.5 h-3.5 text-blue-500" />
|
|
13
|
+
<span class="text-xs font-semibold text-gray-700 dark:text-gray-300 capitalize">{{this.elementType}} Properties</span>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
{{! POSITION SECTION }}
|
|
18
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
19
|
+
@title="Position"
|
|
20
|
+
@icon="arrows-up-down-left-right"
|
|
21
|
+
@isOpen={{this.isSectionOpen "position"}}
|
|
22
|
+
@onToggle={{fn this.toggleSection "position"}}
|
|
23
|
+
>
|
|
24
|
+
<div class="grid grid-cols-2 gap-2">
|
|
25
|
+
<TemplateBuilder::PropertiesPanel::Field @label="X">
|
|
26
|
+
<input type="number" class="tb-input" value={{this.element.x}} {{on "change" (fn this.updateNumericProp "x")}} />
|
|
27
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
28
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Y">
|
|
29
|
+
<input type="number" class="tb-input" value={{this.element.y}} {{on "change" (fn this.updateNumericProp "y")}} />
|
|
30
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
31
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Rotation">
|
|
32
|
+
<input type="number" class="tb-input" value={{this.element.rotation}} min="-360" max="360" {{on "change" (fn this.updateNumericProp "rotation")}} />
|
|
33
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
34
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Z-Index">
|
|
35
|
+
<input type="number" class="tb-input" value={{this.element.z_index}} min="1" {{on "change" (fn this.updateNumericProp "z_index")}} />
|
|
36
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
37
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Opacity" @span="2">
|
|
38
|
+
<input type="range" class="w-full" value={{this.element.opacity}} min="0" max="1" step="0.05" {{on "input" (fn this.updateNumericProp "opacity")}} />
|
|
39
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
40
|
+
</div>
|
|
41
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
42
|
+
|
|
43
|
+
{{! SIZE SECTION }}
|
|
44
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
45
|
+
@title="Size"
|
|
46
|
+
@icon="ruler-combined"
|
|
47
|
+
@isOpen={{this.isSectionOpen "size"}}
|
|
48
|
+
@onToggle={{fn this.toggleSection "size"}}
|
|
49
|
+
>
|
|
50
|
+
<div class="grid grid-cols-2 gap-2">
|
|
51
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Width">
|
|
52
|
+
<input type="number" class="tb-input" value={{this.element.width}} min="1" {{on "change" (fn this.updateNumericProp "width")}} />
|
|
53
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
54
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Height">
|
|
55
|
+
<input type="number" class="tb-input" value={{this.element.height}} min="1" {{on "change" (fn this.updateNumericProp "height")}} />
|
|
56
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
57
|
+
</div>
|
|
58
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
59
|
+
|
|
60
|
+
{{! TEXT CONTENT SECTION (text elements only) }}
|
|
61
|
+
{{#if this.hasTextContent}}
|
|
62
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
63
|
+
@title="Content"
|
|
64
|
+
@icon="align-left"
|
|
65
|
+
@isOpen={{this.isSectionOpen "content"}}
|
|
66
|
+
@onToggle={{fn this.toggleSection "content"}}
|
|
67
|
+
>
|
|
68
|
+
<div class="space-y-2">
|
|
69
|
+
<div class="relative">
|
|
70
|
+
<textarea
|
|
71
|
+
class="tb-input w-full min-h-20 resize-y font-mono text-xs"
|
|
72
|
+
placeholder="Enter text or use {variable.name} syntax..."
|
|
73
|
+
{{on "input" (fn this.updateProp "content" value="target.value")}}
|
|
74
|
+
>{{this.element.content}}</textarea>
|
|
75
|
+
</div>
|
|
76
|
+
<button
|
|
77
|
+
type="button"
|
|
78
|
+
class="flex items-center space-x-1.5 text-xs text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"
|
|
79
|
+
{{on "click" (fn this.openVariablePicker "content")}}
|
|
80
|
+
>
|
|
81
|
+
<FaIcon @icon="code" class="w-3 h-3" />
|
|
82
|
+
<span>Insert variable or formula</span>
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
86
|
+
{{/if}}
|
|
87
|
+
|
|
88
|
+
{{! IMAGE SECTION }}
|
|
89
|
+
{{#if this.isImage}}
|
|
90
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
91
|
+
@title="Image"
|
|
92
|
+
@icon="image"
|
|
93
|
+
@isOpen={{this.isSectionOpen "image"}}
|
|
94
|
+
@onToggle={{fn this.toggleSection "image"}}
|
|
95
|
+
>
|
|
96
|
+
<div class="space-y-2">
|
|
97
|
+
|
|
98
|
+
{{! Source — upload or variable token }}
|
|
99
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Image Source">
|
|
100
|
+
{{#if this.imageIsVariable}}
|
|
101
|
+
{{! Variable token display }}
|
|
102
|
+
<div class="flex items-center space-x-1">
|
|
103
|
+
<div class="flex-1 flex items-center space-x-1.5 px-2 py-1 rounded border border-blue-300 dark:border-blue-600 bg-blue-50 dark:bg-blue-900/20 min-w-0">
|
|
104
|
+
<FaIcon @icon="code" class="w-3 h-3 text-blue-500 shrink-0" />
|
|
105
|
+
<span class="text-xs text-blue-700 dark:text-blue-300 truncate font-mono">{{this.element.src}}</span>
|
|
106
|
+
</div>
|
|
107
|
+
<button type="button" class="tb-icon-btn" title="Change variable" {{on "click" (fn this.openVariablePicker "src")}}>
|
|
108
|
+
<FaIcon @icon="pen" class="w-3 h-3" />
|
|
109
|
+
</button>
|
|
110
|
+
<button type="button" class="tb-icon-btn" title="Clear" {{on "click" this.clearImageSrc}}>
|
|
111
|
+
<FaIcon @icon="xmark" class="w-3 h-3" />
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
{{else if this.imageIsUploaded}}
|
|
115
|
+
{{! Uploaded file display }}
|
|
116
|
+
<div class="flex items-center space-x-1">
|
|
117
|
+
<div class="flex-1 flex items-center space-x-1.5 px-2 py-1 rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 min-w-0">
|
|
118
|
+
{{#if this.isUploadingImage}}
|
|
119
|
+
<span class="text-xs text-gray-500 dark:text-gray-400">Uploading...</span>
|
|
120
|
+
{{else}}
|
|
121
|
+
<FaIcon @icon="image" class="w-3 h-3 text-gray-400 shrink-0" />
|
|
122
|
+
<span class="text-xs text-gray-700 dark:text-gray-300 truncate">{{this.uploadedImageFilename}}</span>
|
|
123
|
+
{{/if}}
|
|
124
|
+
</div>
|
|
125
|
+
<FileUpload
|
|
126
|
+
@name="tb-image-{{this.element.uuid}}"
|
|
127
|
+
@accept="image/*"
|
|
128
|
+
@onFileAdded={{this.onImageFileAdded}}
|
|
129
|
+
>
|
|
130
|
+
<button type="button" class="tb-icon-btn" title="Replace image">
|
|
131
|
+
<FaIcon @icon="arrow-up-from-bracket" class="w-3 h-3" />
|
|
132
|
+
</button>
|
|
133
|
+
</FileUpload>
|
|
134
|
+
<button type="button" class="tb-icon-btn" title="Clear" {{on "click" this.clearImageSrc}}>
|
|
135
|
+
<FaIcon @icon="xmark" class="w-3 h-3" />
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
{{else}}
|
|
139
|
+
{{! Empty state — upload or insert variable }}
|
|
140
|
+
<div class="space-y-1.5">
|
|
141
|
+
<FileUpload
|
|
142
|
+
@name="tb-image-{{this.element.uuid}}"
|
|
143
|
+
@accept="image/*"
|
|
144
|
+
@onFileAdded={{this.onImageFileAdded}}
|
|
145
|
+
>
|
|
146
|
+
<div class="flex items-center justify-center w-full px-3 py-3 rounded border-2 border-dashed border-gray-300 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 cursor-pointer transition-colors group">
|
|
147
|
+
{{#if this.isUploadingImage}}
|
|
148
|
+
<span class="text-xs text-gray-500 dark:text-gray-400">Uploading...</span>
|
|
149
|
+
{{else}}
|
|
150
|
+
<div class="flex flex-col items-center space-y-1">
|
|
151
|
+
<FaIcon @icon="arrow-up-from-bracket" class="w-4 h-4 text-gray-400 group-hover:text-blue-500 transition-colors" />
|
|
152
|
+
<span class="text-xs text-gray-500 dark:text-gray-400 group-hover:text-blue-500 transition-colors">Click to upload image</span>
|
|
153
|
+
</div>
|
|
154
|
+
{{/if}}
|
|
155
|
+
</div>
|
|
156
|
+
</FileUpload>
|
|
157
|
+
<div class="flex items-center space-x-1.5">
|
|
158
|
+
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
159
|
+
<span class="text-xs text-gray-400">or</span>
|
|
160
|
+
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
161
|
+
</div>
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
class="w-full flex items-center justify-center space-x-1.5 px-2 py-1.5 rounded border border-gray-200 dark:border-gray-700 text-xs text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
|
165
|
+
{{on "click" (fn this.openVariablePicker "src")}}
|
|
166
|
+
>
|
|
167
|
+
<FaIcon @icon="code" class="w-3 h-3" />
|
|
168
|
+
<span>Use variable (e.g. {company.logo})</span>
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
{{/if}}
|
|
172
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
173
|
+
|
|
174
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Alt Text">
|
|
175
|
+
<input type="text" class="tb-input" value={{this.element.alt}} {{on "change" (fn this.updateProp "alt")}} />
|
|
176
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
177
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Object Fit">
|
|
178
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "object_fit")}}>
|
|
179
|
+
{{#each this.objectFitOptions as |opt|}}
|
|
180
|
+
<option value={{opt.value}} selected={{eq this.element.object_fit opt.value}}>{{opt.label}}</option>
|
|
181
|
+
{{/each}}
|
|
182
|
+
</select>
|
|
183
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
184
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Border Radius">
|
|
185
|
+
<input type="number" class="tb-input" value={{this.element.border_radius}} min="0" {{on "change" (fn this.updateNumericProp "border_radius")}} />
|
|
186
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
187
|
+
</div>
|
|
188
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
189
|
+
{{/if}}
|
|
190
|
+
|
|
191
|
+
{{! TABLE SECTION }}
|
|
192
|
+
{{#if this.isTable}}
|
|
193
|
+
{{! Columns }}
|
|
194
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
195
|
+
@title="Columns"
|
|
196
|
+
@icon="table-columns"
|
|
197
|
+
@isOpen={{this.isSectionOpen "table-columns"}}
|
|
198
|
+
@onToggle={{fn this.toggleSection "table-columns"}}
|
|
199
|
+
>
|
|
200
|
+
<div class="space-y-1.5">
|
|
201
|
+
{{#each this.tableColumns as |col index|}}
|
|
202
|
+
<div class="flex items-center space-x-1">
|
|
203
|
+
<input
|
|
204
|
+
type="text"
|
|
205
|
+
class="tb-input flex-1"
|
|
206
|
+
placeholder="Column label"
|
|
207
|
+
value={{col.label}}
|
|
208
|
+
{{on "change" (fn this.updateColumnLabel index)}}
|
|
209
|
+
/>
|
|
210
|
+
<input
|
|
211
|
+
type="text"
|
|
212
|
+
class="tb-input flex-1"
|
|
213
|
+
placeholder="data.key"
|
|
214
|
+
value={{col.key}}
|
|
215
|
+
{{on "change" (fn this.updateColumnKey index)}}
|
|
216
|
+
/>
|
|
217
|
+
<button
|
|
218
|
+
type="button"
|
|
219
|
+
class="tb-icon-btn text-red-400 hover:text-red-600"
|
|
220
|
+
title="Remove column"
|
|
221
|
+
{{on "click" (fn this.removeColumn index)}}
|
|
222
|
+
>
|
|
223
|
+
<FaIcon @icon="xmark" class="w-3 h-3" />
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
{{/each}}
|
|
227
|
+
{{#if (eq this.tableColumns.length 0)}}
|
|
228
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-2">No columns defined. Add one below.</p>
|
|
229
|
+
{{/if}}
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
class="w-full flex items-center justify-center space-x-1.5 px-2 py-1.5 rounded border border-dashed border-gray-300 dark:border-gray-600 text-xs text-gray-500 dark:text-gray-400 hover:border-blue-400 hover:text-blue-500 transition-colors"
|
|
233
|
+
{{on "click" this.addColumn}}
|
|
234
|
+
>
|
|
235
|
+
<FaIcon @icon="plus" class="w-3 h-3" />
|
|
236
|
+
<span>Add column</span>
|
|
237
|
+
</button>
|
|
238
|
+
</div>
|
|
239
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
240
|
+
|
|
241
|
+
{{! Data Source }}
|
|
242
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
243
|
+
@title="Data Source"
|
|
244
|
+
@icon="database"
|
|
245
|
+
@isOpen={{this.isSectionOpen "table-data"}}
|
|
246
|
+
@onToggle={{fn this.toggleSection "table-data"}}
|
|
247
|
+
>
|
|
248
|
+
<div class="space-y-2">
|
|
249
|
+
|
|
250
|
+
{{! Two-mode toggle: Variable | Manual }}
|
|
251
|
+
<div class="flex rounded overflow-hidden border border-gray-200 dark:border-gray-700 text-xs">
|
|
252
|
+
<button
|
|
253
|
+
type="button"
|
|
254
|
+
class="flex-1 py-1 font-medium transition-colors
|
|
255
|
+
{{if (eq this.tableDataMode 'variable')
|
|
256
|
+
'bg-blue-500 text-white'
|
|
257
|
+
'bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700'}}"
|
|
258
|
+
{{on "click" (fn this.setTableDataMode "variable")}}
|
|
259
|
+
>Variable</button>
|
|
260
|
+
<button
|
|
261
|
+
type="button"
|
|
262
|
+
class="flex-1 py-1 font-medium border-l border-gray-200 dark:border-gray-700 transition-colors
|
|
263
|
+
{{if (eq this.tableDataMode 'manual')
|
|
264
|
+
'bg-blue-500 text-white'
|
|
265
|
+
'bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700'}}"
|
|
266
|
+
{{on "click" (fn this.setTableDataMode "manual")}}
|
|
267
|
+
>Manual</button>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
{{! ── VARIABLE MODE ── }}
|
|
271
|
+
{{#if (eq this.tableDataMode "variable")}}
|
|
272
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Data Variable">
|
|
273
|
+
<div class="flex space-x-1">
|
|
274
|
+
<input
|
|
275
|
+
type="text"
|
|
276
|
+
class="tb-input flex-1"
|
|
277
|
+
placeholder="{order.items}"
|
|
278
|
+
value={{this.element.data_source}}
|
|
279
|
+
{{on "change" (fn this.updateProp "data_source")}}
|
|
280
|
+
/>
|
|
281
|
+
<button type="button" class="tb-icon-btn" title="Insert variable"
|
|
282
|
+
{{on "click" (fn this.openVariablePicker "data_source")}}>
|
|
283
|
+
<FaIcon @icon="code" class="w-3 h-3" />
|
|
284
|
+
</button>
|
|
285
|
+
</div>
|
|
286
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
287
|
+
<p class="text-xs text-gray-400 dark:text-gray-500">The variable must resolve to an array of objects. Each object's keys should match the column keys defined above.</p>
|
|
288
|
+
|
|
289
|
+
{{! ── MANUAL MODE ── }}
|
|
290
|
+
{{else}}
|
|
291
|
+
<div class="space-y-1.5">
|
|
292
|
+
{{#each this.tableRows as |row rowIndex|}}
|
|
293
|
+
<div class="rounded border border-gray-200 dark:border-gray-700 overflow-hidden">
|
|
294
|
+
<div class="flex items-center justify-between px-2 py-1 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
|
295
|
+
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">Row {{add rowIndex 1}}</span>
|
|
296
|
+
<button
|
|
297
|
+
type="button"
|
|
298
|
+
class="text-red-400 hover:text-red-600"
|
|
299
|
+
title="Remove row"
|
|
300
|
+
{{on "click" (fn this.removeRow rowIndex)}}
|
|
301
|
+
>
|
|
302
|
+
<FaIcon @icon="xmark" class="w-3 h-3" />
|
|
303
|
+
</button>
|
|
304
|
+
</div>
|
|
305
|
+
<div class="p-1.5 space-y-1">
|
|
306
|
+
{{#each this.tableColumns as |col|}}
|
|
307
|
+
<div class="flex items-center space-x-1.5">
|
|
308
|
+
<span class="text-xs text-gray-400 dark:text-gray-500 w-16 shrink-0 truncate">{{col.label}}</span>
|
|
309
|
+
<input
|
|
310
|
+
type="text"
|
|
311
|
+
class="tb-input flex-1"
|
|
312
|
+
placeholder={{col.key}}
|
|
313
|
+
value={{get row col.key}}
|
|
314
|
+
{{on "change" (fn this.updateRowCell rowIndex col.key)}}
|
|
315
|
+
/>
|
|
316
|
+
</div>
|
|
317
|
+
{{/each}}
|
|
318
|
+
{{#if (eq this.tableColumns.length 0)}}
|
|
319
|
+
<p class="text-xs text-gray-400 text-center py-1">Define columns first.</p>
|
|
320
|
+
{{/if}}
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
{{/each}}
|
|
324
|
+
{{#if (eq this.tableRows.length 0)}}
|
|
325
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-2">No rows yet.</p>
|
|
326
|
+
{{/if}}
|
|
327
|
+
<button
|
|
328
|
+
type="button"
|
|
329
|
+
class="w-full flex items-center justify-center space-x-1.5 px-2 py-1.5 rounded border border-dashed border-gray-300 dark:border-gray-600 text-xs text-gray-500 dark:text-gray-400 hover:border-blue-400 hover:text-blue-500 transition-colors"
|
|
330
|
+
{{on "click" this.addRow}}
|
|
331
|
+
>
|
|
332
|
+
<FaIcon @icon="plus" class="w-3 h-3" />
|
|
333
|
+
<span>Add row</span>
|
|
334
|
+
</button>
|
|
335
|
+
</div>
|
|
336
|
+
{{/if}}
|
|
337
|
+
|
|
338
|
+
</div>
|
|
339
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
340
|
+
|
|
341
|
+
{{! Table Style }}
|
|
342
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
343
|
+
@title="Table Style"
|
|
344
|
+
@icon="palette"
|
|
345
|
+
@isOpen={{this.isSectionOpen "table-style"}}
|
|
346
|
+
@onToggle={{fn this.toggleSection "table-style"}}
|
|
347
|
+
>
|
|
348
|
+
<div class="grid grid-cols-2 gap-2">
|
|
349
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Header BG">
|
|
350
|
+
<input type="color" class="tb-color-input" value={{this.element.header_background}} {{on "input" (fn this.updateProp "header_background")}} />
|
|
351
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
352
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Header Text">
|
|
353
|
+
<input type="color" class="tb-color-input" value={{this.element.header_color}} {{on "input" (fn this.updateProp "header_color")}} />
|
|
354
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
355
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Border Color">
|
|
356
|
+
<input type="color" class="tb-color-input" value={{this.element.border_color}} {{on "input" (fn this.updateProp "border_color")}} />
|
|
357
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
358
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Cell Padding">
|
|
359
|
+
<input type="number" class="tb-input" value={{this.element.cell_padding}} min="0" {{on "change" (fn this.updateNumericProp "cell_padding")}} />
|
|
360
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
361
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Row Alt Color" @span="2">
|
|
362
|
+
<input type="color" class="tb-color-input" value={{this.element.row_alt_color}} {{on "input" (fn this.updateProp "row_alt_color")}} />
|
|
363
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
364
|
+
</div>
|
|
365
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
366
|
+
{{/if}}
|
|
367
|
+
|
|
368
|
+
{{! QR CODE / BARCODE SECTION }}
|
|
369
|
+
{{#if (or this.isQrCode this.isBarcode)}}
|
|
370
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
371
|
+
@title={{if this.isQrCode "QR Code" "Barcode"}}
|
|
372
|
+
@icon={{if this.isQrCode "qrcode" "barcode"}}
|
|
373
|
+
@isOpen={{this.isSectionOpen "code"}}
|
|
374
|
+
@onToggle={{fn this.toggleSection "code"}}
|
|
375
|
+
>
|
|
376
|
+
<div class="space-y-2">
|
|
377
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Value">
|
|
378
|
+
<div class="flex space-x-1">
|
|
379
|
+
<input type="text" class="tb-input flex-1" placeholder="{order.tracking_number}" value={{this.element.value}} {{on "change" (fn this.updateProp "value")}} />
|
|
380
|
+
<button type="button" class="tb-icon-btn" title="Insert variable" {{on "click" (fn this.openVariablePicker "value")}}>
|
|
381
|
+
<FaIcon @icon="code" class="w-3 h-3" />
|
|
382
|
+
</button>
|
|
383
|
+
</div>
|
|
384
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
385
|
+
{{#if this.isBarcode}}
|
|
386
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Format">
|
|
387
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "barcode_format")}}>
|
|
388
|
+
<option value="CODE128" selected={{eq this.element.barcode_format "CODE128"}}>CODE128</option>
|
|
389
|
+
<option value="CODE39" selected={{eq this.element.barcode_format "CODE39"}}>CODE39</option>
|
|
390
|
+
<option value="EAN13" selected={{eq this.element.barcode_format "EAN13"}}>EAN-13</option>
|
|
391
|
+
<option value="EAN8" selected={{eq this.element.barcode_format "EAN8"}}>EAN-8</option>
|
|
392
|
+
<option value="UPC" selected={{eq this.element.barcode_format "UPC"}}>UPC</option>
|
|
393
|
+
</select>
|
|
394
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
395
|
+
{{/if}}
|
|
396
|
+
</div>
|
|
397
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
398
|
+
{{/if}}
|
|
399
|
+
|
|
400
|
+
{{! LINE SECTION }}
|
|
401
|
+
{{#if this.isLine}}
|
|
402
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
403
|
+
@title="Line"
|
|
404
|
+
@icon="minus"
|
|
405
|
+
@isOpen={{this.isSectionOpen "line"}}
|
|
406
|
+
@onToggle={{fn this.toggleSection "line"}}
|
|
407
|
+
>
|
|
408
|
+
<div class="grid grid-cols-2 gap-2">
|
|
409
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Color">
|
|
410
|
+
<input type="color" class="tb-color-input" value={{this.element.color}} {{on "input" (fn this.updateProp "color")}} />
|
|
411
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
412
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Width (px)">
|
|
413
|
+
<input type="number" class="tb-input" value={{this.element.line_width}} min="1" {{on "change" (fn this.updateNumericProp "line_width")}} />
|
|
414
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
415
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Style" @span="2">
|
|
416
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "line_style")}}>
|
|
417
|
+
{{#each this.lineStyleOptions as |opt|}}
|
|
418
|
+
<option value={{opt.value}} selected={{eq this.element.line_style opt.value}}>{{opt.label}}</option>
|
|
419
|
+
{{/each}}
|
|
420
|
+
</select>
|
|
421
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
422
|
+
</div>
|
|
423
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
424
|
+
{{/if}}
|
|
425
|
+
|
|
426
|
+
{{! SHAPE SECTION }}
|
|
427
|
+
{{#if this.isShape}}
|
|
428
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
429
|
+
@title="Shape"
|
|
430
|
+
@icon="shapes"
|
|
431
|
+
@isOpen={{this.isSectionOpen "shape"}}
|
|
432
|
+
@onToggle={{fn this.toggleSection "shape"}}
|
|
433
|
+
>
|
|
434
|
+
<div class="grid grid-cols-2 gap-2">
|
|
435
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Shape" @span="2">
|
|
436
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "shape")}}>
|
|
437
|
+
{{#each this.shapeOptions as |opt|}}
|
|
438
|
+
<option value={{opt.value}} selected={{eq this.element.shape opt.value}}>{{opt.label}}</option>
|
|
439
|
+
{{/each}}
|
|
440
|
+
</select>
|
|
441
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
442
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Fill Color">
|
|
443
|
+
<input type="color" class="tb-color-input" value={{this.element.background_color}} {{on "input" (fn this.updateProp "background_color")}} />
|
|
444
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
445
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Border Radius">
|
|
446
|
+
<input type="number" class="tb-input" value={{this.element.border_radius}} min="0" {{on "change" (fn this.updateNumericProp "border_radius")}} />
|
|
447
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
448
|
+
</div>
|
|
449
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
450
|
+
{{/if}}
|
|
451
|
+
|
|
452
|
+
{{! TEXT STYLE SECTION (text elements only) }}
|
|
453
|
+
{{#if this.isText}}
|
|
454
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
455
|
+
@title="Text"
|
|
456
|
+
@icon="font"
|
|
457
|
+
@isOpen={{this.isSectionOpen "text"}}
|
|
458
|
+
@onToggle={{fn this.toggleSection "text"}}
|
|
459
|
+
>
|
|
460
|
+
<div class="space-y-2">
|
|
461
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Font Family">
|
|
462
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "font_family")}}>
|
|
463
|
+
{{#each this.fontFamilyOptions as |opt|}}
|
|
464
|
+
<option value={{opt.value}} selected={{eq this.element.font_family opt.value}}>{{opt.label}}</option>
|
|
465
|
+
{{/each}}
|
|
466
|
+
</select>
|
|
467
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
468
|
+
<div class="grid grid-cols-2 gap-2">
|
|
469
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Size (px)">
|
|
470
|
+
<input type="number" class="tb-input" value={{this.element.font_size}} min="6" max="200" {{on "change" (fn this.updateNumericProp "font_size")}} />
|
|
471
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
472
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Weight">
|
|
473
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "font_weight")}}>
|
|
474
|
+
{{#each this.fontWeightOptions as |opt|}}
|
|
475
|
+
<option value={{opt.value}} selected={{eq this.element.font_weight opt.value}}>{{opt.label}}</option>
|
|
476
|
+
{{/each}}
|
|
477
|
+
</select>
|
|
478
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
479
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Line Height">
|
|
480
|
+
<input type="number" class="tb-input" value={{this.element.line_height}} min="0.5" max="5" step="0.1" {{on "change" (fn this.updateNumericProp "line_height")}} />
|
|
481
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
482
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Letter Spacing">
|
|
483
|
+
<input type="number" class="tb-input" value={{this.element.letter_spacing}} step="0.5" {{on "change" (fn this.updateNumericProp "letter_spacing")}} />
|
|
484
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
485
|
+
</div>
|
|
486
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Color">
|
|
487
|
+
<input type="color" class="tb-color-input" value={{this.element.color}} {{on "input" (fn this.updateProp "color")}} />
|
|
488
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
489
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Alignment">
|
|
490
|
+
<div class="flex space-x-1">
|
|
491
|
+
{{#each this.textAlignOptions as |opt|}}
|
|
492
|
+
<button
|
|
493
|
+
type="button"
|
|
494
|
+
class="flex-1 py-1 rounded border text-xs {{if (eq this.element.text_align opt.value) 'border-blue-500 bg-blue-50 dark:bg-blue-900/20 text-blue-600' 'border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800'}}"
|
|
495
|
+
{{on "click" (fn this.updateProp "text_align" opt.value)}}
|
|
496
|
+
>
|
|
497
|
+
<FaIcon @icon={{opt.icon}} class="w-3 h-3 mx-auto" />
|
|
498
|
+
</button>
|
|
499
|
+
{{/each}}
|
|
500
|
+
</div>
|
|
501
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
502
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Background">
|
|
503
|
+
<input type="color" class="tb-color-input" value={{this.element.background_color}} {{on "input" (fn this.updateProp "background_color")}} />
|
|
504
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
505
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Padding (px)">
|
|
506
|
+
<input type="number" class="tb-input" value={{this.element.padding}} min="0" {{on "change" (fn this.updateNumericProp "padding")}} />
|
|
507
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
508
|
+
</div>
|
|
509
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
510
|
+
{{/if}}
|
|
511
|
+
|
|
512
|
+
{{! BORDER SECTION (non-line elements) }}
|
|
513
|
+
{{#if this.hasBorderOptions}}
|
|
514
|
+
<TemplateBuilder::PropertiesPanel::Section
|
|
515
|
+
@title="Border"
|
|
516
|
+
@icon="border-all"
|
|
517
|
+
@isOpen={{this.isSectionOpen "border"}}
|
|
518
|
+
@onToggle={{fn this.toggleSection "border"}}
|
|
519
|
+
>
|
|
520
|
+
<div class="grid grid-cols-2 gap-2">
|
|
521
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Width (px)">
|
|
522
|
+
<input type="number" class="tb-input" value={{this.element.border_width}} min="0" {{on "change" (fn this.updateNumericProp "border_width")}} />
|
|
523
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
524
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Color">
|
|
525
|
+
<input type="color" class="tb-color-input" value={{this.element.border_color}} {{on "input" (fn this.updateProp "border_color")}} />
|
|
526
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
527
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Style">
|
|
528
|
+
<select class="tb-input" {{on "change" (fn this.updateProp "border_style")}}>
|
|
529
|
+
{{#each this.lineStyleOptions as |opt|}}
|
|
530
|
+
<option value={{opt.value}} selected={{eq this.element.border_style opt.value}}>{{opt.label}}</option>
|
|
531
|
+
{{/each}}
|
|
532
|
+
</select>
|
|
533
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
534
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Radius (px)">
|
|
535
|
+
<input type="number" class="tb-input" value={{this.element.border_radius}} min="0" {{on "change" (fn this.updateNumericProp "border_radius")}} />
|
|
536
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
537
|
+
</div>
|
|
538
|
+
</TemplateBuilder::PropertiesPanel::Section>
|
|
539
|
+
{{/if}}
|
|
540
|
+
|
|
541
|
+
{{else}}
|
|
542
|
+
{{! ============================================================ }}
|
|
543
|
+
{{! CANVAS / TEMPLATE SETTINGS (no element selected) }}
|
|
544
|
+
{{! ============================================================ }}
|
|
545
|
+
|
|
546
|
+
<div class="flex items-center justify-between px-3 py-2 border-b border-gray-200 dark:border-gray-700 sticky top-0 bg-white dark:bg-gray-900 z-10">
|
|
547
|
+
<span class="text-xs font-semibold text-gray-700 dark:text-gray-300">Canvas Settings</span>
|
|
548
|
+
</div>
|
|
549
|
+
|
|
550
|
+
<div class="p-3 space-y-3">
|
|
551
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Template Name">
|
|
552
|
+
<input type="text" class="tb-input" value={{@template.name}} {{on "change" (fn this.updateTemplateProp "name")}} />
|
|
553
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
554
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Paper Size">
|
|
555
|
+
<select class="tb-input" {{on "change" (fn this.updateTemplateProp "paper_size")}}>
|
|
556
|
+
{{#each this.paperSizeOptions as |opt|}}
|
|
557
|
+
<option value={{opt.value}} selected={{eq @template.paper_size opt.value}}>{{opt.label}}</option>
|
|
558
|
+
{{/each}}
|
|
559
|
+
</select>
|
|
560
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
561
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Orientation">
|
|
562
|
+
<select class="tb-input" {{on "change" (fn this.updateTemplateProp "orientation")}}>
|
|
563
|
+
{{#each this.orientationOptions as |opt|}}
|
|
564
|
+
<option value={{opt.value}} selected={{eq @template.orientation opt.value}}>{{opt.label}}</option>
|
|
565
|
+
{{/each}}
|
|
566
|
+
</select>
|
|
567
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
568
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Background Color">
|
|
569
|
+
<input type="color" class="tb-color-input" value={{@template.background_color}} {{on "input" (fn this.updateTemplateProp "background_color")}} />
|
|
570
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
571
|
+
<TemplateBuilder::PropertiesPanel::Field @label="Description">
|
|
572
|
+
<textarea class="tb-input w-full min-h-16 resize-none" {{on "input" (fn this.updateTemplateProp "description" value="target.value")}}>{{@template.description}}</textarea>
|
|
573
|
+
</TemplateBuilder::PropertiesPanel::Field>
|
|
574
|
+
</div>
|
|
575
|
+
{{/if}}
|
|
576
|
+
</div>
|