gardenjs 1.6.9 → 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 (34) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/frame-BOcgZVOc.js +9 -0
  3. package/dist/assets/index-BDdBNVTh.css +19 -0
  4. package/dist/assets/index-DFG9gkdS.js +46 -0
  5. package/dist/assets/props-COK33XqS.js +2 -0
  6. package/dist/frame.html +2 -2
  7. package/dist/index.html +3 -3
  8. package/package.json +1 -1
  9. package/src/client/GardenApp.svelte +105 -92
  10. package/src/client/GardenFrame.svelte +21 -5
  11. package/src/client/components/panes/HorizontalSplitPane.svelte +27 -23
  12. package/src/client/components/panes/VerticalSplitPane.svelte +32 -36
  13. package/src/client/components/sidebar/Sidebar.svelte +6 -10
  14. package/src/client/components/stage/Stage.svelte +47 -27
  15. package/src/client/components/stage/panel/ParamsPane.svelte +11 -13
  16. package/src/client/components/stage/panel/controls/ArrayControl.svelte +3 -3
  17. package/src/client/components/stage/panel/controls/BooleanControl.svelte +24 -22
  18. package/src/client/components/stage/panel/controls/ColorPickerControl.svelte +21 -19
  19. package/src/client/components/stage/panel/controls/DateControl.svelte +23 -21
  20. package/src/client/components/stage/panel/controls/DatetimeControl.svelte +23 -21
  21. package/src/client/components/stage/panel/controls/JsonControl.svelte +3 -3
  22. package/src/client/components/stage/panel/controls/MultiselectControl.svelte +43 -39
  23. package/src/client/components/stage/panel/controls/RangeControl.svelte +23 -21
  24. package/src/client/components/stage/panel/controls/SelectControl.svelte +50 -46
  25. package/src/client/components/stage/panel/controls/TextInputControl.svelte +64 -23
  26. package/src/client/components/stage/panel/controls/TimeControl.svelte +23 -21
  27. package/src/client/components/stage/panel/controls/button_unset.scss +12 -2
  28. package/src/client/logic/localStore.js +23 -0
  29. package/src/client/logic/sidebar.svelte.js +55 -0
  30. package/src/client/logic/stage.js +2 -63
  31. package/dist/assets/frame-HIvQhXZT.js +0 -9
  32. package/dist/assets/index-B_e-ixXE.css +0 -19
  33. package/dist/assets/index-CePXpz0w.js +0 -46
  34. package/dist/assets/props-C0iXbu7F.js +0 -2
@@ -108,13 +108,10 @@
108
108
 
109
109
  <style>
110
110
  .sidebar_container {
111
- --w-sidebar: 260px;
111
+ margin-right: 0.125rem;
112
112
  display: flex;
113
113
  flex-direction: column;
114
114
  position: relative;
115
- margin: 0.375rem 0;
116
- width: 0;
117
- max-width: var(--w-sidebar);
118
115
  height: calc(100vh - 0.75rem);
119
116
  background-color: var(--c-sidebar-bg);
120
117
  border-radius: 0.625rem;
@@ -122,8 +119,7 @@
122
119
  overflow: hidden;
123
120
  }
124
121
  .show-sidebar {
125
- margin: 0.375rem 0.375rem 0.375rem 0;
126
- width: var(--w-sidebar);
122
+ margin: 0.375rem 0.125rem 0.375rem 0;
127
123
  box-sizing: border-box;
128
124
  }
129
125
  .project-identifier {
@@ -135,10 +131,10 @@
135
131
  align-items: center;
136
132
  padding: 0.25rem 0.688rem;
137
133
  margin: 0 0 0.125rem;
138
- width: var(--w-sidebar);
134
+ width: 100%;
139
135
  height: 2.25rem;
140
136
  background-color: var(--c-sidebar);
141
- inline-size: var(--w-sidebar);
137
+ inline-size: 100%;
142
138
  overflow: hidden;
143
139
  white-space: nowrap;
144
140
  font-size: 1.25rem;
@@ -188,7 +184,7 @@
188
184
  box-shadow: 0 0 0 1px var(--c-primary);
189
185
  }
190
186
  .filter_zero-results {
191
- width: var(--w-sidebar);
187
+ width: 100%;
192
188
  padding: 0.5rem 0.688rem 0.375rem 0.688rem;
193
189
  text-transform: initial;
194
190
  font-size: 0.813rem;
@@ -214,7 +210,7 @@
214
210
  .controls {
215
211
  display: block;
216
212
  flex-shrink: 0;
217
- width: var(--w-sidebar);
213
+ width: 100%;
218
214
  height: 103px;
219
215
  bottom: 0.375rem;
220
216
  padding: 0;
@@ -38,11 +38,6 @@
38
38
  let myframeready = $state()
39
39
  let myframe = $state()
40
40
 
41
- const params = $derived.by(() => {
42
- if (das?.params && Array.isArray(das.params)) return das.params
43
- return []
44
- })
45
-
46
41
  const selectedExampleObj = $derived.by(() => {
47
42
  if (!das?.examples?.length) return {}
48
43
  return das.examples.find((ex) => ex.title === selectedExample) ?? {}
@@ -54,30 +49,58 @@
54
49
  return {}
55
50
  })
56
51
 
57
- const computeInitialParamValues = (params, input) => {
58
- const next = {}
59
- for (const p of params ?? []) {
60
- if (!p?.name) continue
61
- if (Object.prototype.hasOwnProperty.call(input, p.name)) {
62
- next[p.name] = input[p.name]
63
- }
52
+ function getType(value) {
53
+ if (Array.isArray(value)) {
54
+ return 'array'
55
+ }
56
+ if (value instanceof Date) {
57
+ return 'date'
64
58
  }
65
- return next
59
+ return typeof value
66
60
  }
67
61
 
68
- let paramValues = $derived(
69
- computeInitialParamValues(params, selectedExampleInput)
70
- )
62
+ function capitalize(str) {
63
+ if (str.length > 1) {
64
+ return str.charAt(0).toUpperCase() + str.substring(1)
65
+ }
66
+ return str
67
+ }
71
68
 
72
- const mergedArgs = $derived.by(() => {
73
- return { ...paramValues }
69
+ const params = $derived.by(() => {
70
+ const configuredParams = das?.params ?? []
71
+ const exampleParams = Object.entries(selectedExampleInput).reduce(
72
+ (acc, [name, value]) => {
73
+ let configuredParam = configuredParams.find((p) => p.name === name)
74
+ if (configuredParam) {
75
+ acc.push(configuredParam)
76
+ } else {
77
+ acc.push({ name, type: getType(value), label: capitalize(name) })
78
+ }
79
+ return acc
80
+ },
81
+ []
82
+ )
83
+
84
+ return [
85
+ ...exampleParams,
86
+ ...configuredParams
87
+ .filter((configuredParam) =>
88
+ exampleParams.every(
89
+ (exampleParam) => exampleParam.name !== configuredParam.name
90
+ )
91
+ )
92
+ .map((p) => ({ ...p, value: undefined })),
93
+ ]
74
94
  })
75
95
 
76
- const argsForMessage = $derived.by(() => {
96
+ let paramValues = $derived(structuredClone(selectedExampleInput))
97
+
98
+ const paramValuesForPostMessage = $derived.by(() => {
77
99
  try {
78
- return JSON.parse(JSON.stringify(mergedArgs))
79
- } catch {
80
- return {}
100
+ return JSON.parse(JSON.stringify(paramValues))
101
+ } catch (e) {
102
+ console.err(e)
103
+ return null
81
104
  }
82
105
  })
83
106
 
@@ -133,10 +156,7 @@
133
156
  paramValues = { ...paramValues, [prop]: value }
134
157
  },
135
158
  onReset: () => {
136
- paramValues = computeInitialParamValues(
137
- params,
138
- selectedExampleInput
139
- )
159
+ paramValues = { ...selectedExampleInput }
140
160
  },
141
161
  selected: selectedExample,
142
162
  examples: das.examples.map((ex) => ex.title),
@@ -181,7 +201,7 @@
181
201
  showInspector,
182
202
  showGrid,
183
203
  gridSettings,
184
- args: argsForMessage,
204
+ paramValues: paramValuesForPostMessage,
185
205
  },
186
206
  window.location
187
207
  )
@@ -24,7 +24,7 @@
24
24
  'datetime',
25
25
  'array',
26
26
  'object',
27
- 'dropdown',
27
+ 'multiselect',
28
28
  'checkboxes',
29
29
  'toggle',
30
30
  'radio',
@@ -46,14 +46,14 @@
46
46
  return 'checkbox'
47
47
  case 'number':
48
48
  return 'number'
49
- case 'string':
50
- return 'text'
51
49
  case 'date':
52
50
  return 'date'
53
51
  case 'array':
54
52
  case 'object':
55
- default:
56
53
  return 'json'
54
+ case 'string':
55
+ default:
56
+ return 'text'
57
57
  }
58
58
  }
59
59
  </script>
@@ -82,7 +82,7 @@
82
82
  {#if controlType === 'checkbox' || controlType === 'toggle'}
83
83
  <BooleanControl
84
84
  value={values?.[param.name] ?? undefined}
85
- variant={controlType}
85
+ control={controlType}
86
86
  onChange={(v) => onChange?.(param.name, v)}
87
87
  />
88
88
  {:else if controlType === 'number'}
@@ -122,11 +122,11 @@
122
122
  schema={param.schema ?? {}}
123
123
  onChange={(v) => onChange?.(param.name, v)}
124
124
  />
125
- {:else if controlType === 'dropdown' || controlType === 'checkboxes'}
125
+ {:else if controlType === 'multiselect' || controlType === 'checkboxes'}
126
126
  <MultiselectControl
127
127
  value={values?.[param.name] ?? []}
128
128
  options={param.options ?? []}
129
- variant={controlType}
129
+ control={controlType}
130
130
  onChange={(v) => onChange?.(param.name, v)}
131
131
  />
132
132
  {:else if controlType === 'range'}
@@ -137,25 +137,23 @@
137
137
  step={param.step}
138
138
  onChange={(v) => onChange?.(param.name, v)}
139
139
  />
140
- {:else if controlType === 'dropdown' || controlType === 'radio'}
140
+ {:else if controlType === 'select' || controlType === 'radio'}
141
141
  <SelectControl
142
142
  value={values?.[param.name] ?? undefined}
143
143
  options={param.options ?? []}
144
- variant={controlType}
144
+ control={controlType}
145
145
  onChange={(v) => onChange?.(param.name, v)}
146
146
  />
147
147
  {:else if controlType === 'textarea' || controlType === 'text'}
148
148
  <TextInputControl
149
149
  value={values?.[param.name] ?? ''}
150
- variant={controlType}
151
- rows={param.rows}
150
+ control={controlType}
151
+ numberOfRows={param.numberOfRows}
152
152
  onChange={(v) => onChange?.(param.name, v)}
153
153
  />
154
154
  {:else}
155
155
  <JsonControl
156
156
  value={values?.[param.name] ?? ''}
157
- variant={controlType}
158
- rows={param.rows}
159
157
  onChange={(v) => onChange?.(param.name, v)}
160
158
  />
161
159
  {/if}
@@ -116,7 +116,7 @@
116
116
  value={item[key] ?? []}
117
117
  onChange={(v) => updateItemProperty(index, key, v)}
118
118
  options={config.options ?? []}
119
- variant={config.variant ?? 'dropdown'}
119
+ control={config.control ?? 'dropdown'}
120
120
  />
121
121
  {:else if fieldType === 'number'}
122
122
  <NumberControl
@@ -151,8 +151,8 @@
151
151
  {:else}
152
152
  <TextInputControl
153
153
  value={item[key] ?? ''}
154
- variant={config.variant ?? 'text'}
155
- rows={config.rows}
154
+ control={config.control ?? 'text'}
155
+ numberOfRows={config.numberOfRows}
156
156
  onChange={(v) => updateItemProperty(index, key, v)}
157
157
  />
158
158
  {/if}
@@ -2,41 +2,43 @@
2
2
  let {
3
3
  value,
4
4
  onChange,
5
- variant = 'checkbox',
5
+ control = 'checkbox',
6
6
  }: {
7
7
  value: boolean
8
8
  onChange: (value: boolean) => void
9
- variant?: 'checkbox' | 'toggle'
9
+ control?: 'checkbox' | 'toggle'
10
10
  } = $props()
11
11
  </script>
12
12
 
13
13
  <input
14
14
  type="checkbox"
15
- class={variant}
15
+ class={control}
16
16
  checked={value === true}
17
17
  onchange={(e) => {
18
18
  onChange((e.currentTarget as HTMLInputElement).checked)
19
19
  }}
20
20
  />
21
- {#if value === undefined || value === null}
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"><path d="M18 6L6 18M6 6l12 12" /></svg
36
- >
37
- unset
38
- </button>
39
- {/if}
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>
40
42
 
41
43
  <style lang="scss">
42
44
  @use './button_unset.scss';
@@ -78,25 +78,27 @@
78
78
  value={value || ''}
79
79
  oninput={handleTextInput}
80
80
  />
81
- {#if isUnset}
82
- <span class="unset-info">is not set</span>
83
- {:else}
84
- <button class="btn_unset" onclick={() => onChange(undefined)}>
85
- <svg
86
- class="close"
87
- xmlns="http://www.w3.org/2000/svg"
88
- width="12"
89
- height="12"
90
- viewBox="0 0 24 24"
91
- fill="none"
92
- stroke="currentColor"
93
- stroke-width="2"
94
- stroke-linecap="round"
95
- stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12" /></svg
96
- >
97
- unset
98
- </button>
99
- {/if}
81
+ <div class="unset-area">
82
+ {#if isUnset}
83
+ <span class="unset-info">is not set</span>
84
+ {:else}
85
+ <button class="btn_unset" onclick={() => onChange(undefined)}>
86
+ <svg
87
+ class="close"
88
+ xmlns="http://www.w3.org/2000/svg"
89
+ width="12"
90
+ height="12"
91
+ viewBox="0 0 24 24"
92
+ fill="none"
93
+ stroke="currentColor"
94
+ stroke-width="2"
95
+ stroke-linecap="round"
96
+ stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12" /></svg
97
+ >
98
+ unset
99
+ </button>
100
+ {/if}
101
+ </div>
100
102
  </div>
101
103
 
102
104
  <style>
@@ -17,27 +17,29 @@
17
17
  onChange(newValue || (undefined as any))
18
18
  }}
19
19
  />
20
- {#if isUnset}
21
- <span class="unset-info">is not set</span>
22
- {:else}
23
- <button class="btn_unset" onclick={() => onChange(undefined as any)}>
24
- <svg
25
- class="close"
26
- xmlns="http://www.w3.org/2000/svg"
27
- width="12"
28
- height="12"
29
- viewBox="0 0 24 24"
30
- fill="none"
31
- stroke="currentColor"
32
- stroke-width="2"
33
- stroke-linecap="round"
34
- stroke-linejoin="round"
35
- >
36
- <path d="M18 6L6 18M6 6l12 12" />
37
- </svg>
38
- unset
39
- </button>
40
- {/if}
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>
41
43
  </div>
42
44
 
43
45
  <style lang="scss">
@@ -17,27 +17,29 @@
17
17
  onChange(newValue || (undefined as any))
18
18
  }}
19
19
  />
20
- {#if isUnset}
21
- <span class="unset-info">is not set</span>
22
- {:else}
23
- <button class="btn_unset" onclick={() => onChange(undefined as any)}>
24
- <svg
25
- class="close"
26
- xmlns="http://www.w3.org/2000/svg"
27
- width="12"
28
- height="12"
29
- viewBox="0 0 24 24"
30
- fill="none"
31
- stroke="currentColor"
32
- stroke-width="2"
33
- stroke-linecap="round"
34
- stroke-linejoin="round"
35
- >
36
- <path d="M18 6L6 18M6 6l12 12" />
37
- </svg>
38
- unset
39
- </button>
40
- {/if}
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>
41
43
  </div>
42
44
 
43
45
  <style lang="scss">
@@ -10,10 +10,10 @@
10
10
 
11
11
  <textarea
12
12
  class="input textarea"
13
- rows="5"
14
- value={String(value ?? '')}
13
+ rows="10"
14
+ value={JSON.stringify(value ?? '', null, 2)}
15
15
  onchange={(e) => {
16
- onChange(JSON.parse((e.currentTarget as HTMLTextAreaElement).value ?? ''))
16
+ onChange(JSON.parse(e.currentTarget.value ?? ''))
17
17
  }}
18
18
  ></textarea>
19
19
 
@@ -3,12 +3,12 @@
3
3
  value,
4
4
  onChange,
5
5
  options = [],
6
- variant = 'dropdown',
6
+ control = 'dropdown',
7
7
  }: {
8
8
  value: string[]
9
9
  onChange: (value: string[]) => void
10
10
  options?: string[] | { label: string; value: string }[]
11
- variant?: 'dropdown' | 'checkboxes'
11
+ control?: 'dropdown' | 'checkboxes'
12
12
  } = $props()
13
13
 
14
14
  let isUnset = $derived(
@@ -36,7 +36,7 @@
36
36
  }
37
37
  </script>
38
38
 
39
- {#if variant === 'dropdown'}
39
+ {#if control === 'dropdown'}
40
40
  <div class="row">
41
41
  <div class="select-wrapper">
42
42
  <button
@@ -82,8 +82,46 @@
82
82
  </div>
83
83
  {/if}
84
84
  </div>
85
+ <div class="unset-area">
86
+ {#if !isUnset}
87
+ <button class="btn_unset" onclick={handleUnset}>
88
+ <svg
89
+ class="close"
90
+ xmlns="http://www.w3.org/2000/svg"
91
+ width="12"
92
+ height="12"
93
+ viewBox="0 0 24 24"
94
+ fill="none"
95
+ stroke="currentColor"
96
+ stroke-width="2"
97
+ stroke-linecap="round"
98
+ stroke-linejoin="round"
99
+ >
100
+ <path d="M18 6L6 18M6 6l12 12" />
101
+ </svg>
102
+ unset
103
+ </button>
104
+ {:else}
105
+ <span class="unset-info">is not set</span>
106
+ {/if}
107
+ </div>
108
+ </div>
109
+ {:else if control === 'checkboxes'}
110
+ <div class="checkboxes-container">
111
+ {#each normalizedOptions as option (option.value)}
112
+ <label class="checkbox-item">
113
+ <input
114
+ type="checkbox"
115
+ checked={value?.includes(option.value) || false}
116
+ onchange={() => toggleOption(option.value)}
117
+ />
118
+ <span>{option.label}</span>
119
+ </label>
120
+ {/each}
121
+ </div>
122
+ <div class="unset-area">
85
123
  {#if !isUnset}
86
- <button class="btn_unset" onclick={handleUnset}>
124
+ <button class="btn_unset checkboxes-btn-unset" onclick={handleUnset}>
87
125
  <svg
88
126
  class="close"
89
127
  xmlns="http://www.w3.org/2000/svg"
@@ -101,43 +139,9 @@
101
139
  unset
102
140
  </button>
103
141
  {:else}
104
- <span class="unset-info">is not set</span>
142
+ <div class="unset-info checkboxes-unset">is not set</div>
105
143
  {/if}
106
144
  </div>
107
- {:else if variant === 'checkboxes'}
108
- <div class="checkboxes-container">
109
- {#each normalizedOptions as option (option.value)}
110
- <label class="checkbox-item">
111
- <input
112
- type="checkbox"
113
- checked={value?.includes(option.value) || false}
114
- onchange={() => toggleOption(option.value)}
115
- />
116
- <span>{option.label}</span>
117
- </label>
118
- {/each}
119
- </div>
120
- {#if !isUnset}
121
- <button class="btn_unset checkboxes-btn-unset" onclick={handleUnset}>
122
- <svg
123
- class="close"
124
- xmlns="http://www.w3.org/2000/svg"
125
- width="12"
126
- height="12"
127
- viewBox="0 0 24 24"
128
- fill="none"
129
- stroke="currentColor"
130
- stroke-width="2"
131
- stroke-linecap="round"
132
- stroke-linejoin="round"
133
- >
134
- <path d="M18 6L6 18M6 6l12 12" />
135
- </svg>
136
- unset
137
- </button>
138
- {:else}
139
- <div class="unset-info checkboxes-unset">is not set</div>
140
- {/if}
141
145
  {/if}
142
146
 
143
147
  <style lang="scss">
@@ -32,27 +32,29 @@
32
32
  />
33
33
  </div>
34
34
  <div class="value">{isUnset ? '-' : value}</div>
35
- {#if !isUnset}
36
- <button class="btn_unset" onclick={() => onChange(null)}>
37
- <svg
38
- class="close"
39
- xmlns="http://www.w3.org/2000/svg"
40
- width="12"
41
- height="12"
42
- viewBox="0 0 24 24"
43
- fill="none"
44
- stroke="currentColor"
45
- stroke-width="2"
46
- stroke-linecap="round"
47
- stroke-linejoin="round"
48
- >
49
- <path d="M18 6L6 18M6 6l12 12" />
50
- </svg>
51
- unset
52
- </button>
53
- {:else}
54
- <span class="unset-info">is not set</span>
55
- {/if}
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>
56
58
  </div>
57
59
 
58
60
  <style lang="scss">