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
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ let {
3
+ value,
4
+ onChange,
5
+ }: { value: number; onChange: (value: number) => void } = $props()
6
+ </script>
7
+
8
+ <input
9
+ class="input"
10
+ type="number"
11
+ {value}
12
+ oninput={(e) => {
13
+ const newValue = (e.currentTarget as HTMLInputElement).valueAsNumber
14
+ if (Number.isFinite(newValue)) onChange(newValue)
15
+ }}
16
+ />
17
+
18
+ <style>
19
+ @import './input.scss';
20
+
21
+ .input {
22
+ padding-right: 0;
23
+ -moz-appearance: auto;
24
+ appearance: auto;
25
+ }
26
+ .input::-webkit-inner-spin-button,
27
+ .input::-webkit-outer-spin-button {
28
+ -webkit-appearance: auto;
29
+ margin: 0;
30
+ }
31
+ </style>
@@ -0,0 +1,148 @@
1
+ <script>
2
+ let { value, onChange, schema = {} } = $props()
3
+
4
+ function addProperty() {
5
+ const defaultKey = schema.keyDefault ?? ''
6
+ const defaultValue = schema.valueDefault ?? ''
7
+ const newObject = { ...(value || {}), [defaultKey]: defaultValue }
8
+ onChange(newObject)
9
+ }
10
+
11
+ function removeProperty(key) {
12
+ const newObject = { ...value }
13
+ delete newObject[key]
14
+ onChange(newObject)
15
+ }
16
+
17
+ function updateKey(oldKey, newKey) {
18
+ if (oldKey === newKey) return
19
+ const newObject = {}
20
+ for (const k in value) {
21
+ if (k === oldKey) {
22
+ newObject[newKey] = value[oldKey]
23
+ } else {
24
+ newObject[k] = value[k]
25
+ }
26
+ }
27
+ onChange(newObject)
28
+ }
29
+
30
+ function updateValue(key, newValue) {
31
+ const newObject = { ...value, [key]: newValue }
32
+ onChange(newObject)
33
+ }
34
+
35
+ let entries = $derived(Object.entries(value || {}))
36
+ </script>
37
+
38
+ <div class="object-param">
39
+ {#if entries.length > 0}
40
+ {#each entries as [key, val], index (index)}
41
+ <div class="object-item">
42
+ <input
43
+ class="input input_key"
44
+ type="text"
45
+ value={key}
46
+ placeholder="key"
47
+ oninput={(e) => updateKey(key, e.currentTarget.value)}
48
+ />
49
+ <span class="separator">:</span>
50
+ <input
51
+ type="text"
52
+ class="input input_value"
53
+ value={String(val ?? '')}
54
+ placeholder="value"
55
+ oninput={(e) => updateValue(key, e.currentTarget.value)}
56
+ />
57
+ <button
58
+ class="btn btn_remove"
59
+ title="Remove property"
60
+ aria-label="Remove property"
61
+ onclick={() => removeProperty(key)}
62
+ >
63
+ <svg
64
+ xmlns="http://www.w3.org/2000/svg"
65
+ width="16"
66
+ viewBox="0 0 24 24"
67
+ height="16"
68
+ fill="none"
69
+ stroke="currentColor"
70
+ stroke-width="1.5"
71
+ stroke-linecap="round"
72
+ stroke-linejoin="round"
73
+ ><path
74
+ 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"
75
+ /></svg
76
+ >
77
+ </button>
78
+ </div>
79
+ {/each}
80
+ {/if}
81
+ <button class="btn" onclick={addProperty}>
82
+ <svg
83
+ class="plus"
84
+ xmlns="http://www.w3.org/2000/svg"
85
+ width="14"
86
+ height="14"
87
+ viewBox="0 0 24 24"
88
+ fill="none"
89
+ stroke="currentColor"
90
+ stroke-width="2"
91
+ stroke-linecap="round"
92
+ stroke-linejoin="round"><path d="M5 12h14" /><path d="M12 5v14" /></svg
93
+ >
94
+ Add Property
95
+ </button>
96
+ </div>
97
+
98
+ <style>
99
+ @import './button.scss';
100
+ @import './input.scss';
101
+
102
+ .object-param {
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 0.375rem;
106
+ width: 100%;
107
+ }
108
+
109
+ .object-item {
110
+ display: flex;
111
+ gap: 0.25rem;
112
+ align-items: center;
113
+ }
114
+
115
+ .input_key {
116
+ flex: 0 0 35%;
117
+ min-width: 0;
118
+ }
119
+
120
+ .separator {
121
+ color: var(--c-basic-500);
122
+ font-weight: 500;
123
+ }
124
+
125
+ .input.input_value {
126
+ flex: 1;
127
+ min-width: 0;
128
+ }
129
+
130
+ .btn_remove {
131
+ width: 1.5rem;
132
+ height: 1.5rem;
133
+ padding: 0;
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ border-radius: 0.125rem;
138
+ }
139
+
140
+ .btn {
141
+ max-width: 8.5rem;
142
+ }
143
+
144
+ .plus {
145
+ display: block;
146
+ margin-right: 0.25rem;
147
+ }
148
+ </style>
@@ -0,0 +1,156 @@
1
+ <script lang="ts">
2
+ let {
3
+ value,
4
+ onChange,
5
+ min = 0,
6
+ max = 100,
7
+ step = 1,
8
+ }: {
9
+ value: number | null
10
+ onChange: (value: number | null) => void
11
+ min?: number
12
+ max?: number
13
+ step?: number
14
+ } = $props()
15
+
16
+ let isUnset = $derived(value === undefined || value === null)
17
+ </script>
18
+
19
+ <div class="row">
20
+ <div class="slider-wrapper">
21
+ <input
22
+ class="range"
23
+ type="range"
24
+ {min}
25
+ {max}
26
+ {step}
27
+ value={value ?? min}
28
+ oninput={(e) => {
29
+ const newValue = parseFloat((e.currentTarget as HTMLInputElement).value)
30
+ onChange(newValue)
31
+ }}
32
+ />
33
+ </div>
34
+ <div class="value">{isUnset ? '-' : value}</div>
35
+ <div class="unset-area">
36
+ {#if !isUnset}
37
+ <button class="btn_unset" onclick={() => onChange(null)}>
38
+ <svg
39
+ class="close"
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ width="12"
42
+ height="12"
43
+ viewBox="0 0 24 24"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ stroke-width="2"
47
+ stroke-linecap="round"
48
+ stroke-linejoin="round"
49
+ >
50
+ <path d="M18 6L6 18M6 6l12 12" />
51
+ </svg>
52
+ unset
53
+ </button>
54
+ {:else}
55
+ <span class="unset-info">is not set</span>
56
+ {/if}
57
+ </div>
58
+ </div>
59
+
60
+ <style lang="scss">
61
+ @use './button_unset.scss';
62
+
63
+ .row {
64
+ display: flex;
65
+ gap: 0.5rem;
66
+ align-items: center;
67
+ }
68
+
69
+ .slider-wrapper {
70
+ flex: 1;
71
+ display: flex;
72
+ align-items: center;
73
+ height: 1.5rem;
74
+ }
75
+
76
+ .range {
77
+ width: 100%;
78
+ cursor: pointer;
79
+ -webkit-appearance: none;
80
+ appearance: none;
81
+ background: transparent;
82
+ outline: none;
83
+ padding: 0;
84
+ margin: 0;
85
+ }
86
+
87
+ .range::-webkit-slider-runnable-track {
88
+ width: 100%;
89
+ height: 0.25rem;
90
+ background-color: var(--c-basic-250);
91
+ border-radius: 0.125rem;
92
+ border: none;
93
+ }
94
+
95
+ .range::-webkit-slider-thumb {
96
+ -webkit-appearance: none;
97
+ appearance: none;
98
+ width: 1.125rem;
99
+ height: 1.125rem;
100
+ background: var(--c-primary);
101
+ border-radius: 50%;
102
+ cursor: pointer;
103
+ box-shadow: 0 1px 0.25rem rgba(0, 0, 0, 0.3);
104
+ margin-top: -8px;
105
+ }
106
+
107
+ .range::-webkit-slider-thumb:hover {
108
+ background: var(--c-primary-hover, var(--c-primary));
109
+ }
110
+
111
+ .range::-moz-range-track {
112
+ width: 100%;
113
+ height: 0.25rem;
114
+ background: var(--c-basic-300);
115
+ border-radius: 0.125rem;
116
+ border: none;
117
+ }
118
+
119
+ .range::-moz-range-thumb {
120
+ width: 1.125rem;
121
+ height: 1.125rem;
122
+ background: var(--c-primary);
123
+ border: 0.125rem solid var(--c-basic-0);
124
+ border-radius: 50%;
125
+ cursor: pointer;
126
+ box-shadow: 0 1px 0.25rem rgba(0, 0, 0, 0.3);
127
+ }
128
+
129
+ .range::-moz-range-thumb:hover {
130
+ background: var(--c-primary-hover, var(--c-primary));
131
+ }
132
+
133
+ .range:focus {
134
+ outline: none;
135
+ }
136
+
137
+ .range:focus::-webkit-slider-thumb {
138
+ box-shadow:
139
+ 0 0 0 0.188rem var(--c-primary-bg),
140
+ 0 1px 0.25rem rgba(0, 0, 0, 0.3);
141
+ }
142
+
143
+ .range:focus::-moz-range-thumb {
144
+ box-shadow:
145
+ 0 0 0 0.188rem var(--c-primary-bg),
146
+ 0 1px 0.25rem rgba(0, 0, 0, 0.3);
147
+ }
148
+
149
+ .value {
150
+ min-width: 3rem;
151
+ text-align: right;
152
+ font-size: 0.938rem;
153
+ font-weight: 500;
154
+ color: var(--c-basic-800);
155
+ }
156
+ </style>
@@ -0,0 +1,233 @@
1
+ <script>
2
+ let { value, onChange, options = [], control = 'dropdown' } = $props()
3
+
4
+ let isUnset = $derived(!value || value === undefined || value === null)
5
+ </script>
6
+
7
+ {#if control === 'radio'}
8
+ <div class="radio-group">
9
+ {#each options as option, index (option.value ?? option ?? index)}
10
+ {@const optionValue = option.value ?? option}
11
+ {@const optionLabel = option.label ?? option}
12
+ <label class="radio-option">
13
+ <input
14
+ type="radio"
15
+ name="radio-{Math.random()}"
16
+ value={optionValue}
17
+ checked={value === optionValue}
18
+ onchange={(e) => onChange(e.currentTarget.value)}
19
+ />
20
+ <span class="radio-label">{optionLabel}</span>
21
+ </label>
22
+ {/each}
23
+ </div>
24
+ <div class="unset-area">
25
+ {#if isUnset}
26
+ <div class="unset-info radio-unset">is not set</div>
27
+ {:else}
28
+ <button
29
+ class="btn_unset radio-btn-unset"
30
+ onclick={() => onChange(undefined)}
31
+ >
32
+ <svg
33
+ class="close"
34
+ xmlns="http://www.w3.org/2000/svg"
35
+ width="12"
36
+ height="12"
37
+ viewBox="0 0 24 24"
38
+ fill="none"
39
+ stroke="currentColor"
40
+ stroke-width="2"
41
+ stroke-linecap="round"
42
+ stroke-linejoin="round"
43
+ >
44
+ <path d="M18 6L6 18M6 6l12 12" />
45
+ </svg>
46
+ unset
47
+ </button>
48
+ {/if}
49
+ </div>
50
+ {:else}
51
+ <div class="row">
52
+ <div class="container">
53
+ <select
54
+ class="select"
55
+ value={value ?? ''}
56
+ onchange={(e) => onChange(e.currentTarget.value)}
57
+ >
58
+ {#if !value}
59
+ <option value="" disabled>Select an option...</option>
60
+ {/if}
61
+ {#each options as option, index (option.value ?? option ?? index)}
62
+ <option value={option.value ?? option}>
63
+ {option.label ?? option}
64
+ </option>
65
+ {/each}
66
+ </select>
67
+ <svg
68
+ class="icon"
69
+ xmlns="http://www.w3.org/2000/svg"
70
+ width="20"
71
+ height="20"
72
+ viewBox="0 0 24 24"
73
+ fill="none"
74
+ stroke="currentColor"
75
+ stroke-width="1.5"
76
+ stroke-linecap="round"
77
+ stroke-linejoin="round"
78
+ >
79
+ <path d="M6 9l6 6 6-6" />
80
+ </svg>
81
+ </div>
82
+ <div class="unset-area">
83
+ {#if isUnset}
84
+ <span class="unset-info">is not set</span>
85
+ {:else}
86
+ <button class="btn_unset" onclick={() => onChange(undefined)}>
87
+ <svg
88
+ class="close"
89
+ xmlns="http://www.w3.org/2000/svg"
90
+ width="12"
91
+ height="12"
92
+ viewBox="0 0 24 24"
93
+ fill="none"
94
+ stroke="currentColor"
95
+ stroke-width="2"
96
+ stroke-linecap="round"
97
+ stroke-linejoin="round"
98
+ >
99
+ <path d="M18 6L6 18M6 6l12 12" />
100
+ </svg>
101
+ unset
102
+ </button>
103
+ {/if}
104
+ </div>
105
+ </div>
106
+ {/if}
107
+
108
+ <style lang="scss">
109
+ @use './button_unset.scss';
110
+
111
+ .row {
112
+ display: flex;
113
+ gap: 0.5rem;
114
+ align-items: center;
115
+ }
116
+
117
+ .container {
118
+ position: relative;
119
+ display: block;
120
+ flex: 1;
121
+ }
122
+
123
+ .select {
124
+ padding: 0.125rem 0.5rem;
125
+ width: 100%;
126
+ height: 1.5rem;
127
+ border: 1px solid var(--c-primary);
128
+ border-radius: 0.125rem;
129
+ background-color: var(--c-basic-0);
130
+ font-size: 0.938rem;
131
+ color: var(--c-basic-800);
132
+ cursor: pointer;
133
+ appearance: none;
134
+ -webkit-appearance: none;
135
+ -moz-appearance: none;
136
+ }
137
+
138
+ .select:focus {
139
+ outline: none;
140
+ box-shadow: 0 0 0 1px var(--c-primary);
141
+ }
142
+
143
+ .select:disabled {
144
+ opacity: 0.5;
145
+ cursor: not-allowed;
146
+ }
147
+
148
+ .icon {
149
+ position: absolute;
150
+ top: 50%;
151
+ right: 0.5rem;
152
+ transform: translateY(-50%);
153
+ color: var(--c-primary);
154
+ pointer-events: none;
155
+ }
156
+
157
+ /* Radio variant styles */
158
+ .radio-group {
159
+ display: flex;
160
+ flex-direction: column;
161
+ gap: 0.5rem;
162
+ }
163
+
164
+ .radio-option {
165
+ display: flex;
166
+ align-items: center;
167
+ gap: 0.5rem;
168
+ cursor: pointer;
169
+ font-size: 0.938rem;
170
+ color: var(--c-basic-700);
171
+ }
172
+
173
+ .radio-option:hover {
174
+ color: var(--c-basic-800);
175
+ }
176
+
177
+ .radio-label {
178
+ user-select: none;
179
+ }
180
+
181
+ input[type='radio'] {
182
+ cursor: pointer;
183
+ margin: 0;
184
+ width: 1.125rem;
185
+ height: 1.125rem;
186
+ border: 1px solid var(--c-primary);
187
+ background-color: var(--c-basic-0);
188
+ }
189
+
190
+ @supports (appearance: none) {
191
+ input[type='radio'] {
192
+ appearance: none;
193
+ position: relative;
194
+ border-radius: 50%;
195
+ transition: all 0.15s ease;
196
+ }
197
+
198
+ input[type='radio']:hover {
199
+ border-color: var(--c-primary);
200
+ }
201
+
202
+ input[type='radio']:checked {
203
+ border-color: var(--c-primary);
204
+ background: var(--c-primary);
205
+ }
206
+
207
+ input[type='radio']:checked::before {
208
+ content: '';
209
+ position: absolute;
210
+ top: 50%;
211
+ left: 50%;
212
+ transform: translate(-50%, -50%);
213
+ width: 0.375rem;
214
+ height: 0.375rem;
215
+ border-radius: 50%;
216
+ background-color: var(--c-basic-0);
217
+ }
218
+
219
+ input[type='radio']:focus-visible {
220
+ outline: none;
221
+ box-shadow: 0 0 0 1px var(--c-primary);
222
+ }
223
+ }
224
+
225
+ .radio-unset,
226
+ .radio-btn-unset {
227
+ margin: 0.75rem 0 0;
228
+ }
229
+
230
+ .radio-unset {
231
+ line-height: 1.25rem;
232
+ }
233
+ </style>
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ let {
3
+ value,
4
+ onChange,
5
+ control = 'text',
6
+ numberOfRows = 4,
7
+ }: {
8
+ value: string
9
+ onChange: (value: string) => void
10
+ control?: 'text' | 'textarea'
11
+ numberOfRows?: number
12
+ } = $props()
13
+
14
+ let isUnset = $derived(!value || value === undefined || value === null)
15
+ </script>
16
+
17
+ <div class="row">
18
+ <div class="container">
19
+ {#if control === 'textarea'}
20
+ <textarea
21
+ class="input textarea"
22
+ rows={numberOfRows}
23
+ value={String(value ?? '')}
24
+ oninput={(e) => {
25
+ onChange(String((e.currentTarget as HTMLTextAreaElement).value ?? ''))
26
+ }}
27
+ ></textarea>
28
+ {:else}
29
+ <input
30
+ class="input"
31
+ type="text"
32
+ value={String(value ?? '')}
33
+ oninput={(e) => {
34
+ onChange(String((e.currentTarget as HTMLInputElement).value ?? ''))
35
+ }}
36
+ />
37
+ {/if}
38
+ </div>
39
+ <div class="unset-area">
40
+ {#if isUnset}
41
+ <span class="unset-info">is not set</span>
42
+ {:else}
43
+ <button class="btn_unset" onclick={() => onChange(undefined as any)}>
44
+ <svg
45
+ class="close"
46
+ xmlns="http://www.w3.org/2000/svg"
47
+ width="12"
48
+ height="12"
49
+ viewBox="0 0 24 24"
50
+ fill="none"
51
+ stroke="currentColor"
52
+ stroke-width="2"
53
+ stroke-linecap="round"
54
+ stroke-linejoin="round"
55
+ >
56
+ <path d="M18 6L6 18M6 6l12 12" />
57
+ </svg>
58
+ unset
59
+ </button>
60
+ {/if}
61
+ </div>
62
+ </div>
63
+
64
+ <style lang="scss">
65
+ @use './input.scss';
66
+ @use './button_unset.scss';
67
+
68
+ .row {
69
+ display: flex;
70
+ gap: 0.5rem;
71
+ align-items: center;
72
+ }
73
+
74
+ .container {
75
+ flex: 1;
76
+ display: block;
77
+ }
78
+
79
+ .textarea {
80
+ resize: vertical;
81
+ height: auto;
82
+ line-height: 1.4;
83
+ padding: 0.375rem 0.5rem;
84
+ }
85
+ </style>
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ let {
3
+ value,
4
+ onChange,
5
+ }: { value: string; onChange: (value: string) => void } = $props()
6
+
7
+ let isUnset = $derived(!value || value === undefined || value === null)
8
+ </script>
9
+
10
+ <div class="row">
11
+ <input
12
+ class="input"
13
+ type="time"
14
+ value={value ?? ''}
15
+ onchange={(e) => {
16
+ const newValue = (e.currentTarget as HTMLInputElement).value
17
+ onChange(newValue || (undefined as any))
18
+ }}
19
+ />
20
+ <div class="unset-area">
21
+ {#if isUnset}
22
+ <span class="unset-info">is not set</span>
23
+ {:else}
24
+ <button class="btn_unset" onclick={() => onChange(undefined as any)}>
25
+ <svg
26
+ class="close"
27
+ xmlns="http://www.w3.org/2000/svg"
28
+ width="12"
29
+ height="12"
30
+ viewBox="0 0 24 24"
31
+ fill="none"
32
+ stroke="currentColor"
33
+ stroke-width="2"
34
+ stroke-linecap="round"
35
+ stroke-linejoin="round"
36
+ >
37
+ <path d="M18 6L6 18M6 6l12 12" />
38
+ </svg>
39
+ unset
40
+ </button>
41
+ {/if}
42
+ </div>
43
+ </div>
44
+
45
+ <style lang="scss">
46
+ @use './input.scss';
47
+ @use './button_unset.scss';
48
+
49
+ .row {
50
+ display: flex;
51
+ gap: 0.5rem;
52
+ align-items: center;
53
+ }
54
+
55
+ input[type='time'] {
56
+ cursor: pointer;
57
+ flex: 1;
58
+ }
59
+
60
+ input[type='time']::-webkit-calendar-picker-indicator {
61
+ cursor: pointer;
62
+ color: var(--c-primary);
63
+ }
64
+ </style>