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.
- package/.prettierrc +16 -5
- package/README.md +1 -1
- package/dist/assets/frame-BOcgZVOc.js +9 -0
- package/dist/assets/index-BDdBNVTh.css +19 -0
- package/dist/assets/index-DFG9gkdS.js +46 -0
- package/dist/assets/props-COK33XqS.js +2 -0
- package/dist/frame.html +2 -2
- package/dist/index.html +3 -3
- package/eslint.config.js +14 -1
- package/package.json +3 -1
- package/src/client/GardenApp.svelte +105 -92
- package/src/client/GardenFrame.svelte +25 -3
- package/src/client/assets/scss/main.scss +0 -1
- package/src/client/components/panes/HorizontalSplitPane.svelte +28 -24
- package/src/client/components/panes/VerticalSplitPane.svelte +118 -0
- package/src/client/components/sidebar/Sidebar.svelte +8 -11
- package/src/client/components/stage/Stage.svelte +76 -1
- package/src/client/components/stage/panel/PanelCode.svelte +7 -1
- package/src/client/components/stage/panel/PanelComponent.svelte +0 -1
- package/src/client/components/stage/panel/PanelDescription.svelte +6 -0
- package/src/client/components/stage/panel/PanelExamplesNav.svelte +38 -12
- package/src/client/components/stage/panel/ParamsPane.svelte +201 -0
- package/src/client/components/stage/panel/controls/ArrayControl.svelte +242 -0
- package/src/client/components/stage/panel/controls/BooleanControl.svelte +142 -0
- package/src/client/components/stage/panel/controls/ColorPickerControl.svelte +185 -0
- package/src/client/components/stage/panel/controls/DateControl.svelte +64 -0
- package/src/client/components/stage/panel/controls/DatetimeControl.svelte +64 -0
- package/src/client/components/stage/panel/controls/JsonControl.svelte +29 -0
- package/src/client/components/stage/panel/controls/MultiselectControl.svelte +354 -0
- package/src/client/components/stage/panel/controls/NumberControl.svelte +31 -0
- package/src/client/components/stage/panel/controls/ObjectControl.svelte +148 -0
- package/src/client/components/stage/panel/controls/RangeControl.svelte +156 -0
- package/src/client/components/stage/panel/controls/SelectControl.svelte +233 -0
- package/src/client/components/stage/panel/controls/TextInputControl.svelte +85 -0
- package/src/client/components/stage/panel/controls/TimeControl.svelte +64 -0
- package/src/client/components/stage/panel/controls/button.scss +15 -0
- package/src/client/components/stage/panel/controls/button_unset.scss +35 -0
- package/src/client/components/stage/panel/controls/input.scss +15 -0
- package/src/client/components/topbar/Topbar.svelte +8 -0
- package/src/client/logic/localStore.js +23 -0
- package/src/client/logic/sidebar.svelte.js +55 -0
- package/src/client/logic/stage.js +2 -63
- package/dist/assets/frame-B7ff1vAd.js +0 -9
- package/dist/assets/index-DfGHKcnI.css +0 -19
- package/dist/assets/index-WI1a7nYK.js +0 -46
- package/dist/assets/props-DKVIKFn7.js +0 -2
- 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>
|
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
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
|
-
<
|
|
6
|
-
{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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>
|