@picked/svelte 0.0.45-next.1 → 0.0.45-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/plugins/devtools/DevToolsExperience.svelte +61 -44
- package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +2 -1
- package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +22 -20
- package/dist/plugins/devtools/components/Tabs.svelte +20 -7
- package/dist/plugins/devtools/experience.js +1 -1
- package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +18 -18
- package/dist/plugins/devtools/screens/events/EventListItem.svelte +28 -42
- package/dist/plugins/devtools/screens/events/EventsListScreen.svelte +10 -11
- package/dist/plugins/devtools/screens/events/EventsTable.svelte +31 -33
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +13 -16
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +20 -71
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +96 -232
- package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +14 -16
- package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +18 -16
- package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +14 -16
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +25 -63
- package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +43 -64
- package/dist/plugins/devtools/ui/actions/Button.svelte +171 -0
- package/dist/plugins/devtools/ui/actions/CopyButton.svelte +88 -0
- package/dist/plugins/devtools/ui/data/Badge.svelte +62 -0
- package/dist/plugins/devtools/ui/data/CodeBlock.svelte +38 -0
- package/dist/plugins/devtools/ui/data/DataTable.svelte +134 -0
- package/dist/plugins/devtools/ui/data/EmptyState.svelte +26 -0
- package/dist/plugins/devtools/ui/data/ExpandableRow.svelte +66 -0
- package/dist/plugins/devtools/ui/data/VisualizationContainer.svelte +55 -0
- package/dist/plugins/devtools/ui/feedback/Banner.svelte +116 -0
- package/dist/plugins/devtools/ui/feedback/Spinner.svelte +79 -0
- package/dist/plugins/devtools/ui/forms/Checkbox.svelte +139 -0
- package/dist/plugins/devtools/ui/forms/RadioGroup.svelte +218 -0
- package/dist/plugins/devtools/ui/forms/Select.svelte +137 -0
- package/dist/plugins/devtools/ui/forms/Switch.svelte +158 -0
- package/dist/plugins/devtools/ui/forms/TextField.svelte +125 -0
- package/dist/plugins/devtools/ui/index.d.ts +22 -0
- package/dist/plugins/devtools/ui/index.js +1 -0
- package/dist/plugins/devtools/ui/layout/Card.svelte +55 -0
- package/dist/plugins/devtools/ui/layout/Divider.svelte +34 -0
- package/dist/plugins/devtools/ui/layout/Spacer.svelte +11 -0
- package/dist/plugins/devtools/ui/layout/Stack.svelte +129 -0
- package/dist/plugins/devtools/ui/overlays/Tooltip.svelte +90 -0
- package/dist/plugins/devtools/ui/tokens.css +144 -0
- package/dist/plugins/devtools/ui/tokens.css.js +1 -0
- package/dist/plugins/devtools/ui/typography/Heading.svelte +55 -0
- package/dist/plugins/devtools/ui/typography/Text.svelte +93 -0
- package/package.json +5 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as d3 from 'd3'
|
|
3
3
|
import type { ExperienceEdge, ExperienceNode, ExperienceStore } from '@picked/client'
|
|
4
|
+
import { Badge, Button, Spinner, Stack, Text, VisualizationContainer } from '../../ui'
|
|
4
5
|
|
|
5
6
|
export let experience: ExperienceStore
|
|
6
7
|
|
|
@@ -237,74 +238,22 @@
|
|
|
237
238
|
}
|
|
238
239
|
</script>
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
<svg bind:this={svgEl} class="tree-svg"></svg>
|
|
260
|
-
</div>
|
|
261
|
-
{/if}
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
<style>
|
|
265
|
-
.graph {
|
|
266
|
-
border: 1px solid lightgray;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.loading-container,
|
|
270
|
-
.error-container {
|
|
271
|
-
display: flex;
|
|
272
|
-
height: 100%;
|
|
273
|
-
align-items: center;
|
|
274
|
-
justify-content: center;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.error-container {
|
|
278
|
-
color: #ef4444;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.tree-container {
|
|
282
|
-
position: relative;
|
|
283
|
-
height: 100%;
|
|
284
|
-
width: 100%;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.controls {
|
|
288
|
-
position: absolute;
|
|
289
|
-
right: 1rem;
|
|
290
|
-
top: 1rem;
|
|
291
|
-
z-index: 10;
|
|
292
|
-
display: flex;
|
|
293
|
-
gap: 0.5rem;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
.zoom-level {
|
|
297
|
-
display: flex;
|
|
298
|
-
align-items: center;
|
|
299
|
-
border-radius: 0.25rem;
|
|
300
|
-
background-color: rgba(255, 255, 255, 0.8);
|
|
301
|
-
padding: 0.25rem 0.5rem;
|
|
302
|
-
font-size: 0.75rem;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.tree-svg {
|
|
306
|
-
height: 100%;
|
|
307
|
-
width: 100%;
|
|
308
|
-
background-color: white;
|
|
309
|
-
}
|
|
310
|
-
</style>
|
|
241
|
+
{#if loading}
|
|
242
|
+
<Stack direction="horizontal" align="center" justify="center" fill>
|
|
243
|
+
<Spinner label="Loading visualization" />
|
|
244
|
+
</Stack>
|
|
245
|
+
{:else if error}
|
|
246
|
+
<Stack direction="horizontal" align="center" justify="center" fill>
|
|
247
|
+
<Text color="error">{error}</Text>
|
|
248
|
+
</Stack>
|
|
249
|
+
{:else}
|
|
250
|
+
<VisualizationContainer>
|
|
251
|
+
{#snippet controls()}
|
|
252
|
+
<Button variant="secondary" size="small" onclick={handleZoomIn}>Zoom in</Button>
|
|
253
|
+
<Button variant="secondary" size="small" onclick={handleZoomOut}>Zoom out</Button>
|
|
254
|
+
<Button variant="secondary" size="small" onclick={handleReset}>Reset</Button>
|
|
255
|
+
<Badge status="default" size="small">{Math.round(zoom * 100)}%</Badge>
|
|
256
|
+
{/snippet}
|
|
257
|
+
<svg bind:this={svgEl}></svg>
|
|
258
|
+
</VisualizationContainer>
|
|
259
|
+
{/if}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { createStore, createEvent } from 'effector'
|
|
3
3
|
import type { FeatureBucket } from '@picked/client'
|
|
4
4
|
import { getPicked, useUnit } from '../../../../hooks'
|
|
5
|
+
import { DataTable, EmptyState, Button, Text, CopyButton, Card, CodeBlock, Stack } from '../../ui'
|
|
5
6
|
|
|
6
7
|
interface FeatureSelection {
|
|
7
8
|
included: boolean
|
|
@@ -14,13 +15,8 @@
|
|
|
14
15
|
const initSelections = createEvent<FeatureBucket[]>()
|
|
15
16
|
const featureToggled = createEvent<string>()
|
|
16
17
|
const valueChanged = createEvent<{ id: string; value: string }>()
|
|
17
|
-
const copySucceeded = createEvent()
|
|
18
|
-
const copyReset = createEvent()
|
|
19
18
|
|
|
20
19
|
const selectionsStore = createStore<Record<string, FeatureSelection>>({})
|
|
21
|
-
// Triggered whenever the features list changes. Adds an entry for any feature
|
|
22
|
-
// not yet tracked, seeding its override value from the already-resolved bucket
|
|
23
|
-
// (or 'true' as a sensible default for unresolved boolean features).
|
|
24
20
|
.on(initSelections, (state, feats) => {
|
|
25
21
|
const next = { ...state }
|
|
26
22
|
feats.forEach((feature) => {
|
|
@@ -34,30 +30,21 @@
|
|
|
34
30
|
})
|
|
35
31
|
return next
|
|
36
32
|
})
|
|
37
|
-
// Toggles whether the feature is included in the generated URL.
|
|
38
33
|
.on(featureToggled, (state, id) => ({
|
|
39
34
|
...state,
|
|
40
35
|
[id]: { ...state[id], included: !state[id].included },
|
|
41
36
|
}))
|
|
42
|
-
// Updates the override value for a feature (edited via the text/select input).
|
|
43
37
|
.on(valueChanged, (state, { id, value }) => ({
|
|
44
38
|
...state,
|
|
45
39
|
[id]: { ...state[id], value },
|
|
46
40
|
}))
|
|
47
41
|
|
|
48
|
-
const copySuccessStore = createStore(false)
|
|
49
|
-
.on(copySucceeded, () => true) // Show "Copied!" feedback after a successful clipboard write.
|
|
50
|
-
.on(copyReset, () => false) // Reset back to "Copy" after the feedback timeout.
|
|
51
|
-
|
|
52
42
|
const selections = useUnit(selectionsStore)
|
|
53
|
-
const copySuccess = useUnit(copySuccessStore)
|
|
54
43
|
|
|
55
44
|
$effect(() => {
|
|
56
45
|
initSelections($featuresList)
|
|
57
46
|
})
|
|
58
47
|
|
|
59
|
-
let urlInputRef: HTMLInputElement | null = $state(null)
|
|
60
|
-
|
|
61
48
|
function getBucketValue(feature: FeatureBucket): string | null {
|
|
62
49
|
return feature.isResolved ? String(feature.getBucket().bucket) : null
|
|
63
50
|
}
|
|
@@ -82,228 +69,105 @@
|
|
|
82
69
|
|
|
83
70
|
const hasSelections = $derived(Object.values($selections).some((s) => s.included))
|
|
84
71
|
|
|
85
|
-
async function copyUrl() {
|
|
86
|
-
try {
|
|
87
|
-
await navigator.clipboard.writeText(generatedUrl)
|
|
88
|
-
copySucceeded()
|
|
89
|
-
setTimeout(copyReset, 2000)
|
|
90
|
-
} catch {
|
|
91
|
-
urlInputRef?.select()
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
72
|
function refreshPage() {
|
|
96
73
|
window.location.href = generatedUrl
|
|
97
74
|
}
|
|
98
|
-
</script>
|
|
99
75
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
76
|
+
const columns = [
|
|
77
|
+
{ key: 'include', header: 'Include' },
|
|
78
|
+
{ key: 'feature', header: 'Feature' },
|
|
79
|
+
{ key: 'currentValue', header: 'Current Value' },
|
|
80
|
+
{ key: 'overrideValue', header: 'Override Value' },
|
|
81
|
+
]
|
|
82
|
+
</script>
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
84
|
+
<Stack direction="vertical" gap="medium" padding="medium">
|
|
85
|
+
<Text size="caption" color="subdued">
|
|
86
|
+
Override feature flags via URL parameters. Select features below, choose values, then copy the
|
|
87
|
+
generated URL. Recipients will see the overridden values on page load.
|
|
88
|
+
</Text>
|
|
89
|
+
|
|
90
|
+
<DataTable columns={columns} rows={$featuresList}>
|
|
91
|
+
{#snippet row(item)}
|
|
92
|
+
{@const feature = item}
|
|
93
|
+
{@const selection = $selections[feature.id]}
|
|
94
|
+
{@const currentValue = getBucketValue(feature)}
|
|
95
|
+
{@const isBoolean = isBooleanFeature(feature)}
|
|
96
|
+
{#if selection}
|
|
97
|
+
<tr class:selected={selection.included}>
|
|
98
|
+
<td class="include-cell">
|
|
99
|
+
<input
|
|
100
|
+
type="checkbox"
|
|
101
|
+
checked={selection.included}
|
|
102
|
+
onchange={() => featureToggled(feature.id)}
|
|
103
|
+
aria-label="Include {feature.name} in URL"
|
|
104
|
+
/>
|
|
105
|
+
</td>
|
|
106
|
+
<td>
|
|
107
|
+
<Text size="small" weight="medium">{feature.name}</Text>
|
|
108
|
+
<CodeBlock code={feature.key} inline />
|
|
109
|
+
</td>
|
|
110
|
+
<td>
|
|
111
|
+
{#if currentValue !== null}
|
|
112
|
+
<CodeBlock code={currentValue} inline />
|
|
113
|
+
{:else}
|
|
114
|
+
<Button variant="ghost" size="small" onclick={() => feature.getBucket()}>
|
|
115
|
+
Click to resolve
|
|
116
|
+
</Button>
|
|
117
|
+
{/if}
|
|
118
|
+
</td>
|
|
119
|
+
<td>
|
|
120
|
+
{#if isBoolean}
|
|
121
|
+
<select
|
|
122
|
+
class="inline-select"
|
|
123
|
+
value={selection.value}
|
|
124
|
+
onchange={(e) => valueChanged({ id: feature.id, value: e.currentTarget.value })}
|
|
125
|
+
disabled={!selection.included}
|
|
126
|
+
>
|
|
127
|
+
<option value="true">true</option>
|
|
128
|
+
<option value="false">false</option>
|
|
129
|
+
</select>
|
|
130
|
+
{:else}
|
|
123
131
|
<input
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
class="inline-input"
|
|
133
|
+
type="text"
|
|
134
|
+
value={selection.value}
|
|
135
|
+
oninput={(e) => valueChanged({ id: feature.id, value: e.currentTarget.value })}
|
|
136
|
+
disabled={!selection.included}
|
|
137
|
+
placeholder="value"
|
|
128
138
|
/>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
oninput={(e) => valueChanged({ id: feature.id, value: e.currentTarget.value })}
|
|
165
|
-
disabled={!selection.included}
|
|
166
|
-
style="width: 100%; font-size: 11px; padding: 0.125rem 0.25rem;"
|
|
167
|
-
placeholder="value"
|
|
168
|
-
/>
|
|
169
|
-
{/if}
|
|
170
|
-
</td>
|
|
171
|
-
</tr>
|
|
172
|
-
{/if}
|
|
173
|
-
{/each}
|
|
174
|
-
</tbody>
|
|
175
|
-
</table>
|
|
176
|
-
|
|
177
|
-
{#if $featuresList.length === 0}
|
|
178
|
-
<div style="padding: 2rem; text-align: center; color: #999;">No features registered</div>
|
|
179
|
-
{/if}
|
|
180
|
-
|
|
181
|
-
<div class="url-output">
|
|
182
|
-
<div class="url-label">Generated URL</div>
|
|
183
|
-
<div class="url-row">
|
|
184
|
-
<input
|
|
185
|
-
bind:this={urlInputRef}
|
|
186
|
-
type="text"
|
|
187
|
-
readonly
|
|
188
|
-
value={generatedUrl}
|
|
189
|
-
class="url-input"
|
|
190
|
-
onclick={(e) => e.currentTarget.select()}
|
|
191
|
-
aria-label="Generated URL"
|
|
192
|
-
/>
|
|
193
|
-
<button
|
|
194
|
-
onclick={copyUrl}
|
|
195
|
-
class="action-button copy-button"
|
|
196
|
-
class:success={copySuccess}
|
|
197
|
-
title="Copy URL to clipboard"
|
|
198
|
-
>
|
|
199
|
-
{copySuccess ? 'Copied!' : 'Copy'}
|
|
200
|
-
</button>
|
|
201
|
-
<button
|
|
202
|
-
onclick={refreshPage}
|
|
203
|
-
class="action-button refresh-button"
|
|
204
|
-
disabled={!hasSelections}
|
|
205
|
-
title="Reload the page with this URL"
|
|
206
|
-
>
|
|
207
|
-
Reload
|
|
208
|
-
</button>
|
|
209
|
-
</div>
|
|
210
|
-
{#if !hasSelections}
|
|
211
|
-
<p class="hint">Select at least one feature above to add overrides to the URL.</p>
|
|
212
|
-
{/if}
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
215
|
-
|
|
216
|
-
<style>
|
|
217
|
-
.url-builder {
|
|
218
|
-
display: flex;
|
|
219
|
-
flex-direction: column;
|
|
220
|
-
gap: 0;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.description {
|
|
224
|
-
margin: 0;
|
|
225
|
-
padding: 0.75rem 0.5rem;
|
|
226
|
-
font-size: 12px;
|
|
227
|
-
color: #555;
|
|
228
|
-
border-bottom: 1px solid lightgray;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
tr.selected {
|
|
232
|
-
background-color: #f0f7ff;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.url-output {
|
|
236
|
-
padding: 0.75rem 0.5rem;
|
|
237
|
-
border-top: 1px solid lightgray;
|
|
238
|
-
display: flex;
|
|
239
|
-
flex-direction: column;
|
|
240
|
-
gap: 0.5rem;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.url-label {
|
|
244
|
-
font-size: 11px;
|
|
245
|
-
font-weight: 600;
|
|
246
|
-
color: #555;
|
|
247
|
-
text-transform: uppercase;
|
|
248
|
-
letter-spacing: 0.05em;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.url-row {
|
|
252
|
-
display: flex;
|
|
253
|
-
gap: 0.5rem;
|
|
254
|
-
align-items: center;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.url-input {
|
|
258
|
-
flex: 1;
|
|
259
|
-
font-size: 11px;
|
|
260
|
-
font-family: monospace;
|
|
261
|
-
padding: 0.375rem 0.5rem;
|
|
262
|
-
border: 1px solid #ccc;
|
|
263
|
-
border-radius: 3px;
|
|
264
|
-
background-color: #f9f9f9;
|
|
265
|
-
color: #333;
|
|
266
|
-
min-width: 0;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.url-input:focus {
|
|
270
|
-
outline: 2px solid #0066cc;
|
|
271
|
-
outline-offset: -1px;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
.action-button {
|
|
275
|
-
padding: 0.375rem 0.75rem;
|
|
276
|
-
font-size: 11px;
|
|
277
|
-
border: none;
|
|
278
|
-
border-radius: 3px;
|
|
279
|
-
cursor: pointer;
|
|
280
|
-
white-space: nowrap;
|
|
281
|
-
font-weight: 500;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.action-button:disabled {
|
|
285
|
-
opacity: 0.5;
|
|
286
|
-
cursor: not-allowed;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.copy-button {
|
|
290
|
-
background-color: #2196f3;
|
|
291
|
-
color: white;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.copy-button.success {
|
|
295
|
-
background-color: #4caf50;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
.refresh-button {
|
|
299
|
-
background-color: #555;
|
|
300
|
-
color: white;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.hint {
|
|
304
|
-
margin: 0;
|
|
305
|
-
font-size: 11px;
|
|
306
|
-
color: #999;
|
|
307
|
-
font-style: italic;
|
|
308
|
-
}
|
|
309
|
-
</style>
|
|
139
|
+
{/if}
|
|
140
|
+
</td>
|
|
141
|
+
</tr>
|
|
142
|
+
{/if}
|
|
143
|
+
{/snippet}
|
|
144
|
+
{#snippet empty()}
|
|
145
|
+
<EmptyState message="No features registered" />
|
|
146
|
+
{/snippet}
|
|
147
|
+
</DataTable>
|
|
148
|
+
|
|
149
|
+
<Card>
|
|
150
|
+
{#snippet header()}
|
|
151
|
+
<Text size="caption" weight="semibold" color="subdued">GENERATED URL</Text>
|
|
152
|
+
{/snippet}
|
|
153
|
+
<Stack direction="vertical" gap="small">
|
|
154
|
+
<CodeBlock code={generatedUrl} />
|
|
155
|
+
<Stack direction="horizontal" gap="small" justify="center">
|
|
156
|
+
<CopyButton text={generatedUrl} label="Copy URL" />
|
|
157
|
+
<Button
|
|
158
|
+
variant="secondary"
|
|
159
|
+
size="small"
|
|
160
|
+
onclick={refreshPage}
|
|
161
|
+
disabled={!hasSelections}
|
|
162
|
+
>
|
|
163
|
+
Reload
|
|
164
|
+
</Button>
|
|
165
|
+
</Stack>
|
|
166
|
+
{#if !hasSelections}
|
|
167
|
+
<Text size="caption" color="subdued">
|
|
168
|
+
Select at least one feature above to add overrides to the URL.
|
|
169
|
+
</Text>
|
|
170
|
+
{/if}
|
|
171
|
+
</Stack>
|
|
172
|
+
</Card>
|
|
173
|
+
</Stack>
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getPicked } from '../../../../hooks'
|
|
3
|
+
import { DataTable, EmptyState } from '../../ui'
|
|
3
4
|
|
|
4
5
|
const client = getPicked()
|
|
5
6
|
|
|
6
7
|
$: frameworks = client.frameworks.listStore
|
|
8
|
+
|
|
9
|
+
const columns = [{ key: 'name', header: 'Name' }]
|
|
7
10
|
</script>
|
|
8
11
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
</tr>
|
|
21
|
-
{/each}
|
|
22
|
-
</tbody>
|
|
23
|
-
</table>
|
|
24
|
-
</div>
|
|
12
|
+
<DataTable columns={columns} rows={$frameworks}>
|
|
13
|
+
{#snippet row(item)}
|
|
14
|
+
{@const framework = item}
|
|
15
|
+
<tr>
|
|
16
|
+
<td>{framework.name}</td>
|
|
17
|
+
</tr>
|
|
18
|
+
{/snippet}
|
|
19
|
+
{#snippet empty()}
|
|
20
|
+
<EmptyState message="No frameworks" />
|
|
21
|
+
{/snippet}
|
|
22
|
+
</DataTable>
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HistoryEntry } from '@picked/client'
|
|
3
3
|
import HistoryEntryListItem from './HistoryEntryListItem.svelte'
|
|
4
|
+
import { DataTable, EmptyState } from '../../ui'
|
|
4
5
|
|
|
5
6
|
export let historyEntries: HistoryEntry[]
|
|
7
|
+
|
|
8
|
+
const columns = [
|
|
9
|
+
{ key: 'id', header: 'ID' },
|
|
10
|
+
{ key: 'experienceId', header: 'Experience ID' },
|
|
11
|
+
{ key: 'nodeId', header: 'Node ID' },
|
|
12
|
+
{ key: 'nodeType', header: 'Node Type' },
|
|
13
|
+
{ key: 'at', header: 'At' },
|
|
14
|
+
]
|
|
6
15
|
</script>
|
|
7
16
|
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</thead>
|
|
18
|
-
<tbody>
|
|
19
|
-
{#each historyEntries as historyEntry (historyEntry.id)}
|
|
20
|
-
<HistoryEntryListItem {historyEntry} />
|
|
21
|
-
{/each}
|
|
22
|
-
</tbody>
|
|
23
|
-
</table>
|
|
17
|
+
<DataTable columns={columns} rows={historyEntries}>
|
|
18
|
+
{#snippet row(item)}
|
|
19
|
+
{@const historyEntry = item}
|
|
20
|
+
<HistoryEntryListItem {historyEntry} />
|
|
21
|
+
{/snippet}
|
|
22
|
+
{#snippet empty()}
|
|
23
|
+
<EmptyState message="No history entries" />
|
|
24
|
+
{/snippet}
|
|
25
|
+
</DataTable>
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getPicked } from '../../../../hooks'
|
|
3
|
+
import { DataTable, EmptyState } from '../../ui'
|
|
3
4
|
|
|
4
5
|
const client = getPicked()
|
|
5
6
|
|
|
6
7
|
$: predicates = client.predicates.listStore
|
|
8
|
+
|
|
9
|
+
const columns = [{ key: 'name', header: 'Name' }]
|
|
7
10
|
</script>
|
|
8
11
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
</tr>
|
|
21
|
-
{/each}
|
|
22
|
-
</tbody>
|
|
23
|
-
</table>
|
|
24
|
-
</div>
|
|
12
|
+
<DataTable columns={columns} rows={$predicates}>
|
|
13
|
+
{#snippet row(item)}
|
|
14
|
+
{@const predicate = item}
|
|
15
|
+
<tr>
|
|
16
|
+
<td>{predicate.name}</td>
|
|
17
|
+
</tr>
|
|
18
|
+
{/snippet}
|
|
19
|
+
{#snippet empty()}
|
|
20
|
+
<EmptyState message="No predicates" />
|
|
21
|
+
{/snippet}
|
|
22
|
+
</DataTable>
|