@papu1337/builder 0.0.4 → 1.0.1

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 (124) hide show
  1. package/dist/builder/BuilderView.svelte +158 -0
  2. package/dist/builder/BuilderView.svelte.d.ts +8 -0
  3. package/dist/builder/builder.vanilla.es.js +6514 -0
  4. package/dist/builder/builder.vanilla.umd.js +89 -0
  5. package/dist/builder/canvas/canvas.svelte +92 -0
  6. package/dist/builder/canvas/canvas.svelte.d.ts +17 -0
  7. package/dist/builder/canvas/styles.css +63 -0
  8. package/dist/builder/createBuilder.svelte.d.ts +9 -0
  9. package/dist/builder/createBuilder.svelte.js +17 -0
  10. package/dist/builder/index.d.ts +8 -0
  11. package/dist/builder/index.js +7 -0
  12. package/dist/builder/leftbar/leftBar.svelte +740 -0
  13. package/dist/builder/leftbar/leftBar.svelte.d.ts +8 -0
  14. package/dist/builder/leftbar/styles.css +152 -0
  15. package/dist/builder/pageMeta.svelte.d.ts +13 -0
  16. package/dist/builder/pageMeta.svelte.js +25 -0
  17. package/dist/builder/rightbar/rightBar.svelte +100 -0
  18. package/dist/builder/rightbar/rightBar.svelte.d.ts +10 -0
  19. package/dist/builder/rightbar/styles.css +167 -0
  20. package/dist/builder/topbar/TopBar.svelte +337 -0
  21. package/dist/builder/topbar/TopBar.svelte.d.ts +12 -0
  22. package/dist/builder/topbar/styles.css +123 -0
  23. package/dist/builder/viewport.svelte.d.ts +9 -0
  24. package/dist/builder/viewport.svelte.js +17 -0
  25. package/dist/elements/_shared/Arrow.svelte +58 -0
  26. package/dist/elements/_shared/Arrow.svelte.d.ts +11 -0
  27. package/dist/elements/_shared/GradientBorder.svelte +55 -0
  28. package/dist/elements/_shared/GradientBorder.svelte.d.ts +10 -0
  29. package/dist/elements/banner/bannerElement.svelte +120 -24
  30. package/dist/elements/banner/settings.d.ts +15 -3
  31. package/dist/elements/banner/settings.js +93 -8
  32. package/dist/elements/button/buttonElement.svelte +31 -21
  33. package/dist/elements/button/settings.d.ts +12 -9
  34. package/dist/elements/button/settings.js +21 -38
  35. package/dist/elements/globalSettings.js +5 -4
  36. package/dist/elements/howItWorks/howItWorksElement.svelte +221 -0
  37. package/dist/elements/howItWorks/howItWorksElement.svelte.d.ts +7 -0
  38. package/dist/elements/howItWorks/settings.d.ts +16 -0
  39. package/dist/elements/howItWorks/settings.js +70 -0
  40. package/dist/elements/steps/settings.d.ts +17 -0
  41. package/dist/elements/steps/settings.js +69 -0
  42. package/dist/elements/steps/stepsElement.svelte +220 -0
  43. package/dist/elements/steps/stepsElement.svelte.d.ts +7 -0
  44. package/dist/elements/terms/settings.d.ts +8 -5
  45. package/dist/elements/terms/settings.js +26 -33
  46. package/dist/elements/terms/termsElement.svelte +164 -79
  47. package/dist/elements/text/settings.d.ts +5 -3
  48. package/dist/elements/text/settings.js +22 -8
  49. package/dist/elements/text/textElement.svelte +25 -20
  50. package/dist/hooks/index.d.ts +1 -0
  51. package/dist/hooks/index.js +1 -0
  52. package/dist/hooks/useTranslation.svelte.d.ts +9 -0
  53. package/dist/hooks/useTranslation.svelte.js +10 -0
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.js +3 -0
  56. package/dist/renderer/BuilderRenderer.svelte +30 -2
  57. package/dist/renderer/registry.js +7 -13
  58. package/dist/renderer/renderer.vanilla.es.js +1395 -1218
  59. package/dist/renderer/renderer.vanilla.umd.js +64 -31
  60. package/dist/renderer/resolve.d.ts +1 -1
  61. package/dist/renderer/resolve.js +28 -14
  62. package/dist/renderer/types.d.ts +2 -0
  63. package/dist/service/element.action.svelte.d.ts +21 -0
  64. package/dist/service/element.action.svelte.js +125 -0
  65. package/dist/service/element.history.svelte.d.ts +8 -0
  66. package/dist/service/element.history.svelte.js +36 -0
  67. package/dist/service/element.io.svelte.d.ts +4 -0
  68. package/dist/service/element.io.svelte.js +232 -0
  69. package/dist/service/element.reader.svelte.d.ts +4 -0
  70. package/dist/service/element.reader.svelte.js +51 -0
  71. package/dist/service/element.translate.svelte.d.ts +12 -0
  72. package/dist/service/element.translate.svelte.js +81 -0
  73. package/dist/service/index.d.ts +5 -0
  74. package/dist/service/index.js +5 -0
  75. package/dist/service/types.d.ts +13 -0
  76. package/dist/service/types.js +1 -0
  77. package/dist/settings/base.svelte.d.ts +6 -1
  78. package/dist/settings/base.svelte.js +64 -22
  79. package/dist/settings/components/ColorSettings.svelte +174 -45
  80. package/dist/settings/components/ColorSettings.svelte.d.ts +3 -2
  81. package/dist/settings/components/ListSettings.svelte +11 -12
  82. package/dist/settings/components/NumberSettings.svelte +121 -24
  83. package/dist/settings/components/RepeaterSettings.svelte +145 -0
  84. package/dist/settings/components/RepeaterSettings.svelte.d.ts +14 -0
  85. package/dist/settings/components/SegmentSettings.svelte +85 -0
  86. package/dist/settings/components/SegmentSettings.svelte.d.ts +5 -0
  87. package/dist/settings/components/SelectSettings.svelte +6 -7
  88. package/dist/settings/components/SettingsGroup.svelte +13 -68
  89. package/dist/settings/components/SettingsRenderer.svelte +76 -0
  90. package/dist/settings/components/SettingsRenderer.svelte.d.ts +8 -0
  91. package/dist/settings/components/TextSettings.svelte +52 -2
  92. package/dist/settings/components/TranslatableSettings.svelte +16 -17
  93. package/dist/settings/components/UploadSettings.svelte +7 -8
  94. package/dist/settings/groups.d.ts +23 -7
  95. package/dist/settings/groups.js +48 -24
  96. package/dist/settings/implementation.svelte.js +4 -0
  97. package/dist/settings/index.d.ts +2 -0
  98. package/dist/settings/index.js +2 -0
  99. package/dist/settings/mode.svelte.d.ts +4 -0
  100. package/dist/settings/mode.svelte.js +4 -0
  101. package/dist/settings/repeater.svelte.d.ts +26 -0
  102. package/dist/settings/repeater.svelte.js +70 -0
  103. package/dist/settings/types.d.ts +28 -2
  104. package/package.json +11 -5
  105. package/dist/elements/auth/authElement.svelte +0 -115
  106. package/dist/elements/auth/authElement.svelte.d.ts +0 -7
  107. package/dist/elements/auth/settings.d.ts +0 -25
  108. package/dist/elements/auth/settings.js +0 -63
  109. package/dist/elements/badge/badgeElement.svelte +0 -48
  110. package/dist/elements/badge/badgeElement.svelte.d.ts +0 -7
  111. package/dist/elements/badge/settings.d.ts +0 -13
  112. package/dist/elements/badge/settings.js +0 -57
  113. package/dist/elements/cards/cardsElement.svelte +0 -136
  114. package/dist/elements/cards/cardsElement.svelte.d.ts +0 -7
  115. package/dist/elements/cards/settings.d.ts +0 -14
  116. package/dist/elements/cards/settings.js +0 -52
  117. package/dist/elements/divider/dividerElement.svelte +0 -34
  118. package/dist/elements/divider/dividerElement.svelte.d.ts +0 -7
  119. package/dist/elements/divider/settings.d.ts +0 -7
  120. package/dist/elements/divider/settings.js +0 -15
  121. package/dist/elements/products/productsElement.svelte +0 -283
  122. package/dist/elements/products/productsElement.svelte.d.ts +0 -7
  123. package/dist/elements/products/settings.d.ts +0 -16
  124. package/dist/elements/products/settings.js +0 -56
@@ -1,21 +1,42 @@
1
1
  <script lang="ts">
2
- import type { BaseSettingProps } from '../types';
2
+ import type { BaseSettingProps, ColorExtra } from '../types';
3
3
 
4
- let { title, value, onchange, onpreview }: BaseSettingProps<string> = $props();
4
+ let { title, value, onchange, onpreview, allowGradient }: BaseSettingProps<string> & ColorExtra =
5
+ $props();
5
6
 
6
- function handleTextInput(e: Event) {
7
- const input = e.currentTarget as HTMLInputElement;
8
- onchange(input.value);
7
+ const isGradientValue = (candidate: string): boolean => /gradient\(/i.test(candidate);
8
+ const gradient = $derived(isGradientValue(value));
9
+
10
+ const HEX_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
11
+
12
+ let from = $state('#E0AE5A');
13
+ let to = $state('#FFFFFF');
14
+ let angle = $state(90);
15
+
16
+ $effect(() => {
17
+ if (!isGradientValue(value)) return;
18
+ const hexes = value.match(/#[0-9a-fA-F]{3,8}/g);
19
+ const deg = value.match(/(-?\d+)deg/);
20
+ if (hexes?.[0]) from = hexes[0];
21
+ if (hexes?.[1]) to = hexes[1];
22
+ if (deg) angle = Number(deg[1]);
23
+ });
24
+
25
+ const build = (): string => `linear-gradient(${angle}deg, ${from}, ${to})`;
26
+
27
+ function setSolid(): void {
28
+ if (!gradient) return;
29
+ onchange(HEX_RE.test(from) ? from : '#FFFFFF');
9
30
  }
10
31
 
11
- function handlePickerInput(e: Event) {
12
- const input = e.currentTarget as HTMLInputElement;
13
- onpreview?.(input.value);
32
+ function setGradient(): void {
33
+ if (gradient) return;
34
+ if (HEX_RE.test(value)) from = value;
35
+ onchange(build());
14
36
  }
15
37
 
16
- function handlePickerChange(e: Event) {
17
- const input = e.currentTarget as HTMLInputElement;
18
- onchange(input.value);
38
+ function pickerValue(candidate: string): string {
39
+ return candidate.startsWith('#') && candidate.length <= 7 ? candidate : '#000000';
19
40
  }
20
41
  </script>
21
42
 
@@ -23,35 +44,60 @@
23
44
  <div class="label-row">
24
45
  <label for={title}>{title}</label>
25
46
  </div>
26
- <div class="color-row">
27
- <div class="swatch-wrapper">
28
- <input
29
- id="color-picker"
30
- type="color"
31
- value={value.startsWith('#') && value.length <= 7 ? value : '#000000'}
32
- oninput={handlePickerInput}
33
- onchange={handlePickerChange}
34
- />
35
- <div class="swatch" style="background: {value}">
36
- <div class="checker"></div>
37
- </div>
47
+
48
+ {#if allowGradient}
49
+ <div class="mode">
50
+ <button type="button" class="mode__btn" class:mode__btn--active={!gradient} onclick={setSolid}>
51
+ Solid
52
+ </button>
53
+ <button type="button" class="mode__btn" class:mode__btn--active={gradient} onclick={setGradient}>
54
+ Gradient
55
+ </button>
38
56
  </div>
39
- <input
40
- type="text"
41
- class="color-text-input"
42
- {value}
43
- onchange={handleTextInput}
44
- placeholder="rgba(0,0,0,0.5)"
45
- />
46
- </div>
57
+ {/if}
58
+
59
+ {#if allowGradient && gradient}
60
+ <div class="preview" style="background-image: {value};"></div>
61
+ <div class="grad-row">
62
+ <span class="swatch-wrapper">
63
+ <input type="color" value={pickerValue(from)} oninput={(e) => { from = e.currentTarget.value; onpreview?.(build()); }} onchange={(e) => { from = e.currentTarget.value; onchange(build()); }} />
64
+ <span class="swatch" style="background: {from}"></span>
65
+ </span>
66
+ <span class="grad-lbl">From</span>
67
+ <span class="grad-val">{from}</span>
68
+ </div>
69
+ <div class="grad-row">
70
+ <span class="swatch-wrapper">
71
+ <input type="color" value={pickerValue(to)} oninput={(e) => { to = e.currentTarget.value; onpreview?.(build()); }} onchange={(e) => { to = e.currentTarget.value; onchange(build()); }} />
72
+ <span class="swatch" style="background: {to}"></span>
73
+ </span>
74
+ <span class="grad-lbl">To</span>
75
+ <span class="grad-val">{to}</span>
76
+ </div>
77
+ <div class="grad-row">
78
+ <span class="grad-lbl">Angle</span>
79
+ <input class="range" type="range" min="0" max="360" value={angle} oninput={(e) => { angle = Number(e.currentTarget.value); onpreview?.(build()); }} onchange={(e) => { angle = Number(e.currentTarget.value); onchange(build()); }} />
80
+ <span class="grad-val">{angle}&deg;</span>
81
+ </div>
82
+ <textarea class="custom" spellcheck="false" rows="2" placeholder="linear-gradient(90deg, #E0AE5A, #FFFFFF)" {value} onchange={(e) => onchange(e.currentTarget.value)}></textarea>
83
+ {:else}
84
+ <div class="color-row">
85
+ <span class="swatch-wrapper">
86
+ <input type="color" value={pickerValue(value)} oninput={(e) => onpreview?.(e.currentTarget.value)} onchange={(e) => onchange(e.currentTarget.value)} />
87
+ <span class="swatch" style="background: {value}"><span class="checker"></span></span>
88
+ </span>
89
+ <span class="hash">#</span>
90
+ <input type="text" class="color-text-input" value={value.replace(/^#/, '')} onchange={(e) => onchange(`#${e.currentTarget.value.replace(/^#/, '')}`)} placeholder="FFFFFF" />
91
+ </div>
92
+ {/if}
47
93
  </div>
48
94
 
49
95
  <style>
50
96
  .setting-item {
51
97
  display: flex;
52
98
  flex-direction: column;
53
- gap: 6px;
54
- padding: 10px 0;
99
+ gap: 8px;
100
+ padding: 12px 0;
55
101
  }
56
102
 
57
103
  .label-row {
@@ -61,20 +107,48 @@
61
107
  }
62
108
 
63
109
  label {
64
- font-size: 0.75rem;
110
+ font-size: 0.8rem;
65
111
  font-weight: 500;
66
112
  color: var(--text-secondary);
67
- text-transform: capitalize;
113
+ }
114
+
115
+ .mode {
116
+ display: flex;
117
+ gap: 3px;
118
+ padding: 3px;
119
+ background: var(--bg-elevated);
120
+ border: 1px solid var(--border-light);
121
+ border-radius: var(--radius-md);
122
+ }
123
+
124
+ .mode__btn {
125
+ flex: 1;
126
+ height: 30px;
127
+ border: none;
128
+ background: transparent;
129
+ border-radius: 6px;
130
+ cursor: pointer;
131
+ font: inherit;
132
+ font-size: 0.8rem;
133
+ font-weight: 600;
134
+ color: var(--text-secondary);
135
+ transition: background var(--transition), color var(--transition);
136
+ }
137
+
138
+ .mode__btn--active {
139
+ background: var(--bg-surface);
140
+ color: var(--accent);
141
+ box-shadow: var(--shadow-sm);
68
142
  }
69
143
 
70
144
  .color-row {
71
145
  display: flex;
72
146
  align-items: center;
73
- gap: 10px;
147
+ gap: 8px;
74
148
  background: var(--bg-elevated);
75
149
  border: 1px solid var(--border-light);
76
- border-radius: var(--radius-sm);
77
- padding: 6px 10px;
150
+ border-radius: var(--radius-md);
151
+ padding: 8px 12px;
78
152
  transition: border-color var(--transition);
79
153
  }
80
154
 
@@ -85,9 +159,9 @@
85
159
 
86
160
  .swatch-wrapper {
87
161
  position: relative;
88
- width: 22px;
89
- height: 22px;
90
- border-radius: 4px;
162
+ width: 24px;
163
+ height: 24px;
164
+ border-radius: 6px;
91
165
  overflow: hidden;
92
166
  flex-shrink: 0;
93
167
  border: 1px solid var(--border-light);
@@ -122,20 +196,75 @@
122
196
  background-size: 8px 8px;
123
197
  }
124
198
 
199
+ .hash {
200
+ font-family: var(--font-mono);
201
+ font-size: 0.8rem;
202
+ color: var(--text-muted);
203
+ }
204
+
125
205
  .color-text-input {
126
206
  flex: 1;
127
207
  min-width: 0;
128
208
  background: none;
129
209
  border: none;
130
210
  outline: none;
131
- font-size: 0.78rem;
211
+ font-size: 0.8rem;
132
212
  font-family: var(--font-mono);
133
- color: var(--text-secondary);
213
+ color: var(--text-primary);
134
214
  letter-spacing: 0.04em;
215
+ text-transform: uppercase;
135
216
  padding: 0;
136
217
  }
137
218
 
138
- .color-text-input::placeholder {
139
- color: var(--text-muted);
219
+ .preview {
220
+ height: 44px;
221
+ border-radius: var(--radius-md);
222
+ box-shadow: inset 0 0 0 1px var(--border);
223
+ background-size: cover;
224
+ }
225
+
226
+ .grad-row {
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 9px;
230
+ }
231
+
232
+ .grad-lbl {
233
+ font-size: 0.75rem;
234
+ color: var(--text-secondary);
235
+ width: 38px;
236
+ }
237
+
238
+ .grad-val {
239
+ flex: 1;
240
+ text-align: right;
241
+ font-family: var(--font-mono);
242
+ font-size: 0.72rem;
243
+ text-transform: uppercase;
244
+ color: var(--text-secondary);
245
+ }
246
+
247
+ .range {
248
+ flex: 1;
249
+ accent-color: var(--accent);
250
+ }
251
+
252
+ .custom {
253
+ width: 100%;
254
+ resize: vertical;
255
+ min-height: 44px;
256
+ padding: 8px 10px;
257
+ font-family: var(--font-mono);
258
+ font-size: 0.7rem;
259
+ line-height: 1.4;
260
+ color: var(--text-primary);
261
+ background: var(--bg-elevated);
262
+ border: 1px solid var(--border-light);
263
+ border-radius: var(--radius-md);
264
+ outline: none;
265
+ }
266
+
267
+ .custom:focus {
268
+ border-color: var(--accent);
140
269
  }
141
270
  </style>
@@ -1,4 +1,5 @@
1
- import type { BaseSettingProps } from '../types';
2
- declare const ColorSettings: import("svelte").Component<BaseSettingProps<string>, {}, "">;
1
+ import type { BaseSettingProps, ColorExtra } from '../types';
2
+ type $$ComponentProps = BaseSettingProps<string> & ColorExtra;
3
+ declare const ColorSettings: import("svelte").Component<$$ComponentProps, {}, "">;
3
4
  type ColorSettings = ReturnType<typeof ColorSettings>;
4
5
  export default ColorSettings;
@@ -70,15 +70,14 @@
70
70
  .setting-item {
71
71
  display: flex;
72
72
  flex-direction: column;
73
- gap: 8px;
74
- padding: 10px 0;
73
+ gap: 10px;
74
+ padding: 12px 0;
75
75
  }
76
76
 
77
77
  label {
78
- font-size: 0.75rem;
78
+ font-size: 0.8rem;
79
79
  font-weight: 500;
80
80
  color: var(--text-secondary);
81
- text-transform: capitalize;
82
81
  }
83
82
 
84
83
  .list-items {
@@ -90,11 +89,11 @@
90
89
  .list-entry {
91
90
  display: flex;
92
91
  flex-direction: column;
93
- gap: 6px;
94
- padding: 10px;
92
+ gap: 8px;
93
+ padding: 12px;
95
94
  background: var(--bg-elevated);
96
95
  border: 1px solid var(--border-light);
97
- border-radius: var(--radius-sm);
96
+ border-radius: var(--radius-md);
98
97
  }
99
98
 
100
99
  .entry-header {
@@ -128,12 +127,12 @@
128
127
  input,
129
128
  textarea {
130
129
  width: 100%;
131
- padding: 7px 10px;
130
+ padding: 9px 11px;
132
131
  background: var(--bg-base, #1a1a2e);
133
132
  border: 1px solid var(--border-light);
134
- border-radius: var(--radius-sm);
133
+ border-radius: var(--radius-md);
135
134
  color: var(--text-primary);
136
- font-size: 0.82rem;
135
+ font-size: 0.8rem;
137
136
  font-family: var(--font);
138
137
  outline: none;
139
138
  resize: vertical;
@@ -149,10 +148,10 @@
149
148
  }
150
149
 
151
150
  .add-btn {
152
- padding: 8px 12px;
151
+ padding: 9px 11px;
153
152
  background: var(--bg-elevated);
154
153
  border: 1px dashed var(--border-light);
155
- border-radius: var(--radius-sm);
154
+ border-radius: var(--radius-md);
156
155
  color: var(--text-secondary);
157
156
  font-size: 0.8rem;
158
157
  cursor: pointer;
@@ -1,29 +1,60 @@
1
1
  <script lang="ts">
2
2
  import type { BaseSettingProps, NumberExtra } from '../types';
3
3
 
4
- let { title, value, onchange, min, max, step }: BaseSettingProps<number> & NumberExtra = $props();
4
+ let { title, value, onchange, onpreview, min, max, step }: BaseSettingProps<number> & NumberExtra =
5
+ $props();
6
+
7
+ const hasRange = $derived(min !== undefined && max !== undefined);
5
8
  </script>
6
9
 
7
10
  <div class="setting-item">
8
11
  <div class="label-row">
9
12
  <label for={title}>{title}</label>
10
13
  </div>
11
- <input
12
- type="number"
13
- {value}
14
- {min}
15
- {max}
16
- {step}
17
- onchange={(e) => onchange(Number(e.currentTarget.value))}
18
- />
14
+
15
+ {#if hasRange}
16
+ <div class="number-slider">
17
+ <input
18
+ class="range"
19
+ type="range"
20
+ {min}
21
+ {max}
22
+ step={step ?? 1}
23
+ {value}
24
+ oninput={(e) => onpreview?.(Number(e.currentTarget.value))}
25
+ onchange={(e) => onchange(Number(e.currentTarget.value))}
26
+ />
27
+ <div class="number-box">
28
+ <input
29
+ type="number"
30
+ {min}
31
+ {max}
32
+ {step}
33
+ {value}
34
+ onchange={(e) => onchange(Number(e.currentTarget.value))}
35
+ />
36
+ <span class="number-unit">px</span>
37
+ </div>
38
+ </div>
39
+ {:else}
40
+ <input
41
+ class="number-plain"
42
+ type="number"
43
+ {value}
44
+ {min}
45
+ {max}
46
+ {step}
47
+ onchange={(e) => onchange(Number(e.currentTarget.value))}
48
+ />
49
+ {/if}
19
50
  </div>
20
51
 
21
52
  <style>
22
53
  .setting-item {
23
54
  display: flex;
24
55
  flex-direction: column;
25
- gap: 6px;
26
- padding: 10px 0;
56
+ gap: 8px;
57
+ padding: 12px 0;
27
58
  }
28
59
 
29
60
  .label-row {
@@ -33,20 +64,92 @@
33
64
  }
34
65
 
35
66
  label {
36
- font-size: 0.75rem;
67
+ font-size: 0.8rem;
37
68
  font-weight: 500;
38
69
  color: var(--text-secondary);
39
- text-transform: capitalize;
40
70
  }
41
71
 
42
- input[type='number'] {
72
+ .number-slider {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: 12px;
76
+ }
77
+
78
+ .range {
79
+ flex: 1;
80
+ -webkit-appearance: none;
81
+ appearance: none;
82
+ height: 4px;
83
+ border-radius: 999px;
84
+ background: var(--bg-hover);
85
+ outline: none;
86
+ cursor: pointer;
87
+ }
88
+
89
+ .range::-webkit-slider-thumb {
90
+ -webkit-appearance: none;
91
+ appearance: none;
92
+ width: 16px;
93
+ height: 16px;
94
+ border-radius: 50%;
95
+ background: var(--accent);
96
+ cursor: pointer;
97
+ border: 2px solid var(--bg-surface);
98
+ box-shadow: var(--shadow-sm);
99
+ }
100
+
101
+ .range::-moz-range-thumb {
102
+ width: 16px;
103
+ height: 16px;
104
+ border-radius: 50%;
105
+ background: var(--accent);
106
+ cursor: pointer;
107
+ border: 2px solid var(--bg-surface);
108
+ }
109
+
110
+ .number-box {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 4px;
114
+ width: 92px;
115
+ flex: none;
116
+ padding: 0 11px;
117
+ background: var(--bg-elevated);
118
+ border: 1px solid var(--border-light);
119
+ border-radius: var(--radius-md);
120
+ transition: border-color var(--transition);
121
+ }
122
+
123
+ .number-box:focus-within {
124
+ border-color: var(--accent);
125
+ box-shadow: 0 0 0 3px var(--accent-glow);
126
+ }
127
+
128
+ .number-box input {
43
129
  width: 100%;
44
- padding: 7px 10px;
130
+ height: 36px;
131
+ border: none;
132
+ background: transparent;
133
+ outline: none;
134
+ text-align: right;
135
+ font: inherit;
136
+ font-size: 0.8rem;
137
+ color: var(--text-primary);
138
+ }
139
+
140
+ .number-unit {
141
+ font-size: 0.75rem;
142
+ color: var(--text-muted);
143
+ }
144
+
145
+ .number-plain {
146
+ width: 100%;
147
+ padding: 9px 11px;
45
148
  background: var(--bg-elevated);
46
149
  border: 1px solid var(--border-light);
47
- border-radius: var(--radius-sm);
150
+ border-radius: var(--radius-md);
48
151
  color: var(--text-primary);
49
- font-size: 0.82rem;
152
+ font-size: 0.8rem;
50
153
  font-family: var(--font);
51
154
  outline: none;
52
155
  transition:
@@ -54,13 +157,7 @@
54
157
  box-shadow var(--transition);
55
158
  }
56
159
 
57
- input[type='number']::-webkit-inner-spin-button,
58
- input[type='number']::-webkit-outer-spin-button {
59
- opacity: 0.4;
60
- filter: invert(1);
61
- }
62
-
63
- input[type='number']:focus {
160
+ .number-plain:focus {
64
161
  border-color: var(--accent);
65
162
  box-shadow: 0 0 0 3px var(--accent-glow);
66
163
  }
@@ -0,0 +1,145 @@
1
+ <script lang="ts">
2
+ import type { Settings } from '../base.svelte';
3
+ import type { SettingMap } from '../types';
4
+ import SettingsRenderer from './SettingsRenderer.svelte';
5
+
6
+ interface RepeaterProps {
7
+ title: string;
8
+ itemLabel: string;
9
+ addButtonText: string;
10
+ items: Settings<SettingMap>[];
11
+ onadd: () => void;
12
+ onremove: (index: number) => void;
13
+ onmove: (from: number, to: number) => void;
14
+ }
15
+
16
+ let { title, itemLabel, addButtonText, items, onadd, onremove, onmove }: RepeaterProps = $props();
17
+ </script>
18
+
19
+ <div class="repeater">
20
+ <div class="repeater-title">{title}</div>
21
+
22
+ {#each items as item, index (index)}
23
+ <div class="repeater-item">
24
+ <div class="repeater-item-head">
25
+ <span class="repeater-item-label">{itemLabel} {index + 1}</span>
26
+ <div class="repeater-item-actions">
27
+ <button
28
+ type="button"
29
+ title="Move up"
30
+ disabled={index === 0}
31
+ onclick={() => onmove(index, index - 1)}
32
+ >
33
+ &#8593;
34
+ </button>
35
+ <button
36
+ type="button"
37
+ title="Move down"
38
+ disabled={index === items.length - 1}
39
+ onclick={() => onmove(index, index + 1)}
40
+ >
41
+ &#8595;
42
+ </button>
43
+ <button type="button" class="remove" title="Remove" onclick={() => onremove(index)}>
44
+ &#10005;
45
+ </button>
46
+ </div>
47
+ </div>
48
+ <div class="repeater-item-body">
49
+ <SettingsRenderer entries={item.entries} />
50
+ </div>
51
+ </div>
52
+ {/each}
53
+
54
+ <button type="button" class="repeater-add" onclick={onadd}>{addButtonText}</button>
55
+ </div>
56
+
57
+ <style>
58
+ .repeater {
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: 10px;
62
+ padding: 12px 0;
63
+ }
64
+
65
+ .repeater-title {
66
+ font-size: 0.72rem;
67
+ font-weight: 600;
68
+ color: var(--text-secondary);
69
+ }
70
+
71
+ .repeater-item {
72
+ border: 1px solid var(--border-light);
73
+ border-radius: var(--radius-md);
74
+ background: var(--bg-surface);
75
+ overflow: hidden;
76
+ }
77
+
78
+ .repeater-item-head {
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: space-between;
82
+ padding: 8px 10px;
83
+ background: var(--bg-elevated);
84
+ border-bottom: 1px solid var(--border-light);
85
+ }
86
+
87
+ .repeater-item-label {
88
+ font-size: 0.72rem;
89
+ font-weight: 700;
90
+ letter-spacing: 0.06em;
91
+ text-transform: uppercase;
92
+ color: var(--text-secondary);
93
+ }
94
+
95
+ .repeater-item-actions {
96
+ display: flex;
97
+ gap: 4px;
98
+ }
99
+
100
+ .repeater-item-actions button {
101
+ width: 24px;
102
+ height: 22px;
103
+ display: inline-flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ border: 1px solid var(--border-light);
107
+ background: var(--bg-surface);
108
+ border-radius: var(--radius-sm);
109
+ color: var(--text-secondary);
110
+ cursor: pointer;
111
+ font-size: 0.7rem;
112
+ line-height: 1;
113
+ }
114
+
115
+ .repeater-item-actions button:disabled {
116
+ opacity: 0.35;
117
+ cursor: not-allowed;
118
+ }
119
+
120
+ .repeater-item-actions .remove:hover:not(:disabled) {
121
+ color: #e5484d;
122
+ border-color: #e5484d;
123
+ }
124
+
125
+ .repeater-item-body {
126
+ padding: 4px 10px 10px;
127
+ }
128
+
129
+ .repeater-add {
130
+ height: 36px;
131
+ border: 1px dashed var(--border);
132
+ background: var(--bg-elevated);
133
+ border-radius: var(--radius-md);
134
+ color: var(--text-secondary);
135
+ font-size: 0.72rem;
136
+ font-weight: 600;
137
+ cursor: pointer;
138
+ transition: border-color var(--transition), color var(--transition);
139
+ }
140
+
141
+ .repeater-add:hover {
142
+ border-color: var(--accent);
143
+ color: var(--accent);
144
+ }
145
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { Settings } from '../base.svelte';
2
+ import type { SettingMap } from '../types';
3
+ interface RepeaterProps {
4
+ title: string;
5
+ itemLabel: string;
6
+ addButtonText: string;
7
+ items: Settings<SettingMap>[];
8
+ onadd: () => void;
9
+ onremove: (index: number) => void;
10
+ onmove: (from: number, to: number) => void;
11
+ }
12
+ declare const RepeaterSettings: import("svelte").Component<RepeaterProps, {}, "">;
13
+ type RepeaterSettings = ReturnType<typeof RepeaterSettings>;
14
+ export default RepeaterSettings;