@picked/svelte 0.0.1-dev.1789541668638 → 0.0.1-next.2
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/dist/component.d.ts +5 -5
- package/dist/component.js +1 -1
- package/dist/context.js +1 -1
- package/dist/form/index.d.ts +15 -3
- package/dist/form/index.js +1 -1
- package/dist/hooks/client.d.ts +1 -1
- package/dist/hooks/client.js +1 -1
- package/dist/hooks/field.d.ts +5 -16
- package/dist/hooks/field.js +1 -1
- package/dist/hooks/index.d.ts +1 -9
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/step.d.ts +15 -93
- package/dist/hooks/step.js +1 -1
- package/dist/hooks/storage.d.ts +2 -2
- package/dist/hooks/storage.js +1 -1
- package/dist/hooks/stores.d.ts +1 -1
- package/dist/hooks/stores.js +1 -1
- package/dist/index.d.ts +1 -8
- package/dist/index.js +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +53 -355
- package/dist/plugins/devtools/PickedLogo.svelte +58 -0
- package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +4 -17
- package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +19 -26
- package/dist/plugins/devtools/components/Tabs.svelte +12 -69
- package/dist/plugins/devtools/experience.d.ts +1 -0
- package/dist/plugins/devtools/experience.js +1 -0
- package/dist/plugins/devtools/index.d.ts +0 -2
- package/dist/plugins/devtools/index.js +1 -1
- package/dist/plugins/devtools/plugin.d.ts +5 -37
- package/dist/plugins/devtools/plugin.js +1 -1
- package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +14 -18
- package/dist/plugins/devtools/screens/experience-instances/ExperienceInstancesListScreen.svelte +3 -1
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +33 -31
- package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte +3 -1
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +103 -293
- package/dist/plugins/devtools/screens/experiences/ExperiencesListScreen.svelte +1 -2
- package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +13 -15
- package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +16 -22
- package/dist/plugins/devtools/screens/history/HistoryEntryListItem.svelte +1 -7
- package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +13 -15
- package/dist/plugins/devtools/screens/project/ProjectScreen.svelte +1 -46
- package/dist/plugins/framework/index.js +1 -1
- package/dist/plugins/framework/plugin.d.ts +4 -4
- package/dist/plugins/framework/plugin.js +1 -1
- package/dist/render/browser.d.ts +2 -35
- package/dist/render/browser.js +1 -1
- package/dist/render/server.d.ts +8 -22
- package/dist/render/server.js +1 -1
- package/dist/router/Step.svelte +4 -6
- package/dist/router/StepRouter.svelte +34 -14
- package/dist/router/context.d.ts +3 -3
- package/dist/router/context.js +1 -1
- package/dist/router/index.js +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +1 -1
- package/dist/utils/array.js +1 -1
- package/dist/vite-env.d.js +1 -0
- package/package.json +33 -106
- package/dist/.tsbuildinfo +0 -1
- package/dist/_virtual/_vite/preload-helper.js +0 -1
- package/dist/components/LazyImage.svelte +0 -192
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.js +0 -1
- package/dist/experience/ExperienceHost.svelte +0 -112
- package/dist/experience/ExperienceSlot.svelte +0 -154
- package/dist/experience/HtmlRender.svelte +0 -52
- package/dist/experience/set-picked-context.d.ts +0 -29
- package/dist/experience/set-picked-context.js +0 -1
- package/dist/form/__tests__/CreateFormHarness.svelte +0 -30
- package/dist/form/create-form.js +0 -1
- package/dist/form/create-form.svelte.d.ts +0 -40
- package/dist/form/create-form.svelte.js +0 -1
- package/dist/form/get-form.d.ts +0 -30
- package/dist/form/get-form.js +0 -1
- package/dist/hooks/action.d.ts +0 -40
- package/dist/hooks/action.js +0 -1
- package/dist/hooks/content.d.ts +0 -18
- package/dist/hooks/content.js +0 -1
- package/dist/hooks/create-query/index.d.ts +0 -145
- package/dist/hooks/create-query/index.js +0 -1
- package/dist/hooks/events.d.ts +0 -2
- package/dist/hooks/events.js +0 -1
- package/dist/hooks/feature.d.ts +0 -47
- package/dist/hooks/feature.js +0 -1
- package/dist/hooks/predicate.d.ts +0 -3
- package/dist/hooks/predicate.js +0 -1
- package/dist/hooks/reference.d.ts +0 -61
- package/dist/hooks/reference.js +0 -1
- package/dist/plugins/devtools/PickedIcon.svelte +0 -19
- package/dist/plugins/devtools/components/AdaptedToolHost.svelte +0 -103
- package/dist/plugins/devtools/customTools.d.ts +0 -27
- package/dist/plugins/devtools/customTools.js +0 -1
- package/dist/plugins/devtools/screens/config/ConfigScreen.svelte +0 -160
- package/dist/plugins/devtools/screens/content/ContentListScreen.svelte +0 -89
- package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +0 -29
- package/dist/plugins/devtools/screens/events/EventListItem.svelte +0 -216
- package/dist/plugins/devtools/screens/events/EventsListScreen.svelte +0 -18
- package/dist/plugins/devtools/screens/events/EventsTable.svelte +0 -76
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +0 -239
- package/dist/plugins/devtools/screens/fields/FieldTableRow.svelte +0 -276
- package/dist/plugins/devtools/screens/fields/FieldsListScreen.svelte +0 -12
- package/dist/plugins/devtools/screens/fields/FieldsTable.svelte +0 -78
- package/dist/plugins/devtools/screens/identity/IdentityScreen.svelte +0 -66
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +0 -280
- package/dist/plugins/devtools/screens/responses/ResponsesListScreen.svelte +0 -12
- package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +0 -88
- package/dist/plugins/devtools/ui/actions/Button.svelte +0 -175
- package/dist/plugins/devtools/ui/actions/CopyButton.svelte +0 -88
- package/dist/plugins/devtools/ui/data/Badge.svelte +0 -62
- package/dist/plugins/devtools/ui/data/CodeBlock.svelte +0 -38
- package/dist/plugins/devtools/ui/data/DataTable.svelte +0 -144
- package/dist/plugins/devtools/ui/data/EmptyState.svelte +0 -26
- package/dist/plugins/devtools/ui/data/ExpandableRow.svelte +0 -66
- package/dist/plugins/devtools/ui/data/VisualizationContainer.svelte +0 -55
- package/dist/plugins/devtools/ui/feedback/Banner.svelte +0 -116
- package/dist/plugins/devtools/ui/feedback/Spinner.svelte +0 -79
- package/dist/plugins/devtools/ui/forms/Checkbox.svelte +0 -139
- package/dist/plugins/devtools/ui/forms/RadioGroup.svelte +0 -218
- package/dist/plugins/devtools/ui/forms/Select.svelte +0 -137
- package/dist/plugins/devtools/ui/forms/Switch.svelte +0 -158
- package/dist/plugins/devtools/ui/forms/TextField.svelte +0 -125
- package/dist/plugins/devtools/ui/index.d.ts +0 -22
- package/dist/plugins/devtools/ui/index.js +0 -1
- package/dist/plugins/devtools/ui/layout/Card.svelte +0 -55
- package/dist/plugins/devtools/ui/layout/Divider.svelte +0 -34
- package/dist/plugins/devtools/ui/layout/Spacer.svelte +0 -11
- package/dist/plugins/devtools/ui/layout/Stack.svelte +0 -129
- package/dist/plugins/devtools/ui/overlays/Tooltip.svelte +0 -90
- package/dist/plugins/devtools/ui/tokens.css +0 -144
- package/dist/plugins/devtools/ui/typography/Heading.svelte +0 -55
- package/dist/plugins/devtools/ui/typography/Text.svelte +0 -93
- package/dist/utils/ErrorBoundary.svelte +0 -27
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Editable table row for a single field response in the devtools responses screen. */
|
|
3
|
-
import type { ResponseListItem, Field, FieldValue, JsonValue } from '@picked/client'
|
|
4
|
-
import { getPicked } from '../../../../hooks'
|
|
5
|
-
import { Button, Text, CodeBlock, Banner, Stack } from '../../ui'
|
|
6
|
-
|
|
7
|
-
export let field: Field
|
|
8
|
-
export let response: ResponseListItem | null
|
|
9
|
-
|
|
10
|
-
const client = getPicked()
|
|
11
|
-
|
|
12
|
-
let isEditing = false
|
|
13
|
-
let editValue = ''
|
|
14
|
-
let editError = ''
|
|
15
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- Set is reassigned for Svelte reactivity via `selectedOptions = selectedOptions`
|
|
16
|
-
let selectedOptions: Set<JsonValue> = new Set()
|
|
17
|
-
|
|
18
|
-
$: hasOptions = Array.isArray(field?.options) && field.options.length > 0
|
|
19
|
-
$: staticOptions = Array.isArray(field?.options) ? field.options : null
|
|
20
|
-
$: currentValue = response?.value ?? null
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Formats a response value for display, handling arrays, objects, and primitives.
|
|
24
|
-
*
|
|
25
|
-
* @param {unknown} value - The response value to format.
|
|
26
|
-
* @returns {string} The display string for the value.
|
|
27
|
-
* @example
|
|
28
|
-
* ```typescript
|
|
29
|
-
* formatValue(['a', 'b']) // 'a, b'
|
|
30
|
-
* formatValue(null) // '-'
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
function formatValue(value: unknown): string {
|
|
34
|
-
if (value === null || value === undefined) {
|
|
35
|
-
return '-'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (Array.isArray(value) && value.every((item) => typeof item === 'string')) {
|
|
39
|
-
return value.join(', ')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (typeof value === 'object') {
|
|
43
|
-
return JSON.stringify(value, null, 2)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return String(value)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Enters edit mode for this field, initializing the edit state from the current value.
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```typescript
|
|
54
|
-
* startEdit() // opens the inline editor
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
function startEdit() {
|
|
58
|
-
isEditing = true
|
|
59
|
-
editError = ''
|
|
60
|
-
|
|
61
|
-
if (hasOptions) {
|
|
62
|
-
selectedOptions = new Set()
|
|
63
|
-
if (Array.isArray(currentValue)) {
|
|
64
|
-
currentValue.forEach((v) => selectedOptions.add(v))
|
|
65
|
-
} else if (currentValue !== null && currentValue !== undefined) {
|
|
66
|
-
selectedOptions.add(currentValue)
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
69
|
-
editValue = formatValue(currentValue)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Cancels the current edit, discarding changes and resetting edit state.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```typescript
|
|
78
|
-
* cancelEdit() // closes editor without saving
|
|
79
|
-
* ```
|
|
80
|
-
*/
|
|
81
|
-
function cancelEdit() {
|
|
82
|
-
isEditing = false
|
|
83
|
-
editValue = ''
|
|
84
|
-
editError = ''
|
|
85
|
-
selectedOptions = new Set()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Toggles selection of a field option, supporting both single and multi-select modes.
|
|
90
|
-
*
|
|
91
|
-
* @param {JsonValue} optionValue - The option value to toggle.
|
|
92
|
-
* @example
|
|
93
|
-
* ```typescript
|
|
94
|
-
* toggleOption('red') // selects or deselects the 'red' option
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
function toggleOption(optionValue: JsonValue) {
|
|
98
|
-
if (field?.multiple) {
|
|
99
|
-
if (selectedOptions.has(optionValue)) {
|
|
100
|
-
selectedOptions.delete(optionValue)
|
|
101
|
-
} else {
|
|
102
|
-
selectedOptions.add(optionValue)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
selectedOptions = selectedOptions
|
|
106
|
-
} else {
|
|
107
|
-
selectedOptions = new Set([optionValue])
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Parses a string input into a typed value based on the field's type.
|
|
113
|
-
*
|
|
114
|
-
* @param {string} input - The raw string input to parse.
|
|
115
|
-
* @returns {FieldValue} The parsed value (null, number, boolean, JSON, or string).
|
|
116
|
-
* @example
|
|
117
|
-
* ```typescript
|
|
118
|
-
* parseValue('42') // 42 (when field.type is 'number')
|
|
119
|
-
* parseValue('true') // true (when field.type is 'boolean')
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
function parseValue(input: string): FieldValue {
|
|
123
|
-
const trimmed = input.trim()
|
|
124
|
-
|
|
125
|
-
if (trimmed === '' || trimmed === '-') {
|
|
126
|
-
return null
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
130
|
-
try {
|
|
131
|
-
return JSON.parse(trimmed)
|
|
132
|
-
} catch {
|
|
133
|
-
throw new Error('Invalid JSON format')
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (field.type === 'boolean') {
|
|
138
|
-
if (trimmed === 'true') return true
|
|
139
|
-
if (trimmed === 'false') return false
|
|
140
|
-
throw new Error('Boolean must be "true" or "false"')
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (field.type === 'number') {
|
|
144
|
-
const num = Number(trimmed)
|
|
145
|
-
if (isNaN(num)) {
|
|
146
|
-
throw new Error('Invalid number format')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return num
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return trimmed
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Commits the edited value via the client responses API and exits edit mode.
|
|
157
|
-
*
|
|
158
|
-
* @example
|
|
159
|
-
* ```typescript
|
|
160
|
-
* await saveEdit() // saves the current edit value
|
|
161
|
-
* ```
|
|
162
|
-
*/
|
|
163
|
-
async function saveEdit() {
|
|
164
|
-
try {
|
|
165
|
-
let newValue: FieldValue
|
|
166
|
-
|
|
167
|
-
if (hasOptions) {
|
|
168
|
-
const values = Array.from(selectedOptions)
|
|
169
|
-
if (field?.multiple) {
|
|
170
|
-
newValue = values
|
|
171
|
-
} else {
|
|
172
|
-
newValue = values.at(0) ?? null
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
newValue = parseValue(editValue)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const result = await client.responses.commit({ [field.key]: newValue })
|
|
179
|
-
|
|
180
|
-
if (result.errors.length > 0) {
|
|
181
|
-
editError = result.errors[0].userMessage
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
isEditing = false
|
|
186
|
-
editError = ''
|
|
187
|
-
selectedOptions = new Set()
|
|
188
|
-
} catch (error) {
|
|
189
|
-
editError = error instanceof Error ? error.message : 'Failed to save'
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Clears the response value for this field via the client responses API.
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* ```typescript
|
|
198
|
-
* handleClear() // removes the response for this field
|
|
199
|
-
* ```
|
|
200
|
-
*/
|
|
201
|
-
function handleClear() {
|
|
202
|
-
client.responses.clear(field.key)
|
|
203
|
-
}
|
|
204
|
-
</script>
|
|
205
|
-
|
|
206
|
-
<tr>
|
|
207
|
-
<td>
|
|
208
|
-
<CodeBlock code={field.key} inline />
|
|
209
|
-
</td>
|
|
210
|
-
<td>
|
|
211
|
-
<Text size="small" weight="semibold">{field.label}</Text>
|
|
212
|
-
</td>
|
|
213
|
-
<td>
|
|
214
|
-
{#if isEditing}
|
|
215
|
-
<Stack direction="vertical" gap="small">
|
|
216
|
-
{#if hasOptions && staticOptions}
|
|
217
|
-
<Stack direction="vertical" gap="small">
|
|
218
|
-
{#each staticOptions as option (option.id)}
|
|
219
|
-
<label class="option-label">
|
|
220
|
-
{#if field.multiple}
|
|
221
|
-
<input
|
|
222
|
-
type="checkbox"
|
|
223
|
-
checked={selectedOptions.has(option.value)}
|
|
224
|
-
on:change={() => toggleOption(option.value)}
|
|
225
|
-
/>
|
|
226
|
-
{:else}
|
|
227
|
-
<input
|
|
228
|
-
type="radio"
|
|
229
|
-
name="field-{field.id}"
|
|
230
|
-
checked={selectedOptions.has(option.value)}
|
|
231
|
-
on:change={() => toggleOption(option.value)}
|
|
232
|
-
/>
|
|
233
|
-
{/if}
|
|
234
|
-
<Text size="caption" inline>
|
|
235
|
-
{option.label}
|
|
236
|
-
{#if option.description}
|
|
237
|
-
<Text size="caption" color="subdued" inline>({option.description})</Text>
|
|
238
|
-
{/if}
|
|
239
|
-
</Text>
|
|
240
|
-
</label>
|
|
241
|
-
{/each}
|
|
242
|
-
</Stack>
|
|
243
|
-
{:else}
|
|
244
|
-
<textarea
|
|
245
|
-
bind:value={editValue}
|
|
246
|
-
rows="3"
|
|
247
|
-
placeholder="Enter {field.type} value..."
|
|
248
|
-
></textarea>
|
|
249
|
-
{/if}
|
|
250
|
-
{#if editError}
|
|
251
|
-
<Banner status="error">{editError}</Banner>
|
|
252
|
-
{/if}
|
|
253
|
-
</Stack>
|
|
254
|
-
{:else if response === null}
|
|
255
|
-
<Text size="caption" color="subdued">No value set</Text>
|
|
256
|
-
{:else}
|
|
257
|
-
<CodeBlock code={formatValue(currentValue)} />
|
|
258
|
-
{/if}
|
|
259
|
-
</td>
|
|
260
|
-
<td>
|
|
261
|
-
<CodeBlock code="{field.type}{field.multiple ? '[]' : ''}" inline />
|
|
262
|
-
</td>
|
|
263
|
-
<td>
|
|
264
|
-
{#if isEditing}
|
|
265
|
-
<Stack direction="horizontal" gap="none" align="end">
|
|
266
|
-
<Button variant="primary" size="small" onclick={saveEdit}>Save</Button>
|
|
267
|
-
<Button variant="destructive" size="small" onclick={cancelEdit}>Cancel</Button>
|
|
268
|
-
</Stack>
|
|
269
|
-
{:else}
|
|
270
|
-
<Stack direction="horizontal" gap="none" align="end">
|
|
271
|
-
<Button variant="secondary" size="small" onclick={startEdit}>
|
|
272
|
-
{response === null ? 'Set' : 'Edit'}
|
|
273
|
-
</Button>
|
|
274
|
-
{#if response !== null}
|
|
275
|
-
<Button variant="destructive" size="small" onclick={handleClear}>Clear</Button>
|
|
276
|
-
{/if}
|
|
277
|
-
</Stack>
|
|
278
|
-
{/if}
|
|
279
|
-
</td>
|
|
280
|
-
</tr>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getPicked } from '../../../../hooks'
|
|
3
|
-
import ResponsesTable from './ResponsesTable.svelte'
|
|
4
|
-
|
|
5
|
-
const client = getPicked()
|
|
6
|
-
|
|
7
|
-
$: responsesStore = client.responses.listStore
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<div>
|
|
11
|
-
<ResponsesTable responses={$responsesStore} />
|
|
12
|
-
</div>
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Table displaying all field responses with filtering and clear-all functionality in the devtools panel. */
|
|
3
|
-
import type { ResponseListItem } from '@picked/client'
|
|
4
|
-
import { getPicked } from '../../../../hooks'
|
|
5
|
-
import { Card, DataTable, EmptyState, Button, RadioGroup, Stack } from '../../ui'
|
|
6
|
-
import ResponseTableRow from './ResponseTableRow.svelte'
|
|
7
|
-
|
|
8
|
-
export let responses: ResponseListItem[] = []
|
|
9
|
-
|
|
10
|
-
const client = getPicked()
|
|
11
|
-
|
|
12
|
-
let filter = 'set'
|
|
13
|
-
|
|
14
|
-
const fieldsStore = client.fields.listStore
|
|
15
|
-
$: allFields = $fieldsStore ?? []
|
|
16
|
-
|
|
17
|
-
$: responseMap = new Map(responses.map((r) => [r.fieldKey, r]))
|
|
18
|
-
|
|
19
|
-
$: allFieldRows = allFields.map((field) => {
|
|
20
|
-
const response = responseMap.get(field.key)
|
|
21
|
-
return {
|
|
22
|
-
field,
|
|
23
|
-
response: response || null,
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
$: fieldRows = allFieldRows.filter((row) => {
|
|
28
|
-
if (filter === 'set') return row.response !== null
|
|
29
|
-
if (filter === 'unset') return row.response === null
|
|
30
|
-
return true
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Clears all committed field responses via the client responses API.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```typescript
|
|
38
|
-
* handleClearAll() // removes all responses
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
function handleClearAll() {
|
|
42
|
-
client.responses.clearAll()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const columns = [
|
|
46
|
-
{ key: 'key', header: 'Key' },
|
|
47
|
-
{ key: 'field', header: 'Field' },
|
|
48
|
-
{ key: 'value', header: 'Value' },
|
|
49
|
-
{ key: 'type', header: 'Type' },
|
|
50
|
-
{ key: 'actions', header: 'Actions' },
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
const filterOptions = [
|
|
54
|
-
{ value: 'all', label: 'All' },
|
|
55
|
-
{ value: 'set', label: 'Set (0)' },
|
|
56
|
-
{ value: 'unset', label: 'Unset (0)' },
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
$: filterOptions[1] = { value: 'set', label: `Set (${responses.length})` }
|
|
60
|
-
$: filterOptions[2] = { value: 'unset', label: `Unset (${allFields.length - responses.length})` }
|
|
61
|
-
</script>
|
|
62
|
-
|
|
63
|
-
<Stack padding="medium">
|
|
64
|
-
<Card sectioned>
|
|
65
|
-
<Stack direction="horizontal" gap="small" align="space-between" justify="center" padding="medium">
|
|
66
|
-
<RadioGroup
|
|
67
|
-
label="Show"
|
|
68
|
-
name="filter"
|
|
69
|
-
options={filterOptions}
|
|
70
|
-
value={filter}
|
|
71
|
-
onchange={(val) => { filter = val }}
|
|
72
|
-
orientation="horizontal"
|
|
73
|
-
/>
|
|
74
|
-
{#if responses.length > 0}
|
|
75
|
-
<Button variant="destructive" size="small" onclick={handleClearAll}>Clear All</Button>
|
|
76
|
-
{/if}
|
|
77
|
-
</Stack>
|
|
78
|
-
<DataTable columns={columns} rows={fieldRows}>
|
|
79
|
-
{#snippet row(item)}
|
|
80
|
-
{@const rowData = item}
|
|
81
|
-
<ResponseTableRow field={rowData.field} response={rowData.response} />
|
|
82
|
-
{/snippet}
|
|
83
|
-
{#snippet empty()}
|
|
84
|
-
<EmptyState message="No fields registered yet" />
|
|
85
|
-
{/snippet}
|
|
86
|
-
</DataTable>
|
|
87
|
-
</Card>
|
|
88
|
-
</Stack>
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
@paired-component apps/dashboard/src/shared/ui/actions/button.tsx
|
|
3
|
-
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
-
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
-
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
-
-->
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
import type { Snippet } from 'svelte'
|
|
9
|
-
import Spinner from '../feedback/Spinner.svelte'
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
children: Snippet
|
|
13
|
-
icon?: Snippet
|
|
14
|
-
variant?: 'primary' | 'secondary' | 'destructive' | 'ghost'
|
|
15
|
-
size?: 'small' | 'medium' | 'large'
|
|
16
|
-
disabled?: boolean
|
|
17
|
-
loading?: boolean
|
|
18
|
-
type?: 'button' | 'submit' | 'reset'
|
|
19
|
-
fullWidth?: boolean
|
|
20
|
-
href?: string
|
|
21
|
-
target?: string
|
|
22
|
-
onclick?: (event: MouseEvent) => void
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let {
|
|
26
|
-
children,
|
|
27
|
-
icon,
|
|
28
|
-
variant = 'primary',
|
|
29
|
-
size = 'medium',
|
|
30
|
-
disabled = false,
|
|
31
|
-
loading = false,
|
|
32
|
-
type = 'button',
|
|
33
|
-
fullWidth = false,
|
|
34
|
-
href,
|
|
35
|
-
target,
|
|
36
|
-
onclick,
|
|
37
|
-
}: Props = $props()
|
|
38
|
-
|
|
39
|
-
const isDisabled = $derived(disabled || loading)
|
|
40
|
-
</script>
|
|
41
|
-
|
|
42
|
-
{#if href}
|
|
43
|
-
<a
|
|
44
|
-
class="button variant-{variant} size-{size}"
|
|
45
|
-
class:full-width={fullWidth}
|
|
46
|
-
class:is-disabled={isDisabled}
|
|
47
|
-
{href}
|
|
48
|
-
{target}
|
|
49
|
-
rel={target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
50
|
-
aria-disabled={isDisabled || undefined}
|
|
51
|
-
tabindex={isDisabled ? -1 : undefined}
|
|
52
|
-
onclick={isDisabled
|
|
53
|
-
? (e: MouseEvent) => e.preventDefault()
|
|
54
|
-
: onclick}
|
|
55
|
-
>
|
|
56
|
-
{#if loading}
|
|
57
|
-
<Spinner size="small" color="current" label="Loading" />
|
|
58
|
-
{:else if icon}
|
|
59
|
-
{@render icon()}
|
|
60
|
-
{/if}
|
|
61
|
-
{@render children()}
|
|
62
|
-
</a>
|
|
63
|
-
{:else}
|
|
64
|
-
<button
|
|
65
|
-
class="button variant-{variant} size-{size}"
|
|
66
|
-
class:full-width={fullWidth}
|
|
67
|
-
{type}
|
|
68
|
-
disabled={isDisabled}
|
|
69
|
-
{onclick}
|
|
70
|
-
>
|
|
71
|
-
{#if loading}
|
|
72
|
-
<Spinner size="small" color="current" label="Loading" />
|
|
73
|
-
{:else if icon}
|
|
74
|
-
{@render icon()}
|
|
75
|
-
{/if}
|
|
76
|
-
{@render children()}
|
|
77
|
-
</button>
|
|
78
|
-
{/if}
|
|
79
|
-
|
|
80
|
-
<style>
|
|
81
|
-
.button {
|
|
82
|
-
position: relative;
|
|
83
|
-
display: inline-flex;
|
|
84
|
-
cursor: pointer;
|
|
85
|
-
align-items: center;
|
|
86
|
-
justify-content: center;
|
|
87
|
-
gap: var(--space-2);
|
|
88
|
-
border-radius: var(--radius-md);
|
|
89
|
-
font-weight: var(--font-medium);
|
|
90
|
-
font-family: inherit;
|
|
91
|
-
line-height: 1.5;
|
|
92
|
-
border: none;
|
|
93
|
-
text-decoration: none;
|
|
94
|
-
transition: background-color 0.15s ease, color 0.15s ease;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.button:focus-visible {
|
|
98
|
-
outline: var(--ring-width) solid var(--ring-color);
|
|
99
|
-
outline-offset: var(--ring-offset);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* Variants */
|
|
103
|
-
.variant-primary {
|
|
104
|
-
background-color: var(--color-blue-600);
|
|
105
|
-
color: var(--color-white);
|
|
106
|
-
}
|
|
107
|
-
.variant-primary:hover:not(:disabled):not(.is-disabled) {
|
|
108
|
-
background-color: var(--color-blue-700);
|
|
109
|
-
}
|
|
110
|
-
.variant-primary:active:not(:disabled):not(.is-disabled) {
|
|
111
|
-
background-color: var(--color-blue-800);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.variant-secondary {
|
|
115
|
-
background-color: var(--color-gray-100);
|
|
116
|
-
color: var(--color-gray-900);
|
|
117
|
-
}
|
|
118
|
-
.variant-secondary:hover:not(:disabled):not(.is-disabled) {
|
|
119
|
-
background-color: var(--color-gray-200);
|
|
120
|
-
}
|
|
121
|
-
.variant-secondary:active:not(:disabled):not(.is-disabled) {
|
|
122
|
-
background-color: var(--color-gray-300);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.variant-destructive {
|
|
126
|
-
background-color: var(--color-red-600);
|
|
127
|
-
color: var(--color-white);
|
|
128
|
-
}
|
|
129
|
-
.variant-destructive:hover:not(:disabled):not(.is-disabled) {
|
|
130
|
-
background-color: var(--color-red-700);
|
|
131
|
-
}
|
|
132
|
-
.variant-destructive:active:not(:disabled):not(.is-disabled) {
|
|
133
|
-
background-color: var(--color-red-800);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.variant-ghost {
|
|
137
|
-
background-color: transparent;
|
|
138
|
-
color: var(--color-gray-700);
|
|
139
|
-
}
|
|
140
|
-
.variant-ghost:hover:not(:disabled):not(.is-disabled) {
|
|
141
|
-
background-color: var(--color-gray-100);
|
|
142
|
-
}
|
|
143
|
-
.variant-ghost:active:not(:disabled):not(.is-disabled) {
|
|
144
|
-
background-color: var(--color-gray-200);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/* Sizes */
|
|
148
|
-
.size-small {
|
|
149
|
-
padding: var(--space-1) var(--space-2-5);
|
|
150
|
-
font-size: var(--text-sm);
|
|
151
|
-
}
|
|
152
|
-
.size-medium {
|
|
153
|
-
padding: var(--space-1-5) var(--space-3);
|
|
154
|
-
font-size: var(--text-sm);
|
|
155
|
-
}
|
|
156
|
-
.size-large {
|
|
157
|
-
padding: var(--space-2) var(--space-5);
|
|
158
|
-
font-size: var(--text-base);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/* States */
|
|
162
|
-
.button:disabled,
|
|
163
|
-
.is-disabled {
|
|
164
|
-
cursor: not-allowed;
|
|
165
|
-
opacity: 0.5;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
a.is-disabled {
|
|
169
|
-
pointer-events: none;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
.full-width {
|
|
173
|
-
width: 100%;
|
|
174
|
-
}
|
|
175
|
-
</style>
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
@paired-component apps/dashboard/src/shared/ui/actions/copy-button.tsx
|
|
3
|
-
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
-
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
-
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
-
-->
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
interface Props {
|
|
9
|
-
text: string
|
|
10
|
-
label?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let { text, label = 'Copy to clipboard' }: Props = $props()
|
|
14
|
-
|
|
15
|
-
let copied = $state(false)
|
|
16
|
-
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
17
|
-
|
|
18
|
-
async function handleCopy(): Promise<void> {
|
|
19
|
-
try {
|
|
20
|
-
await navigator.clipboard.writeText(text)
|
|
21
|
-
copied = true
|
|
22
|
-
clearTimeout(timeoutId)
|
|
23
|
-
timeoutId = setTimeout(() => {
|
|
24
|
-
copied = false
|
|
25
|
-
}, 1500)
|
|
26
|
-
} catch {
|
|
27
|
-
// Clipboard API may not be available
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
</script>
|
|
31
|
-
|
|
32
|
-
<button
|
|
33
|
-
class="copy-button"
|
|
34
|
-
onclick={handleCopy}
|
|
35
|
-
aria-label={label}
|
|
36
|
-
>
|
|
37
|
-
{#if copied}
|
|
38
|
-
<svg class="icon icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
39
|
-
<polyline points="20 6 9 17 4 12" />
|
|
40
|
-
</svg>
|
|
41
|
-
<span class="copied-text" aria-live="polite">Copied!</span>
|
|
42
|
-
{:else}
|
|
43
|
-
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
44
|
-
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
|
45
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
46
|
-
</svg>
|
|
47
|
-
{/if}
|
|
48
|
-
</button>
|
|
49
|
-
|
|
50
|
-
<style>
|
|
51
|
-
.copy-button {
|
|
52
|
-
display: inline-flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
gap: var(--space-1);
|
|
55
|
-
border-radius: var(--radius-sm);
|
|
56
|
-
padding: var(--space-1) var(--space-2);
|
|
57
|
-
font-size: var(--text-sm);
|
|
58
|
-
font-family: inherit;
|
|
59
|
-
color: var(--color-gray-600);
|
|
60
|
-
background: none;
|
|
61
|
-
border: none;
|
|
62
|
-
cursor: pointer;
|
|
63
|
-
transition: background-color 0.15s ease, color 0.15s ease;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.copy-button:hover {
|
|
67
|
-
background-color: var(--color-gray-100);
|
|
68
|
-
color: var(--color-gray-900);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.copy-button:focus-visible {
|
|
72
|
-
outline: var(--ring-width) solid var(--ring-color);
|
|
73
|
-
outline-offset: var(--ring-offset);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.icon {
|
|
77
|
-
width: 1rem;
|
|
78
|
-
height: 1rem;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.icon-check {
|
|
82
|
-
color: var(--color-green-700);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.copied-text {
|
|
86
|
-
color: var(--color-green-700);
|
|
87
|
-
}
|
|
88
|
-
</style>
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
@paired-component apps/dashboard/src/shared/ui/data/badge.tsx
|
|
3
|
-
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
-
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
-
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
-
-->
|
|
7
|
-
<script lang="ts">
|
|
8
|
-
import type { Snippet } from 'svelte'
|
|
9
|
-
|
|
10
|
-
interface Props {
|
|
11
|
-
children: Snippet
|
|
12
|
-
status?: 'default' | 'success' | 'warning' | 'error' | 'info'
|
|
13
|
-
size?: 'small' | 'medium'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let { children, status = 'default', size = 'medium' }: Props = $props()
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<span class="badge status-{status} size-{size}">
|
|
20
|
-
{@render children()}
|
|
21
|
-
</span>
|
|
22
|
-
|
|
23
|
-
<style>
|
|
24
|
-
.badge {
|
|
25
|
-
display: inline-flex;
|
|
26
|
-
align-items: center;
|
|
27
|
-
border-radius: var(--radius-full);
|
|
28
|
-
font-weight: var(--font-medium);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/* Sizes */
|
|
32
|
-
.size-small {
|
|
33
|
-
padding: 1px var(--space-1-5);
|
|
34
|
-
font-size: 10px;
|
|
35
|
-
}
|
|
36
|
-
.size-medium {
|
|
37
|
-
padding: var(--space-0-5) var(--space-2);
|
|
38
|
-
font-size: var(--text-xs);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* Status colors */
|
|
42
|
-
.status-default {
|
|
43
|
-
background-color: var(--color-gray-100);
|
|
44
|
-
color: var(--color-gray-700);
|
|
45
|
-
}
|
|
46
|
-
.status-success {
|
|
47
|
-
background-color: var(--color-green-100);
|
|
48
|
-
color: var(--color-green-800);
|
|
49
|
-
}
|
|
50
|
-
.status-warning {
|
|
51
|
-
background-color: var(--color-yellow-100);
|
|
52
|
-
color: var(--color-yellow-800);
|
|
53
|
-
}
|
|
54
|
-
.status-error {
|
|
55
|
-
background-color: #fee2e2;
|
|
56
|
-
color: var(--color-red-800, #991b1b);
|
|
57
|
-
}
|
|
58
|
-
.status-info {
|
|
59
|
-
background-color: #dbeafe;
|
|
60
|
-
color: var(--color-blue-800, #1e40af);
|
|
61
|
-
}
|
|
62
|
-
</style>
|