gardenjs 1.6.8 → 1.7.0

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 (47) hide show
  1. package/.prettierrc +16 -5
  2. package/README.md +1 -1
  3. package/dist/assets/frame-BOcgZVOc.js +9 -0
  4. package/dist/assets/index-BDdBNVTh.css +19 -0
  5. package/dist/assets/index-DFG9gkdS.js +46 -0
  6. package/dist/assets/props-COK33XqS.js +2 -0
  7. package/dist/frame.html +2 -2
  8. package/dist/index.html +3 -3
  9. package/eslint.config.js +14 -1
  10. package/package.json +3 -1
  11. package/src/client/GardenApp.svelte +105 -92
  12. package/src/client/GardenFrame.svelte +25 -3
  13. package/src/client/assets/scss/main.scss +0 -1
  14. package/src/client/components/panes/HorizontalSplitPane.svelte +28 -24
  15. package/src/client/components/panes/VerticalSplitPane.svelte +118 -0
  16. package/src/client/components/sidebar/Sidebar.svelte +8 -11
  17. package/src/client/components/stage/Stage.svelte +76 -1
  18. package/src/client/components/stage/panel/PanelCode.svelte +7 -1
  19. package/src/client/components/stage/panel/PanelComponent.svelte +0 -1
  20. package/src/client/components/stage/panel/PanelDescription.svelte +6 -0
  21. package/src/client/components/stage/panel/PanelExamplesNav.svelte +38 -12
  22. package/src/client/components/stage/panel/ParamsPane.svelte +201 -0
  23. package/src/client/components/stage/panel/controls/ArrayControl.svelte +242 -0
  24. package/src/client/components/stage/panel/controls/BooleanControl.svelte +142 -0
  25. package/src/client/components/stage/panel/controls/ColorPickerControl.svelte +185 -0
  26. package/src/client/components/stage/panel/controls/DateControl.svelte +64 -0
  27. package/src/client/components/stage/panel/controls/DatetimeControl.svelte +64 -0
  28. package/src/client/components/stage/panel/controls/JsonControl.svelte +29 -0
  29. package/src/client/components/stage/panel/controls/MultiselectControl.svelte +354 -0
  30. package/src/client/components/stage/panel/controls/NumberControl.svelte +31 -0
  31. package/src/client/components/stage/panel/controls/ObjectControl.svelte +148 -0
  32. package/src/client/components/stage/panel/controls/RangeControl.svelte +156 -0
  33. package/src/client/components/stage/panel/controls/SelectControl.svelte +233 -0
  34. package/src/client/components/stage/panel/controls/TextInputControl.svelte +85 -0
  35. package/src/client/components/stage/panel/controls/TimeControl.svelte +64 -0
  36. package/src/client/components/stage/panel/controls/button.scss +15 -0
  37. package/src/client/components/stage/panel/controls/button_unset.scss +35 -0
  38. package/src/client/components/stage/panel/controls/input.scss +15 -0
  39. package/src/client/components/topbar/Topbar.svelte +8 -0
  40. package/src/client/logic/localStore.js +23 -0
  41. package/src/client/logic/sidebar.svelte.js +55 -0
  42. package/src/client/logic/stage.js +2 -63
  43. package/dist/assets/frame-B7ff1vAd.js +0 -9
  44. package/dist/assets/index-DfGHKcnI.css +0 -19
  45. package/dist/assets/index-WI1a7nYK.js +0 -46
  46. package/dist/assets/props-DKVIKFn7.js +0 -2
  47. package/src/client/assets/scss/base/input-number.css +0 -11
@@ -34,7 +34,7 @@
34
34
  })
35
35
  </script>
36
36
 
37
- <div>
37
+ <div class="panel_code">
38
38
  {#if devmodus}
39
39
  Running Gardenjs in devmodus. No Code view supported.
40
40
  {/if}
@@ -42,3 +42,9 @@
42
42
  <pre><code class="language-xml" bind:this={codeblock}>{code}</code></pre>
43
43
  {/if}
44
44
  </div>
45
+
46
+ <style>
47
+ .panel_code {
48
+ padding: 1.25rem;
49
+ }
50
+ </style>
@@ -167,7 +167,6 @@
167
167
  right: 0;
168
168
  bottom: 0;
169
169
  left: 0;
170
- padding: 1.25rem;
171
170
  width: 100%;
172
171
  overflow-y: auto;
173
172
  }
@@ -25,3 +25,9 @@
25
25
  <!-- eslint-disable-next-line -->
26
26
  {@html marked(das.descriptionfile || das.description)}
27
27
  </div>
28
+
29
+ <style>
30
+ .markdown-body {
31
+ padding: 1.25rem;
32
+ }
33
+ </style>
@@ -1,23 +1,49 @@
1
1
  <script>
2
- let { examples = [], selected, onSelectExample } = $props()
2
+ import ParamsPane from './ParamsPane.svelte'
3
+ import VerticalSplitPane from './../../panes/VerticalSplitPane.svelte'
4
+ let {
5
+ examples = [],
6
+ params,
7
+ values,
8
+ onChange,
9
+ onReset,
10
+ selected,
11
+ onSelectExample,
12
+ } = $props()
13
+ let leftWidth = $state('')
14
+ let maxWidth = $state('')
3
15
  </script>
4
16
 
5
- <ul class="examples">
6
- {#each examples as example, index (index)}
7
- <li class:active={selected == example}>
8
- <button onclick={() => onSelectExample(example)}>
9
- <span class="dot"></span>
10
- {example}
11
- </button>
12
- </li>
13
- {/each}
14
- </ul>
17
+ <VerticalSplitPane
18
+ {leftWidth}
19
+ {maxWidth}
20
+ onSetLeftWidth={(newLeftWidth) => (leftWidth = newLeftWidth)}
21
+ onSetMaxWidth={(newMaxWidth) => (maxWidth = newMaxWidth)}
22
+ >
23
+ {#snippet left()}
24
+ <ul class="examples">
25
+ {#each examples as example, index (index)}
26
+ <li class:active={selected == example}>
27
+ <button onclick={() => onSelectExample(example)}>
28
+ <span class="dot"></span>
29
+ {example}
30
+ </button>
31
+ </li>
32
+ {/each}
33
+ </ul>
34
+ {/snippet}
35
+ {#snippet right()}
36
+ <ParamsPane {params} {values} {onChange} {onReset} />
37
+ {/snippet}
38
+ </VerticalSplitPane>
15
39
 
16
40
  <style>
17
41
  .examples {
18
42
  list-style: none;
19
43
  margin: 0;
20
- padding: 0;
44
+ padding: 1.25rem 0 1.25rem 1.25rem;
45
+ width: 100%;
46
+ overflow-x: hidden;
21
47
  }
22
48
  .examples li button {
23
49
  display: flex;
@@ -0,0 +1,201 @@
1
+ <script>
2
+ import ArrayControl from './controls/ArrayControl.svelte'
3
+ import BooleanControl from './controls/BooleanControl.svelte'
4
+ import ColorPickerControl from './controls/ColorPickerControl.svelte'
5
+ import DateControl from './controls/DateControl.svelte'
6
+ import DatetimeControl from './controls/DatetimeControl.svelte'
7
+ import JsonControl from './controls/JsonControl.svelte'
8
+ import MultiselectControl from './controls/MultiselectControl.svelte'
9
+ import NumberControl from './controls/NumberControl.svelte'
10
+ import ObjectControl from './controls/ObjectControl.svelte'
11
+ import RangeControl from './controls/RangeControl.svelte'
12
+ import SelectControl from './controls/SelectControl.svelte'
13
+ import TextInputControl from './controls/TextInputControl.svelte'
14
+ import TimeControl from './controls/TimeControl.svelte'
15
+
16
+ let { params = [], values = {}, onChange, onReset } = $props()
17
+
18
+ const CONTROL_TYPES = [
19
+ 'checkbox',
20
+ 'number',
21
+ 'color',
22
+ 'date',
23
+ 'time',
24
+ 'datetime',
25
+ 'array',
26
+ 'object',
27
+ 'multiselect',
28
+ 'checkboxes',
29
+ 'toggle',
30
+ 'radio',
31
+ 'textarea',
32
+ 'text',
33
+ 'range',
34
+ 'select',
35
+ ]
36
+
37
+ const isValidControl = (control) =>
38
+ CONTROL_TYPES.includes(control.toLowerCase())
39
+
40
+ const getControlType = (param) => {
41
+ if (param.control && isValidControl(param.control)) {
42
+ return param.control.toLowerCase()
43
+ }
44
+ switch (param.type?.toLowerCase()) {
45
+ case 'boolean':
46
+ return 'checkbox'
47
+ case 'number':
48
+ return 'number'
49
+ case 'date':
50
+ return 'date'
51
+ case 'array':
52
+ case 'object':
53
+ return 'json'
54
+ case 'string':
55
+ default:
56
+ return 'text'
57
+ }
58
+ }
59
+ </script>
60
+
61
+ <div class="pane">
62
+ <div class="header">
63
+ <div class="title">Parameters</div>
64
+ <button
65
+ class="btn btn_reset"
66
+ title="Reset all parameters"
67
+ aria-label="Reset all parameters"
68
+ onclick={() => onReset?.()}>Reset</button
69
+ >
70
+ </div>
71
+
72
+ {#if params.length === 0}
73
+ <div class="empty">No parameters defined.</div>
74
+ {:else}
75
+ <div class="grid">
76
+ {#each params as param (param.name)}
77
+ {@const controlType = getControlType(param)}
78
+ <div class="label">
79
+ {param.label || param.name}
80
+ </div>
81
+ <div class="input">
82
+ {#if controlType === 'checkbox' || controlType === 'toggle'}
83
+ <BooleanControl
84
+ value={values?.[param.name] ?? undefined}
85
+ control={controlType}
86
+ onChange={(v) => onChange?.(param.name, v)}
87
+ />
88
+ {:else if controlType === 'number'}
89
+ <NumberControl
90
+ value={values?.[param.name] ?? null}
91
+ onChange={(v) => onChange?.(param.name, v)}
92
+ />
93
+ {:else if controlType === 'color'}
94
+ <ColorPickerControl
95
+ value={values?.[param.name] ?? undefined}
96
+ onChange={(v) => onChange?.(param.name, v)}
97
+ />
98
+ {:else if controlType === 'date'}
99
+ <DateControl
100
+ value={values?.[param.name] ?? undefined}
101
+ onChange={(v) => onChange?.(param.name, v)}
102
+ />
103
+ {:else if controlType === 'time'}
104
+ <TimeControl
105
+ value={values?.[param.name] ?? undefined}
106
+ onChange={(v) => onChange?.(param.name, v)}
107
+ />
108
+ {:else if controlType === 'datetime'}
109
+ <DatetimeControl
110
+ value={values?.[param.name] ?? undefined}
111
+ onChange={(v) => onChange?.(param.name, v)}
112
+ />
113
+ {:else if controlType === 'array'}
114
+ <ArrayControl
115
+ value={values?.[param.name] ?? []}
116
+ schema={param.schema ?? {}}
117
+ onChange={(v) => onChange?.(param.name, v)}
118
+ />
119
+ {:else if controlType === 'object'}
120
+ <ObjectControl
121
+ value={values?.[param.name] ?? {}}
122
+ schema={param.schema ?? {}}
123
+ onChange={(v) => onChange?.(param.name, v)}
124
+ />
125
+ {:else if controlType === 'multiselect' || controlType === 'checkboxes'}
126
+ <MultiselectControl
127
+ value={values?.[param.name] ?? []}
128
+ options={param.options ?? []}
129
+ control={controlType}
130
+ onChange={(v) => onChange?.(param.name, v)}
131
+ />
132
+ {:else if controlType === 'range'}
133
+ <RangeControl
134
+ value={values?.[param.name] ?? null}
135
+ min={param.min}
136
+ max={param.max}
137
+ step={param.step}
138
+ onChange={(v) => onChange?.(param.name, v)}
139
+ />
140
+ {:else if controlType === 'select' || controlType === 'radio'}
141
+ <SelectControl
142
+ value={values?.[param.name] ?? undefined}
143
+ options={param.options ?? []}
144
+ control={controlType}
145
+ onChange={(v) => onChange?.(param.name, v)}
146
+ />
147
+ {:else if controlType === 'textarea' || controlType === 'text'}
148
+ <TextInputControl
149
+ value={values?.[param.name] ?? ''}
150
+ control={controlType}
151
+ numberOfRows={param.numberOfRows}
152
+ onChange={(v) => onChange?.(param.name, v)}
153
+ />
154
+ {:else}
155
+ <JsonControl
156
+ value={values?.[param.name] ?? ''}
157
+ onChange={(v) => onChange?.(param.name, v)}
158
+ />
159
+ {/if}
160
+ </div>
161
+ {/each}
162
+ </div>
163
+ {/if}
164
+ </div>
165
+
166
+ <style>
167
+ @import './controls/button.scss';
168
+
169
+ .pane {
170
+ width: 100%;
171
+ padding: 1.25rem;
172
+ overflow-y: auto;
173
+ }
174
+ .header {
175
+ display: flex;
176
+ justify-content: space-between;
177
+ align-items: center;
178
+ margin-bottom: 1rem;
179
+ }
180
+ .title {
181
+ font-size: 0.95rem;
182
+ font-weight: 600;
183
+ color: var(--c-basic-800);
184
+ }
185
+ .empty {
186
+ color: var(--c-basic-600);
187
+ font-size: 0.9rem;
188
+ }
189
+ .grid {
190
+ display: grid;
191
+ grid-template-columns: minmax(120px, auto) 1fr;
192
+ gap: 0.75rem;
193
+ align-items: start;
194
+ }
195
+ .label {
196
+ font-size: 0.938rem;
197
+ font-weight: 500;
198
+ color: var(--c-basic-700);
199
+ max-width: 220px;
200
+ }
201
+ </style>
@@ -0,0 +1,242 @@
1
+ <script>
2
+ import ArrayControl from './ArrayControl.svelte'
3
+ import BooleanControl from './BooleanControl.svelte'
4
+ import ColorPickerControl from './ColorPickerControl.svelte'
5
+ import DateControl from './DateControl.svelte'
6
+ import DatetimeControl from './DatetimeControl.svelte'
7
+ import MultiselectControl from './MultiselectControl.svelte'
8
+ import NumberControl from './NumberControl.svelte'
9
+ import ObjectControl from './ObjectControl.svelte'
10
+ import RangeControl from './RangeControl.svelte'
11
+ import SelectControl from './SelectControl.svelte'
12
+ import TextInputControl from './TextInputControl.svelte'
13
+ import TimeControl from './TimeControl.svelte'
14
+
15
+ let { value, onChange, schema = {} } = $props()
16
+
17
+ function addItem() {
18
+ const newItem = {}
19
+ Object.keys(schema).forEach((key) => {
20
+ const fieldType = getFieldType(schema[key])
21
+ if (fieldType === 'array' || fieldType === 'multiselect') {
22
+ newItem[key] = []
23
+ } else if (fieldType === 'boolean') {
24
+ newItem[key] = undefined
25
+ } else if (fieldType === 'number' || fieldType === 'range') {
26
+ newItem[key] = null
27
+ } else if (fieldType === 'object') {
28
+ newItem[key] = {}
29
+ } else {
30
+ newItem[key] = undefined
31
+ }
32
+ })
33
+ const newArray = [...(value || []), newItem]
34
+ onChange(newArray)
35
+ }
36
+
37
+ function removeItem(index) {
38
+ const newArray = [...value]
39
+ newArray.splice(index, 1)
40
+ onChange(newArray)
41
+ }
42
+
43
+ function updateItemProperty(index, propertyKey, newValue) {
44
+ const newArray = [...value]
45
+ newArray[index] = { ...newArray[index], [propertyKey]: newValue }
46
+ onChange(newArray)
47
+ }
48
+
49
+ const getFieldType = (config) => String(config?.type ?? 'text').toLowerCase()
50
+
51
+ let items = $derived(Array.isArray(value) ? value : [])
52
+ </script>
53
+
54
+ <div class="items">
55
+ {#if items.length > 0}
56
+ {#each items as item, index (index)}
57
+ <div class="item">
58
+ <div class="item_header">
59
+ <span class="item_number">#{index + 1}</span>
60
+ <button
61
+ class="btn btn_remove"
62
+ title="Remove item"
63
+ aria-label="Remove item"
64
+ onclick={() => removeItem(index)}
65
+ >
66
+ <svg
67
+ xmlns="http://www.w3.org/2000/svg"
68
+ width="16"
69
+ viewBox="0 0 24 24"
70
+ height="16"
71
+ fill="none"
72
+ stroke="currentColor"
73
+ stroke-width="1.5"
74
+ stroke-linecap="round"
75
+ stroke-linejoin="round"
76
+ ><path
77
+ d="M10 11v6m4-6v6m5-11v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"
78
+ /></svg
79
+ >
80
+ </button>
81
+ </div>
82
+ <div class="grid">
83
+ {#each Object.entries(schema) as [key, config] (key)}
84
+ {@const fieldType = getFieldType(config)}
85
+ <div class="label">
86
+ <div class="field-label">{config.label || key}</div>
87
+ </div>
88
+ <div class="input_wrapper">
89
+ {#if fieldType === 'array'}
90
+ <ArrayControl
91
+ value={item[key] ?? []}
92
+ onChange={(v) => updateItemProperty(index, key, v)}
93
+ />
94
+ {:else if fieldType === 'boolean'}
95
+ <BooleanControl
96
+ value={item[key] ?? undefined}
97
+ onChange={(v) => updateItemProperty(index, key, v)}
98
+ />
99
+ {:else if fieldType === 'color'}
100
+ <ColorPickerControl
101
+ value={item[key] ?? undefined}
102
+ onChange={(v) => updateItemProperty(index, key, v)}
103
+ />
104
+ {:else if fieldType === 'date'}
105
+ <DateControl
106
+ value={item[key] ?? undefined}
107
+ onChange={(v) => updateItemProperty(index, key, v)}
108
+ />
109
+ {:else if fieldType === 'datetime'}
110
+ <DatetimeControl
111
+ value={item[key] ?? undefined}
112
+ onChange={(v) => updateItemProperty(index, key, v)}
113
+ />
114
+ {:else if fieldType === 'multiselect'}
115
+ <MultiselectControl
116
+ value={item[key] ?? []}
117
+ onChange={(v) => updateItemProperty(index, key, v)}
118
+ options={config.options ?? []}
119
+ control={config.control ?? 'dropdown'}
120
+ />
121
+ {:else if fieldType === 'number'}
122
+ <NumberControl
123
+ value={item[key] ?? null}
124
+ onChange={(v) => updateItemProperty(index, key, v)}
125
+ />
126
+ {:else if fieldType === 'object'}
127
+ <ObjectControl
128
+ value={item[key] ?? {}}
129
+ onChange={(v) => updateItemProperty(index, key, v)}
130
+ schema={config.schema ?? {}}
131
+ />
132
+ {:else if fieldType === 'range'}
133
+ <RangeControl
134
+ value={item[key] ?? null}
135
+ min={config.min}
136
+ max={config.max}
137
+ step={config.step}
138
+ onChange={(v) => updateItemProperty(index, key, v)}
139
+ />
140
+ {:else if fieldType === 'select'}
141
+ <SelectControl
142
+ value={item[key] ?? undefined}
143
+ onChange={(v) => updateItemProperty(index, key, v)}
144
+ options={config.options ?? []}
145
+ />
146
+ {:else if fieldType === 'time'}
147
+ <TimeControl
148
+ value={item[key] ?? undefined}
149
+ onChange={(v) => updateItemProperty(index, key, v)}
150
+ />
151
+ {:else}
152
+ <TextInputControl
153
+ value={item[key] ?? ''}
154
+ control={config.control ?? 'text'}
155
+ numberOfRows={config.numberOfRows}
156
+ onChange={(v) => updateItemProperty(index, key, v)}
157
+ />
158
+ {/if}
159
+ </div>
160
+ {/each}
161
+ </div>
162
+ </div>
163
+ {/each}
164
+ {/if}
165
+ <button class="btn btn_add" onclick={addItem}>
166
+ <svg
167
+ class="plus"
168
+ xmlns="http://www.w3.org/2000/svg"
169
+ width="14"
170
+ height="14"
171
+ viewBox="0 0 24 24"
172
+ fill="none"
173
+ stroke="currentColor"
174
+ stroke-width="2"
175
+ stroke-linecap="round"
176
+ stroke-linejoin="round"><path d="M5 12h14" /><path d="M12 5v14" /></svg
177
+ >
178
+ Add Item
179
+ </button>
180
+ </div>
181
+
182
+ <style>
183
+ @import './button.scss';
184
+
185
+ .items {
186
+ display: flex;
187
+ flex-direction: column;
188
+ gap: 1rem;
189
+ width: 100%;
190
+ }
191
+
192
+ .item {
193
+ border-left: 1px solid var(--c-basic-250);
194
+ padding: 0 0 0 0.75rem;
195
+ background: var(--c-basic-0);
196
+ }
197
+
198
+ .item_header {
199
+ display: flex;
200
+ justify-content: space-between;
201
+ align-items: center;
202
+ margin-bottom: 0.75rem;
203
+ }
204
+
205
+ .item_number {
206
+ font-size: 0.875rem;
207
+ font-weight: 600;
208
+ color: var(--c-basic-600);
209
+ }
210
+
211
+ .grid {
212
+ display: grid;
213
+ grid-template-columns: minmax(120px, auto) 1fr;
214
+ gap: 0.75rem;
215
+ align-items: start;
216
+ }
217
+ .label {
218
+ font-size: 0.938rem;
219
+ font-weight: 500;
220
+ color: var(--c-basic-700);
221
+ max-width: 220px;
222
+ }
223
+
224
+ .btn_remove {
225
+ width: 1.5rem;
226
+ height: 1.5rem;
227
+ padding: 0;
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: center;
231
+ border-radius: 0.125rem;
232
+ }
233
+
234
+ .btn_add {
235
+ max-width: 8.5rem;
236
+ }
237
+
238
+ .plus {
239
+ display: block;
240
+ margin-right: 0.25rem;
241
+ }
242
+ </style>
@@ -0,0 +1,142 @@
1
+ <script lang="ts">
2
+ let {
3
+ value,
4
+ onChange,
5
+ control = 'checkbox',
6
+ }: {
7
+ value: boolean
8
+ onChange: (value: boolean) => void
9
+ control?: 'checkbox' | 'toggle'
10
+ } = $props()
11
+ </script>
12
+
13
+ <input
14
+ type="checkbox"
15
+ class={control}
16
+ checked={value === true}
17
+ onchange={(e) => {
18
+ onChange((e.currentTarget as HTMLInputElement).checked)
19
+ }}
20
+ />
21
+ <div class="unset-area">
22
+ {#if value === undefined || value === null}
23
+ <span class="unset-info">is not set</span>
24
+ {:else}
25
+ <button class="btn_unset" onclick={() => onChange(undefined as any)}>
26
+ <svg
27
+ class="close"
28
+ xmlns="http://www.w3.org/2000/svg"
29
+ width="12"
30
+ height="12"
31
+ viewBox="0 0 24 24"
32
+ fill="none"
33
+ stroke="currentColor"
34
+ stroke-width="2"
35
+ stroke-linecap="round"
36
+ stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12" /></svg
37
+ >
38
+ unset
39
+ </button>
40
+ {/if}
41
+ </div>
42
+
43
+ <style lang="scss">
44
+ @use './button_unset.scss';
45
+
46
+ input {
47
+ border: 1px solid var(--c-basic-150);
48
+ background: var(--c-basic-0);
49
+ color: var(--c-basic-800);
50
+ }
51
+ @supports (appearance: none) {
52
+ input[type='checkbox'] {
53
+ appearance: none;
54
+ position: relative;
55
+ display: inline-block;
56
+ margin: 0;
57
+ padding: 0;
58
+ height: 1.25rem;
59
+ vertical-align: top;
60
+ outline: none;
61
+ border: 1px solid var(--c-primary);
62
+ cursor: pointer;
63
+ &:after {
64
+ position: absolute;
65
+ display: block;
66
+ content: '';
67
+ left: 0;
68
+ top: 0;
69
+ }
70
+ &:checked {
71
+ background-color: var(--c-primary);
72
+ border: 1px solid var(--c-primary);
73
+ }
74
+ &:disabled {
75
+ background-color: var(--c-text);
76
+ cursor: not-allowed;
77
+ opacity: 0.4;
78
+ &:checked {
79
+ background-color: var(--c-text);
80
+ border-color: var(--c-text);
81
+ }
82
+ }
83
+ &:hover {
84
+ &:not(:checked) {
85
+ &:not(:disabled) {
86
+ border-color: var(--c-primary);
87
+ }
88
+ }
89
+ }
90
+ &:focus-visible {
91
+ outline: none;
92
+ box-shadow: 0 0 0 1px var(--c-primary);
93
+ }
94
+ }
95
+
96
+ // checkbox variant
97
+ input[type='checkbox'].checkbox {
98
+ width: 1.25rem;
99
+ border-radius: 0;
100
+ &:after {
101
+ width: 0.313rem;
102
+ height: 0.563rem;
103
+ border: 2px solid #fff;
104
+ border-top: 0;
105
+ border-left: 0;
106
+ left: 0.4rem;
107
+ top: 0.2125rem;
108
+ transform: rotate(43deg);
109
+ opacity: var(--o, 0);
110
+ }
111
+ &:checked {
112
+ --o: 1;
113
+ }
114
+ }
115
+
116
+ // toggle variant
117
+ input[type='checkbox'].toggle {
118
+ width: 2.5rem;
119
+ border-radius: 1.125rem;
120
+ transition: background-color 0.2s ease;
121
+
122
+ &:after {
123
+ width: 1rem;
124
+ height: 1rem;
125
+ border-radius: 50%;
126
+ background-color: var(--c-basic-0);
127
+ left: 0.0625rem;
128
+ top: 0.0625rem;
129
+ transition: left 0.2s ease;
130
+ }
131
+
132
+ &:checked:after {
133
+ left: 1.3125rem;
134
+ }
135
+
136
+ &:not(:checked) {
137
+ background-color: var(--c-basic-300);
138
+ border-color: var(--c-basic-300);
139
+ }
140
+ }
141
+ }
142
+ </style>