@picked/svelte 0.0.62-next.12 → 0.0.62-next.3
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/components/LazyImage.svelte +1 -1
- package/dist/hooks/index.d.ts +1 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/step.d.ts +2 -25
- package/dist/hooks/step.js +1 -1
- package/dist/plugins/devtools/DevToolsExperience.svelte +20 -53
- package/dist/plugins/devtools/components/Tabs.svelte +2 -23
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +9 -11
- package/package.json +2 -2
- package/dist/hooks/content.d.ts +0 -18
- package/dist/hooks/content.js +0 -1
- package/dist/plugins/devtools/screens/config/ConfigScreen.svelte +0 -153
- package/dist/plugins/devtools/screens/content/ContentListScreen.svelte +0 -89
- 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
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
if (isConvincelyDomain(url)) {
|
|
89
89
|
const encodedUrl = encodeURIComponent(url.toString())
|
|
90
90
|
const optimizedSize = findClosestSize(targetWidth)
|
|
91
|
-
return `https://img.
|
|
91
|
+
return `https://img.convincely.io/_next/image?url=${encodedUrl}&w=${optimizedSize}&q=75`
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// For other domains, return the original URL
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -3,8 +3,7 @@ export { getPicked, getExperience } from './client';
|
|
|
3
3
|
export { getPredicate, getPredicateResult } from './predicate';
|
|
4
4
|
export { getFeature, getFeatureResult } from './feature';
|
|
5
5
|
export { getAction, getActionResult } from './action';
|
|
6
|
-
export { getStep, setStepContext
|
|
7
|
-
export { getContent } from './content';
|
|
6
|
+
export { getStep, setStepContext } from './step';
|
|
8
7
|
export { useUnit, getContextScope, setContextScope } from './stores';
|
|
9
8
|
export { createSessionStorageStore, createLocalStorageStore } from './storage.js';
|
|
10
9
|
export { getTrackRecommendationPresentEvent } from './events';
|
package/dist/hooks/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getExperience as e,getPicked as t}from"./client.js";import{getPredicate as n,getPredicateResult as r}from"./predicate.js";import{getFeature as i,getFeatureResult as a}from"./feature.js";import{getAction as o,getActionResult as s}from"./action.js";import{getStep as c,
|
|
1
|
+
import{getExperience as e,getPicked as t}from"./client.js";import{getPredicate as n,getPredicateResult as r}from"./predicate.js";import{getFeature as i,getFeatureResult as a}from"./feature.js";import{getAction as o,getActionResult as s}from"./action.js";import{getStep as c,setStepContext as l}from"./step.js";import{getContextScope as u,setContextScope as d,useUnit as f}from"./stores.js";import{createLocalStorageStore as p,createSessionStorageStore as m}from"./storage.js";import{getTrackRecommendationPresentEvent as h}from"./events.js";import{createQuery as g,queryCache as _,queryPresets as v}from"./create-query/index.js";export{p as createLocalStorageStore,g as createQuery,m as createSessionStorageStore,o as getAction,s as getActionResult,u as getContextScope,e as getExperience,i as getFeature,a as getFeatureResult,t as getPicked,n as getPredicate,r as getPredicateResult,c as getStep,h as getTrackRecommendationPresentEvent,_ as queryCache,v as queryPresets,d as setContextScope,l as setStepContext,f as useUnit};
|
package/dist/hooks/step.d.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { Store } from 'effector';
|
|
2
|
-
import {
|
|
2
|
+
import { ExperienceStep } from '@picked/client';
|
|
3
3
|
import { FormInstance, FormField } from '../form/create-form.svelte';
|
|
4
4
|
export interface StepContext extends FormInstance {
|
|
5
5
|
step: Store<ExperienceStep>;
|
|
6
6
|
/** Directly accessible fields with current response values — usable without a form. */
|
|
7
7
|
fields: Store<FormField[]>;
|
|
8
|
-
/**
|
|
9
|
-
* Reactive store of the current step's CMS layout content. Resolves the step's
|
|
10
|
-
* `layoutContentId` against `client.contents` and stays reactive.
|
|
11
|
-
*/
|
|
12
|
-
layoutContentStore: Store<Content | null>;
|
|
13
8
|
isActive: Store<boolean>;
|
|
14
9
|
/** @deprecated Use `isActive` instead. */
|
|
15
10
|
isActiveStep: Store<boolean>;
|
|
@@ -17,31 +12,13 @@ export interface StepContext extends FormInstance {
|
|
|
17
12
|
/**
|
|
18
13
|
* Creates and sets step context in the Svelte context tree.
|
|
19
14
|
*
|
|
20
|
-
* @param
|
|
21
|
-
* @returns {StepContext} The created step context.
|
|
15
|
+
* @param stepKey - The step key.
|
|
22
16
|
* @example setStepContext('step-1')
|
|
23
17
|
*/
|
|
24
18
|
export declare function setStepContext(stepKey: string): StepContext;
|
|
25
19
|
/**
|
|
26
20
|
* Retrieves the step context from the Svelte context tree.
|
|
27
21
|
*
|
|
28
|
-
* @returns {StepContext} The current step context.
|
|
29
22
|
* @example getStep()
|
|
30
23
|
*/
|
|
31
24
|
export declare function getStep(): StepContext;
|
|
32
|
-
/**
|
|
33
|
-
* Returns a Svelte/Effector store that yields the current step's resolved CMS content
|
|
34
|
-
* data, or `null` when no content is registered for the step's `layoutContentId`.
|
|
35
|
-
* Subscribers automatically receive updates when content is registered or replaced.
|
|
36
|
-
*
|
|
37
|
-
* @returns {Store<Record<string, unknown> | null>} A store of the Content record's `data`.
|
|
38
|
-
* @example
|
|
39
|
-
* ```svelte
|
|
40
|
-
* <script>
|
|
41
|
-
* import { getStepContent } from '@picked/svelte'
|
|
42
|
-
* const content = getStepContent()
|
|
43
|
-
* </script>
|
|
44
|
-
* <h1>{$content?.headline ?? ''}</h1>
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export declare function getStepContent(): Store<Record<string, unknown> | null>;
|
package/dist/hooks/step.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{STEP_CONTEXT_KEY as e}from"../context.js";import{getPicked as t}from"./client.js";import{getStepRouter as n}from"../router/context.js";import{
|
|
1
|
+
import{STEP_CONTEXT_KEY as e}from"../context.js";import{getPicked as t}from"./client.js";import{getStepRouter as n}from"../router/context.js";import{getContext as r,setContext as i}from"svelte";import{createForm as a}from"../form/create-form.svelte";function o(r){let o=t(),{activeStep:s}=n(),c=s.map(e=>e?.key===r),l=o.steps.listStore.map(e=>e.find(e=>e.key===r)??{id:r,key:r,name:``,fieldKeys:[]});return i(e,{step:l,isActive:c,isActiveStep:c,...a(r,l.map(e=>e.name),l.map(e=>e.fieldKeys))})}function s(){return r(e)}export{s as getStep,o as setStepContext};
|
|
@@ -15,12 +15,8 @@
|
|
|
15
15
|
import ComponentsListScreen from './screens/components/ComponentsListScreen.svelte'
|
|
16
16
|
import FrameworksListScreen from './screens/frameworks/FrameworksListScreen.svelte'
|
|
17
17
|
import ProjectScreen from './screens/project/ProjectScreen.svelte'
|
|
18
|
-
import ConfigScreen from './screens/config/ConfigScreen.svelte'
|
|
19
|
-
import IdentityScreen from './screens/identity/IdentityScreen.svelte'
|
|
20
18
|
import HistoryEntriesListScreen from './screens/history/HistoryEntriesListScreen.svelte'
|
|
21
19
|
import ResponsesListScreen from './screens/responses/ResponsesListScreen.svelte'
|
|
22
|
-
import FieldsListScreen from './screens/fields/FieldsListScreen.svelte'
|
|
23
|
-
import ContentListScreen from './screens/content/ContentListScreen.svelte'
|
|
24
20
|
import FeaturesListScreen from './screens/features/FeaturesListScreen.svelte'
|
|
25
21
|
import EventsListScreen from './screens/events/EventsListScreen.svelte'
|
|
26
22
|
import PickedIcon from './PickedIcon.svelte'
|
|
@@ -39,21 +35,9 @@
|
|
|
39
35
|
props: {},
|
|
40
36
|
},
|
|
41
37
|
{
|
|
42
|
-
id: '
|
|
43
|
-
label: '
|
|
44
|
-
component:
|
|
45
|
-
props: {},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: 'config',
|
|
49
|
-
label: 'Config',
|
|
50
|
-
component: ConfigScreen,
|
|
51
|
-
props: {},
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: 'content',
|
|
55
|
-
label: 'Content',
|
|
56
|
-
component: ContentListScreen,
|
|
38
|
+
id: 'history',
|
|
39
|
+
label: 'History',
|
|
40
|
+
component: HistoryEntriesListScreen,
|
|
57
41
|
props: {},
|
|
58
42
|
},
|
|
59
43
|
{
|
|
@@ -62,6 +46,12 @@
|
|
|
62
46
|
component: EventsListScreen,
|
|
63
47
|
props: {},
|
|
64
48
|
},
|
|
49
|
+
{
|
|
50
|
+
id: 'responses',
|
|
51
|
+
label: 'Responses',
|
|
52
|
+
component: ResponsesListScreen,
|
|
53
|
+
props: {},
|
|
54
|
+
},
|
|
65
55
|
{
|
|
66
56
|
id: 'experience-instances',
|
|
67
57
|
label: 'Experience Instances',
|
|
@@ -74,6 +64,12 @@
|
|
|
74
64
|
component: ExperiencesListScreen,
|
|
75
65
|
props: {},
|
|
76
66
|
},
|
|
67
|
+
{
|
|
68
|
+
id: 'predicates',
|
|
69
|
+
label: 'Predicates',
|
|
70
|
+
component: PredicatesListScreen,
|
|
71
|
+
props: {},
|
|
72
|
+
},
|
|
77
73
|
{
|
|
78
74
|
id: 'features',
|
|
79
75
|
label: 'Features',
|
|
@@ -81,9 +77,9 @@
|
|
|
81
77
|
props: {},
|
|
82
78
|
},
|
|
83
79
|
{
|
|
84
|
-
id: '
|
|
85
|
-
label: '
|
|
86
|
-
component:
|
|
80
|
+
id: 'components',
|
|
81
|
+
label: 'Components',
|
|
82
|
+
component: ComponentsListScreen,
|
|
87
83
|
props: {},
|
|
88
84
|
},
|
|
89
85
|
{
|
|
@@ -92,30 +88,6 @@
|
|
|
92
88
|
component: FrameworksListScreen,
|
|
93
89
|
props: {},
|
|
94
90
|
},
|
|
95
|
-
{
|
|
96
|
-
id: 'history',
|
|
97
|
-
label: 'History',
|
|
98
|
-
component: HistoryEntriesListScreen,
|
|
99
|
-
props: {},
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: 'identity',
|
|
103
|
-
label: 'Identity',
|
|
104
|
-
component: IdentityScreen,
|
|
105
|
-
props: {},
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
id: 'predicates',
|
|
109
|
-
label: 'Predicates',
|
|
110
|
-
component: PredicatesListScreen,
|
|
111
|
-
props: {},
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
id: 'responses',
|
|
115
|
-
label: 'Responses',
|
|
116
|
-
component: ResponsesListScreen,
|
|
117
|
-
props: {},
|
|
118
|
-
},
|
|
119
91
|
]
|
|
120
92
|
|
|
121
93
|
const isOpen = experience.isOpenStore
|
|
@@ -146,7 +118,7 @@
|
|
|
146
118
|
</div>
|
|
147
119
|
</header>
|
|
148
120
|
<section class="main">
|
|
149
|
-
<Tabs {tabs} initialTab={
|
|
121
|
+
<Tabs {tabs} initialTab={1} />
|
|
150
122
|
</section>
|
|
151
123
|
</div>
|
|
152
124
|
</div>
|
|
@@ -157,7 +129,6 @@
|
|
|
157
129
|
{/if}
|
|
158
130
|
<button
|
|
159
131
|
class="floating-button"
|
|
160
|
-
class:has-preview-tag={isPreview}
|
|
161
132
|
onclick={() => experience.open()}
|
|
162
133
|
aria-label="Open Picked Devtools"
|
|
163
134
|
title="Open Picked Devtools"
|
|
@@ -352,7 +323,7 @@
|
|
|
352
323
|
}
|
|
353
324
|
|
|
354
325
|
.floating-button {
|
|
355
|
-
border-radius: 6px;
|
|
326
|
+
border-radius: 0 6px 6px 0;
|
|
356
327
|
background-color: #ffffff;
|
|
357
328
|
display: grid;
|
|
358
329
|
place-items: center;
|
|
@@ -366,10 +337,6 @@
|
|
|
366
337
|
}
|
|
367
338
|
}
|
|
368
339
|
|
|
369
|
-
.floating-button.has-preview-tag {
|
|
370
|
-
border-radius: 0 6px 6px 0;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
340
|
.preview-tag {
|
|
374
341
|
display: flex;
|
|
375
342
|
align-items: center;
|
|
@@ -13,21 +13,18 @@
|
|
|
13
13
|
export let initialTab = 0
|
|
14
14
|
|
|
15
15
|
let activeTab = initialTab
|
|
16
|
-
const tabButtons: (HTMLButtonElement | undefined)[] = []
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
|
-
* Sets the active tab to the given index
|
|
20
|
-
* horizontal viewport so keyboard navigation never lands off-screen.
|
|
18
|
+
* Sets the active tab to the given index.
|
|
21
19
|
*
|
|
22
20
|
* @param {number} index - The zero-based tab index to activate.
|
|
23
21
|
* @example
|
|
24
22
|
* ```typescript
|
|
25
|
-
* activateTab(2) // switches to the third tab
|
|
23
|
+
* activateTab(2) // switches to the third tab
|
|
26
24
|
* ```
|
|
27
25
|
*/
|
|
28
26
|
function activateTab(index: number) {
|
|
29
27
|
activeTab = index
|
|
30
|
-
tabButtons[index]?.scrollIntoView({ inline: 'nearest', block: 'nearest', behavior: 'smooth' })
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
/**
|
|
@@ -69,7 +66,6 @@
|
|
|
69
66
|
<div role="tablist" tabindex="0" on:keydown={handleKeydown} class="tab-list">
|
|
70
67
|
{#each tabs as tab, i (tab.id)}
|
|
71
68
|
<button
|
|
72
|
-
bind:this={tabButtons[i]}
|
|
73
69
|
role="tab"
|
|
74
70
|
id="tab-{tab.id}"
|
|
75
71
|
aria-controls="panel-{tab.id}"
|
|
@@ -114,23 +110,6 @@
|
|
|
114
110
|
border-bottom: 1px solid var(--color-gray-200);
|
|
115
111
|
flex-shrink: 0;
|
|
116
112
|
overflow-x: auto;
|
|
117
|
-
overflow-y: hidden;
|
|
118
|
-
scrollbar-width: thin;
|
|
119
|
-
scrollbar-color: var(--color-gray-300) transparent;
|
|
120
|
-
scroll-behavior: smooth;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.tab-list::-webkit-scrollbar {
|
|
124
|
-
height: 6px;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.tab-list::-webkit-scrollbar-thumb {
|
|
128
|
-
background-color: var(--color-gray-300);
|
|
129
|
-
border-radius: var(--radius-full);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.tab-list::-webkit-scrollbar-track {
|
|
133
|
-
background-color: transparent;
|
|
134
113
|
}
|
|
135
114
|
|
|
136
115
|
.tab-button {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/** @file Editable table row for a single field response in the devtools responses screen. */
|
|
3
|
-
import type { ResponseListItem,
|
|
3
|
+
import type { ResponseListItem, PrimitiveFieldValue, Field } from '@picked/client'
|
|
4
4
|
import { getPicked } from '../../../../hooks'
|
|
5
5
|
import { Button, Text, CodeBlock, Banner, Stack } from '../../ui'
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
let editValue = ''
|
|
14
14
|
let editError = ''
|
|
15
15
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- Set is reassigned for Svelte reactivity via `selectedOptions = selectedOptions`
|
|
16
|
-
let selectedOptions: Set<
|
|
16
|
+
let selectedOptions: Set<PrimitiveFieldValue> = new Set()
|
|
17
17
|
|
|
18
18
|
$: hasOptions = Array.isArray(field?.options) && field.options.length > 0
|
|
19
19
|
$: staticOptions = Array.isArray(field?.options) ? field.options : null
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
/**
|
|
23
23
|
* Formats a response value for display, handling arrays, objects, and primitives.
|
|
24
24
|
*
|
|
25
|
-
* @param
|
|
26
|
-
* @returns {string} The display string for the value.
|
|
25
|
+
* @param value - The response value to format.
|
|
27
26
|
* @example
|
|
28
27
|
* ```typescript
|
|
29
28
|
* formatValue(['a', 'b']) // 'a, b'
|
|
@@ -88,13 +87,13 @@
|
|
|
88
87
|
/**
|
|
89
88
|
* Toggles selection of a field option, supporting both single and multi-select modes.
|
|
90
89
|
*
|
|
91
|
-
* @param
|
|
90
|
+
* @param optionValue - The option value to toggle.
|
|
92
91
|
* @example
|
|
93
92
|
* ```typescript
|
|
94
93
|
* toggleOption('red') // selects or deselects the 'red' option
|
|
95
94
|
* ```
|
|
96
95
|
*/
|
|
97
|
-
function toggleOption(optionValue:
|
|
96
|
+
function toggleOption(optionValue: PrimitiveFieldValue) {
|
|
98
97
|
if (field?.multiple) {
|
|
99
98
|
if (selectedOptions.has(optionValue)) {
|
|
100
99
|
selectedOptions.delete(optionValue)
|
|
@@ -111,15 +110,14 @@
|
|
|
111
110
|
/**
|
|
112
111
|
* Parses a string input into a typed value based on the field's type.
|
|
113
112
|
*
|
|
114
|
-
* @param
|
|
115
|
-
* @returns {FieldValue} The parsed value (null, number, boolean, JSON, or string).
|
|
113
|
+
* @param input - The raw string input to parse.
|
|
116
114
|
* @example
|
|
117
115
|
* ```typescript
|
|
118
116
|
* parseValue('42') // 42 (when field.type is 'number')
|
|
119
117
|
* parseValue('true') // true (when field.type is 'boolean')
|
|
120
118
|
* ```
|
|
121
119
|
*/
|
|
122
|
-
function parseValue(input: string):
|
|
120
|
+
function parseValue(input: string): unknown {
|
|
123
121
|
const trimmed = input.trim()
|
|
124
122
|
|
|
125
123
|
if (trimmed === '' || trimmed === '-') {
|
|
@@ -175,7 +173,7 @@
|
|
|
175
173
|
newValue = parseValue(editValue)
|
|
176
174
|
}
|
|
177
175
|
|
|
178
|
-
const result = await client.responses.commit({ [field.
|
|
176
|
+
const result = await client.responses.commit({ [field.id]: newValue })
|
|
179
177
|
|
|
180
178
|
if (result.errors.length > 0) {
|
|
181
179
|
editError = result.errors[0].userMessage
|
|
@@ -199,7 +197,7 @@
|
|
|
199
197
|
* ```
|
|
200
198
|
*/
|
|
201
199
|
function handleClear() {
|
|
202
|
-
client.responses.clear(field.
|
|
200
|
+
client.responses.clear(field.id)
|
|
203
201
|
}
|
|
204
202
|
</script>
|
|
205
203
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/svelte",
|
|
3
|
-
"version": "0.0.62-next.
|
|
3
|
+
"version": "0.0.62-next.3",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"d3": "^7.9.0"
|
|
6
6
|
},
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
},
|
|
89
89
|
"module": "./dist/index.js",
|
|
90
90
|
"peerDependencies": {
|
|
91
|
-
"@picked/client": "0.0.62-next.
|
|
91
|
+
"@picked/client": "0.0.62-next.3",
|
|
92
92
|
"effector": "^23.3.0",
|
|
93
93
|
"svelte": "^5.46.4"
|
|
94
94
|
},
|
package/dist/hooks/content.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Store } from 'effector';
|
|
2
|
-
import { Content } from '@picked/client';
|
|
3
|
-
/**
|
|
4
|
-
* Returns the registry store for a content id, mirroring `getField(key)`. Resolves
|
|
5
|
-
* the Picked client via `getPicked()` and returns `client.contents.getById(id)`.
|
|
6
|
-
*
|
|
7
|
-
* @param {string} id - The content record id to look up.
|
|
8
|
-
* @returns {Store<Content | null>} The registry store; `null` until content is registered.
|
|
9
|
-
* @example
|
|
10
|
-
* ```svelte
|
|
11
|
-
* <script>
|
|
12
|
-
* import { getContent } from '@picked/svelte'
|
|
13
|
-
* const banner = getContent('lc_banner')
|
|
14
|
-
* </script>
|
|
15
|
-
* <h1>{$banner?.data?.headline ?? ''}</h1>
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
export declare function getContent(id: string): Store<Content | null>;
|
package/dist/hooks/content.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{getPicked as e}from"./client.js";function t(t){return e().contents.getById(t)}export{t as getContent};
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Devtools screen for the resolved Picked client configuration and reactive lifecycle state. */
|
|
3
|
-
import { getPicked } from '../../../../hooks'
|
|
4
|
-
import { Badge, Card, CodeBlock, DataTable, EmptyState, Stack, Text } from '../../ui'
|
|
5
|
-
|
|
6
|
-
const client = getPicked()
|
|
7
|
-
const lifecycleStageStore = client.lifecycleStageStore
|
|
8
|
-
const readyStore = client.readyStore
|
|
9
|
-
const preconditionPassedStore = client.preconditionPassedStore
|
|
10
|
-
const conditionPassedStore = client.conditionPassedStore
|
|
11
|
-
|
|
12
|
-
const searchParamEntries = Object.entries(client.searchParams ?? {}).map(([key, value]) => ({
|
|
13
|
-
key,
|
|
14
|
-
value: Array.isArray(value) ? value.join(', ') : String(value ?? ''),
|
|
15
|
-
}))
|
|
16
|
-
|
|
17
|
-
const staticRows = [
|
|
18
|
-
{ label: 'Project ID', value: client.projectId },
|
|
19
|
-
{ label: 'API URL', value: client.apiUrl },
|
|
20
|
-
{ label: 'Dashboard URL', value: client.dashboardUrl },
|
|
21
|
-
{ label: 'Documentation URL', value: client.documentationSiteUrl },
|
|
22
|
-
{ label: 'Data source', value: client.dataSource === null ? '—' : JSON.stringify(client.dataSource) },
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
const configColumns = [
|
|
26
|
-
{ key: 'label', header: 'Property' },
|
|
27
|
-
{ key: 'value', header: 'Value' },
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
const searchParamColumns = [
|
|
31
|
-
{ key: 'key', header: 'Key' },
|
|
32
|
-
{ key: 'value', header: 'Value' },
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Maps a lifecycle stage to a Badge status colour.
|
|
37
|
-
*
|
|
38
|
-
* @param {'idle' | 'starting' | 'ready' | 'failed'} stage - The current lifecycle stage.
|
|
39
|
-
* @returns {'default' | 'info' | 'success' | 'error'} The Badge status variant.
|
|
40
|
-
* @example
|
|
41
|
-
* ```typescript
|
|
42
|
-
* stageStatus('ready') // 'success'
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
function stageStatus(
|
|
46
|
-
stage: 'idle' | 'starting' | 'ready' | 'failed',
|
|
47
|
-
): 'default' | 'info' | 'success' | 'error' {
|
|
48
|
-
if (stage === 'ready') return 'success'
|
|
49
|
-
if (stage === 'starting') return 'info'
|
|
50
|
-
if (stage === 'failed') return 'error'
|
|
51
|
-
return 'default'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Maps a tri-state boolean gate result to a Badge status colour.
|
|
56
|
-
*
|
|
57
|
-
* @param {boolean | undefined} value - The gate value: true (passed), false (blocked), or undefined (unevaluated).
|
|
58
|
-
* @returns {'default' | 'success' | 'error'} The Badge status variant.
|
|
59
|
-
* @example
|
|
60
|
-
* ```typescript
|
|
61
|
-
* gateStatus(undefined) // 'default'
|
|
62
|
-
* gateStatus(true) // 'success'
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
function gateStatus(value: boolean | undefined): 'default' | 'success' | 'error' {
|
|
66
|
-
if (value === true) return 'success'
|
|
67
|
-
if (value === false) return 'error'
|
|
68
|
-
return 'default'
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Renders a tri-state boolean gate as a short label.
|
|
73
|
-
*
|
|
74
|
-
* @param {boolean | undefined} value - The gate value.
|
|
75
|
-
* @returns {string} The label text.
|
|
76
|
-
* @example
|
|
77
|
-
* ```typescript
|
|
78
|
-
* gateLabel(undefined) // 'pending'
|
|
79
|
-
* ```
|
|
80
|
-
*/
|
|
81
|
-
function gateLabel(value: boolean | undefined): string {
|
|
82
|
-
if (value === true) return 'passed'
|
|
83
|
-
if (value === false) return 'blocked'
|
|
84
|
-
return 'pending'
|
|
85
|
-
}
|
|
86
|
-
</script>
|
|
87
|
-
|
|
88
|
-
<Stack direction="vertical" gap="medium" padding="medium">
|
|
89
|
-
<Text size="caption" color="subdued">
|
|
90
|
-
The resolved configuration and live lifecycle state for this Picked client instance.
|
|
91
|
-
</Text>
|
|
92
|
-
|
|
93
|
-
<Card sectioned>
|
|
94
|
-
{#snippet header()}
|
|
95
|
-
<Text size="caption" weight="semibold" color="subdued">CONFIGURATION</Text>
|
|
96
|
-
{/snippet}
|
|
97
|
-
<DataTable columns={configColumns} rows={staticRows}>
|
|
98
|
-
{#snippet row(item)}
|
|
99
|
-
<tr>
|
|
100
|
-
<td><Text size="small" weight="medium">{item.label}</Text></td>
|
|
101
|
-
<td><CodeBlock code={item.value} inline /></td>
|
|
102
|
-
</tr>
|
|
103
|
-
{/snippet}
|
|
104
|
-
</DataTable>
|
|
105
|
-
</Card>
|
|
106
|
-
|
|
107
|
-
<Card sectioned>
|
|
108
|
-
{#snippet header()}
|
|
109
|
-
<Text size="caption" weight="semibold" color="subdued">LIFECYCLE</Text>
|
|
110
|
-
{/snippet}
|
|
111
|
-
<Stack direction="vertical" gap="small" padding="medium">
|
|
112
|
-
<Stack direction="horizontal" gap="small">
|
|
113
|
-
<Text size="small" weight="medium">Stage:</Text>
|
|
114
|
-
<Badge status={stageStatus($lifecycleStageStore)} size="small">{$lifecycleStageStore}</Badge>
|
|
115
|
-
</Stack>
|
|
116
|
-
<Stack direction="horizontal" gap="small">
|
|
117
|
-
<Text size="small" weight="medium">Ready:</Text>
|
|
118
|
-
<Badge status={$readyStore ? 'success' : 'default'} size="small">{$readyStore ? 'true' : 'false'}</Badge>
|
|
119
|
-
</Stack>
|
|
120
|
-
<Stack direction="horizontal" gap="small">
|
|
121
|
-
<Text size="small" weight="medium">Precondition:</Text>
|
|
122
|
-
<Badge status={gateStatus($preconditionPassedStore)} size="small">
|
|
123
|
-
{gateLabel($preconditionPassedStore)}
|
|
124
|
-
</Badge>
|
|
125
|
-
</Stack>
|
|
126
|
-
<Stack direction="horizontal" gap="small">
|
|
127
|
-
<Text size="small" weight="medium">Condition:</Text>
|
|
128
|
-
<Badge status={gateStatus($conditionPassedStore)} size="small">
|
|
129
|
-
{gateLabel($conditionPassedStore)}
|
|
130
|
-
</Badge>
|
|
131
|
-
</Stack>
|
|
132
|
-
</Stack>
|
|
133
|
-
</Card>
|
|
134
|
-
|
|
135
|
-
<Card sectioned>
|
|
136
|
-
{#snippet header()}
|
|
137
|
-
<Text size="caption" weight="semibold" color="subdued">
|
|
138
|
-
PICKED SEARCH PARAMS ({searchParamEntries.length})
|
|
139
|
-
</Text>
|
|
140
|
-
{/snippet}
|
|
141
|
-
<DataTable columns={searchParamColumns} rows={searchParamEntries}>
|
|
142
|
-
{#snippet row(entry)}
|
|
143
|
-
<tr>
|
|
144
|
-
<td><CodeBlock code={entry.key} inline /></td>
|
|
145
|
-
<td><CodeBlock code={entry.value} inline /></td>
|
|
146
|
-
</tr>
|
|
147
|
-
{/snippet}
|
|
148
|
-
{#snippet empty()}
|
|
149
|
-
<EmptyState message="No picked-* search params on the current URL" />
|
|
150
|
-
{/snippet}
|
|
151
|
-
</DataTable>
|
|
152
|
-
</Card>
|
|
153
|
-
</Stack>
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Devtools screen listing CMS content records registered with the client. */
|
|
3
|
-
import type { Content } from '@picked/client'
|
|
4
|
-
import { getPicked } from '../../../../hooks'
|
|
5
|
-
import { Badge, Card, CodeBlock, DataTable, EmptyState, ExpandableRow, Stack, Text } from '../../ui'
|
|
6
|
-
|
|
7
|
-
const client = getPicked()
|
|
8
|
-
const contentsStore = client.contents.listStore
|
|
9
|
-
|
|
10
|
-
let expandedId: string | null = null
|
|
11
|
-
|
|
12
|
-
const columns = [
|
|
13
|
-
{ key: 'id', header: 'ID' },
|
|
14
|
-
{ key: 'keys', header: 'Top-level keys' },
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Returns the top-level keys of a content record's `data` object, used to give a quick
|
|
19
|
-
* shape preview in the row summary without expanding.
|
|
20
|
-
*
|
|
21
|
-
* @param {Content} content - The content record.
|
|
22
|
-
* @returns {string[]} The data object's top-level keys.
|
|
23
|
-
* @example
|
|
24
|
-
* ```typescript
|
|
25
|
-
* dataKeys({ id: 'c1', data: { headline: 'hi' } }) // ['headline']
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
function dataKeys(content: Content): string[] {
|
|
29
|
-
return Object.keys(content.data ?? {})
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Pretty-prints the content record's `data` object for use inside a multi-line CodeBlock.
|
|
34
|
-
*
|
|
35
|
-
* @param {Content} content - The content record.
|
|
36
|
-
* @returns {string} A two-space-indented JSON representation of `data`.
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* formatData({ id: 'c1', data: { a: 1 } }) // '{\n "a": 1\n}'
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
function formatData(content: Content): string {
|
|
43
|
-
return JSON.stringify(content.data, null, 2)
|
|
44
|
-
}
|
|
45
|
-
</script>
|
|
46
|
-
|
|
47
|
-
<Stack direction="vertical" gap="medium" padding="medium">
|
|
48
|
-
<Text size="caption" color="subdued">
|
|
49
|
-
Content records registered with the client by the active content loader.
|
|
50
|
-
</Text>
|
|
51
|
-
|
|
52
|
-
<Card sectioned>
|
|
53
|
-
<DataTable {columns} rows={$contentsStore}>
|
|
54
|
-
{#snippet row(content)}
|
|
55
|
-
{@const expanded = expandedId === content.id}
|
|
56
|
-
{@const keys = dataKeys(content)}
|
|
57
|
-
<ExpandableRow
|
|
58
|
-
{expanded}
|
|
59
|
-
onToggle={() => { expandedId = expanded ? null : content.id }}
|
|
60
|
-
colspan={columns.length}
|
|
61
|
-
>
|
|
62
|
-
{#snippet summary()}
|
|
63
|
-
<td><CodeBlock code={content.id} inline /></td>
|
|
64
|
-
<td>
|
|
65
|
-
{#if keys.length > 0}
|
|
66
|
-
<Stack direction="horizontal" gap="small" wrap={true}>
|
|
67
|
-
{#each keys as key (key)}
|
|
68
|
-
<Badge status="default" size="small">{key}</Badge>
|
|
69
|
-
{/each}
|
|
70
|
-
</Stack>
|
|
71
|
-
{:else}
|
|
72
|
-
<Text size="small" color="subdued">—</Text>
|
|
73
|
-
{/if}
|
|
74
|
-
</td>
|
|
75
|
-
{/snippet}
|
|
76
|
-
{#snippet details()}
|
|
77
|
-
<Stack direction="vertical" gap="small">
|
|
78
|
-
<Text size="caption" weight="semibold" color="subdued">DATA</Text>
|
|
79
|
-
<CodeBlock code={formatData(content)} />
|
|
80
|
-
</Stack>
|
|
81
|
-
{/snippet}
|
|
82
|
-
</ExpandableRow>
|
|
83
|
-
{/snippet}
|
|
84
|
-
{#snippet empty()}
|
|
85
|
-
<EmptyState message="No content registered" />
|
|
86
|
-
{/snippet}
|
|
87
|
-
</DataTable>
|
|
88
|
-
</Card>
|
|
89
|
-
</Stack>
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Expandable row presenting a single registered field's schema in the devtools fields screen. */
|
|
3
|
-
import type { Field } from '@picked/client'
|
|
4
|
-
import { Badge, CodeBlock, ExpandableRow, Stack, Text } from '../../ui'
|
|
5
|
-
|
|
6
|
-
export let field: Field
|
|
7
|
-
export let columnCount: number
|
|
8
|
-
|
|
9
|
-
let expanded = false
|
|
10
|
-
|
|
11
|
-
$: optionsList = Array.isArray(field.options) ? field.options : []
|
|
12
|
-
$: hasOptions = optionsList.length > 0
|
|
13
|
-
$: hasCondition = field.condition !== undefined && field.condition !== null
|
|
14
|
-
$: hasValidation = field.validation !== undefined && field.validation !== null
|
|
15
|
-
$: hasDefault = field.defaultValue !== undefined
|
|
16
|
-
$: doNotTrack = Boolean(field.policies?.doNotTrackValue)
|
|
17
|
-
$: doNotStore = Boolean(field.policies?.doNotStoreValue)
|
|
18
|
-
$: hasDetails =
|
|
19
|
-
hasCondition ||
|
|
20
|
-
hasValidation ||
|
|
21
|
-
hasOptions ||
|
|
22
|
-
Boolean(field.description) ||
|
|
23
|
-
Boolean(field.helpText) ||
|
|
24
|
-
Boolean(field.placeholder) ||
|
|
25
|
-
Boolean(field.label && field.label !== field.name) ||
|
|
26
|
-
Boolean(field.id)
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Returns a Badge status colour for a given field type so each type renders consistently.
|
|
30
|
-
*
|
|
31
|
-
* @param {Field['type']} type - The field's primitive type.
|
|
32
|
-
* @returns {'default' | 'success' | 'warning' | 'info'} The Badge status variant to apply.
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* typeStatus('number') // 'success'
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
function typeStatus(type: Field['type']): 'default' | 'success' | 'warning' | 'info' {
|
|
39
|
-
if (type === 'text') return 'info'
|
|
40
|
-
if (type === 'number') return 'success'
|
|
41
|
-
if (type === 'boolean') return 'warning'
|
|
42
|
-
return 'default'
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Renders an arbitrary value as a short, single-line preview for use in compact table cells.
|
|
47
|
-
* Strings are returned unquoted, primitives are stringified, and objects/arrays are JSON-encoded
|
|
48
|
-
* and truncated.
|
|
49
|
-
*
|
|
50
|
-
* @param {unknown} value - The value to preview.
|
|
51
|
-
* @param {number} [maxLength] - Maximum character length before the preview is truncated.
|
|
52
|
-
* @returns {string} The short preview string, or `'—'` when the value is null/undefined.
|
|
53
|
-
* @example
|
|
54
|
-
* ```typescript
|
|
55
|
-
* previewValue({ foo: 'bar' }) // '{"foo":"bar"}'
|
|
56
|
-
* previewValue(null) // '—'
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
function previewValue(value: unknown, maxLength = 48): string {
|
|
60
|
-
if (value === null || value === undefined) return '—'
|
|
61
|
-
|
|
62
|
-
let str: string
|
|
63
|
-
if (typeof value === 'string') {
|
|
64
|
-
str = value
|
|
65
|
-
} else if (typeof value === 'object') {
|
|
66
|
-
str = JSON.stringify(value)
|
|
67
|
-
} else {
|
|
68
|
-
str = String(value)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (str.length > maxLength) return `${str.slice(0, maxLength - 1)}…`
|
|
72
|
-
return str
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Pretty-prints a JSON-serializable value for use inside a multi-line CodeBlock.
|
|
77
|
-
*
|
|
78
|
-
* @param {unknown} value - The value to format.
|
|
79
|
-
* @returns {string} A two-space-indented JSON representation.
|
|
80
|
-
* @example
|
|
81
|
-
* ```typescript
|
|
82
|
-
* formatJson({ a: 1 }) // '{\n "a": 1\n}'
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
function formatJson(value: unknown): string {
|
|
86
|
-
return JSON.stringify(value, null, 2)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Renders a `FieldOption.value` (which may be any JSON-serializable shape across field types)
|
|
91
|
-
* as a short, copyable code string.
|
|
92
|
-
*
|
|
93
|
-
* @param {unknown} value - The option's underlying value.
|
|
94
|
-
* @returns {string} The string to display inside an inline CodeBlock.
|
|
95
|
-
* @example
|
|
96
|
-
* ```typescript
|
|
97
|
-
* formatOptionValue('us') // 'us'
|
|
98
|
-
* formatOptionValue({ id: 1 }) // '{"id":1}'
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
|
-
function formatOptionValue(value: unknown): string {
|
|
102
|
-
if (typeof value === 'string') return value
|
|
103
|
-
return JSON.stringify(value)
|
|
104
|
-
}
|
|
105
|
-
</script>
|
|
106
|
-
|
|
107
|
-
<ExpandableRow
|
|
108
|
-
{expanded}
|
|
109
|
-
onToggle={() => { if (hasDetails) expanded = !expanded }}
|
|
110
|
-
colspan={columnCount}
|
|
111
|
-
>
|
|
112
|
-
{#snippet summary()}
|
|
113
|
-
<td><CodeBlock code={field.key} inline /></td>
|
|
114
|
-
<td>
|
|
115
|
-
<Stack direction="vertical" gap="none">
|
|
116
|
-
<Text size="small" weight="medium">{field.name}</Text>
|
|
117
|
-
{#if field.label && field.label !== field.name}
|
|
118
|
-
<Text size="caption" color="subdued">{field.label}</Text>
|
|
119
|
-
{/if}
|
|
120
|
-
</Stack>
|
|
121
|
-
</td>
|
|
122
|
-
<td><Badge status={typeStatus(field.type)} size="small">{field.type}</Badge></td>
|
|
123
|
-
<td>
|
|
124
|
-
<Stack direction="horizontal" gap="small">
|
|
125
|
-
{#if field.multiple}
|
|
126
|
-
<Badge status="default" size="small">multiple</Badge>
|
|
127
|
-
{/if}
|
|
128
|
-
{#if field.required}
|
|
129
|
-
<Badge status="warning" size="small">required</Badge>
|
|
130
|
-
{/if}
|
|
131
|
-
{#if doNotTrack}
|
|
132
|
-
<Badge status="error" size="small">no-track</Badge>
|
|
133
|
-
{/if}
|
|
134
|
-
{#if doNotStore}
|
|
135
|
-
<Badge status="error" size="small">no-store</Badge>
|
|
136
|
-
{/if}
|
|
137
|
-
</Stack>
|
|
138
|
-
</td>
|
|
139
|
-
<td>
|
|
140
|
-
{#if hasDefault}
|
|
141
|
-
<CodeBlock code={previewValue(field.defaultValue)} inline />
|
|
142
|
-
{:else}
|
|
143
|
-
<Text size="small" color="subdued">—</Text>
|
|
144
|
-
{/if}
|
|
145
|
-
</td>
|
|
146
|
-
<td>
|
|
147
|
-
{#if hasOptions}
|
|
148
|
-
<Badge status="info" size="small">{optionsList.length}</Badge>
|
|
149
|
-
{:else}
|
|
150
|
-
<Text size="small" color="subdued">—</Text>
|
|
151
|
-
{/if}
|
|
152
|
-
</td>
|
|
153
|
-
<td>
|
|
154
|
-
<Stack direction="horizontal" gap="small">
|
|
155
|
-
{#if hasCondition}
|
|
156
|
-
<Badge status="info" size="small">condition</Badge>
|
|
157
|
-
{/if}
|
|
158
|
-
{#if hasValidation}
|
|
159
|
-
<Badge status="info" size="small">validation</Badge>
|
|
160
|
-
{/if}
|
|
161
|
-
{#if !hasCondition && !hasValidation}
|
|
162
|
-
<Text size="small" color="subdued">—</Text>
|
|
163
|
-
{/if}
|
|
164
|
-
</Stack>
|
|
165
|
-
</td>
|
|
166
|
-
{/snippet}
|
|
167
|
-
|
|
168
|
-
{#snippet details()}
|
|
169
|
-
<Stack direction="vertical" gap="medium">
|
|
170
|
-
<Stack direction="vertical" gap="small">
|
|
171
|
-
<Text size="caption" weight="semibold" color="subdued">IDENTITY</Text>
|
|
172
|
-
<Stack direction="horizontal" gap="medium">
|
|
173
|
-
<Text size="small"><strong>id:</strong></Text>
|
|
174
|
-
<CodeBlock code={field.id} inline />
|
|
175
|
-
</Stack>
|
|
176
|
-
</Stack>
|
|
177
|
-
|
|
178
|
-
{#if field.description || field.helpText || field.placeholder}
|
|
179
|
-
<Stack direction="vertical" gap="small">
|
|
180
|
-
<Text size="caption" weight="semibold" color="subdued">COPY</Text>
|
|
181
|
-
{#if field.description}
|
|
182
|
-
<Text size="small"><strong>description:</strong> {field.description}</Text>
|
|
183
|
-
{/if}
|
|
184
|
-
{#if field.helpText}
|
|
185
|
-
<Text size="small"><strong>help text:</strong> {field.helpText}</Text>
|
|
186
|
-
{/if}
|
|
187
|
-
{#if field.placeholder}
|
|
188
|
-
<Text size="small"><strong>placeholder:</strong> {field.placeholder}</Text>
|
|
189
|
-
{/if}
|
|
190
|
-
</Stack>
|
|
191
|
-
{/if}
|
|
192
|
-
|
|
193
|
-
{#if hasDefault}
|
|
194
|
-
<Stack direction="vertical" gap="small">
|
|
195
|
-
<Text size="caption" weight="semibold" color="subdued">DEFAULT VALUE</Text>
|
|
196
|
-
<CodeBlock code={formatJson(field.defaultValue)} />
|
|
197
|
-
</Stack>
|
|
198
|
-
{/if}
|
|
199
|
-
|
|
200
|
-
{#if hasOptions}
|
|
201
|
-
<Stack direction="vertical" gap="small">
|
|
202
|
-
<Text size="caption" weight="semibold" color="subdued">OPTIONS ({optionsList.length})</Text>
|
|
203
|
-
<table class="options-table">
|
|
204
|
-
<thead>
|
|
205
|
-
<tr>
|
|
206
|
-
<th>Label</th>
|
|
207
|
-
<th>Value</th>
|
|
208
|
-
<th>ID</th>
|
|
209
|
-
<th>Description</th>
|
|
210
|
-
</tr>
|
|
211
|
-
</thead>
|
|
212
|
-
<tbody>
|
|
213
|
-
{#each optionsList as option (option.id)}
|
|
214
|
-
<tr>
|
|
215
|
-
<td>{option.label}</td>
|
|
216
|
-
<td><CodeBlock code={formatOptionValue(option.value)} inline /></td>
|
|
217
|
-
<td><CodeBlock code={option.id} inline /></td>
|
|
218
|
-
<td>
|
|
219
|
-
{#if option.description}
|
|
220
|
-
{option.description}
|
|
221
|
-
{:else}
|
|
222
|
-
<Text size="small" color="subdued">—</Text>
|
|
223
|
-
{/if}
|
|
224
|
-
</td>
|
|
225
|
-
</tr>
|
|
226
|
-
{/each}
|
|
227
|
-
</tbody>
|
|
228
|
-
</table>
|
|
229
|
-
</Stack>
|
|
230
|
-
{/if}
|
|
231
|
-
|
|
232
|
-
{#if hasCondition}
|
|
233
|
-
<Stack direction="vertical" gap="small">
|
|
234
|
-
<Text size="caption" weight="semibold" color="subdued">CONDITION</Text>
|
|
235
|
-
<CodeBlock code={formatJson(field.condition)} />
|
|
236
|
-
</Stack>
|
|
237
|
-
{/if}
|
|
238
|
-
|
|
239
|
-
{#if hasValidation}
|
|
240
|
-
<Stack direction="vertical" gap="small">
|
|
241
|
-
<Text size="caption" weight="semibold" color="subdued">VALIDATION</Text>
|
|
242
|
-
<CodeBlock code={formatJson(field.validation)} />
|
|
243
|
-
</Stack>
|
|
244
|
-
{/if}
|
|
245
|
-
</Stack>
|
|
246
|
-
{/snippet}
|
|
247
|
-
</ExpandableRow>
|
|
248
|
-
|
|
249
|
-
<style>
|
|
250
|
-
.options-table {
|
|
251
|
-
width: 100%;
|
|
252
|
-
border-collapse: collapse;
|
|
253
|
-
background-color: var(--color-white);
|
|
254
|
-
border: 1px solid var(--color-gray-200);
|
|
255
|
-
border-radius: var(--radius-md);
|
|
256
|
-
overflow: hidden;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
.options-table th,
|
|
260
|
-
.options-table td {
|
|
261
|
-
padding: var(--space-1-5) var(--space-2);
|
|
262
|
-
text-align: left;
|
|
263
|
-
font-size: var(--text-xs);
|
|
264
|
-
border-bottom: 1px solid var(--color-gray-200);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
.options-table tr:last-child td {
|
|
268
|
-
border-bottom: 0;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.options-table th {
|
|
272
|
-
font-weight: var(--font-medium);
|
|
273
|
-
color: var(--color-gray-700);
|
|
274
|
-
background-color: var(--color-gray-50);
|
|
275
|
-
}
|
|
276
|
-
</style>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Devtools screen that lists every field registered with the SDK. */
|
|
3
|
-
import { getPicked } from '../../../../hooks'
|
|
4
|
-
import FieldsTable from './FieldsTable.svelte'
|
|
5
|
-
|
|
6
|
-
const client = getPicked()
|
|
7
|
-
const fieldsStore = client.fields.listStore
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<div>
|
|
11
|
-
<FieldsTable fields={$fieldsStore} />
|
|
12
|
-
</div>
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Table of registered fields with type filtering for the devtools fields screen. */
|
|
3
|
-
import type { Field } from '@picked/client'
|
|
4
|
-
import { Card, DataTable, EmptyState, RadioGroup, Stack } from '../../ui'
|
|
5
|
-
import FieldTableRow from './FieldTableRow.svelte'
|
|
6
|
-
|
|
7
|
-
export let fields: Field[] = []
|
|
8
|
-
|
|
9
|
-
type TypeFilter = 'all' | Field['type']
|
|
10
|
-
|
|
11
|
-
let filter: TypeFilter = 'all'
|
|
12
|
-
|
|
13
|
-
$: counts = {
|
|
14
|
-
all: fields.length,
|
|
15
|
-
text: fields.filter((f) => f.type === 'text').length,
|
|
16
|
-
number: fields.filter((f) => f.type === 'number').length,
|
|
17
|
-
boolean: fields.filter((f) => f.type === 'boolean').length,
|
|
18
|
-
object: fields.filter((f) => f.type === 'object').length,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
$: visibleFields = filter === 'all' ? fields : fields.filter((f) => f.type === filter)
|
|
22
|
-
|
|
23
|
-
const columns = [
|
|
24
|
-
{ key: 'key', header: 'Key' },
|
|
25
|
-
{ key: 'name', header: 'Name' },
|
|
26
|
-
{ key: 'type', header: 'Type' },
|
|
27
|
-
{ key: 'flags', header: 'Flags' },
|
|
28
|
-
{ key: 'default', header: 'Default' },
|
|
29
|
-
{ key: 'options', header: 'Options' },
|
|
30
|
-
{ key: 'rules', header: 'Rules' },
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
$: filterOptions = [
|
|
34
|
-
{ value: 'all', label: `All (${counts.all})` },
|
|
35
|
-
{ value: 'text', label: `Text (${counts.text})` },
|
|
36
|
-
{ value: 'number', label: `Number (${counts.number})` },
|
|
37
|
-
{ value: 'boolean', label: `Boolean (${counts.boolean})` },
|
|
38
|
-
{ value: 'object', label: `Object (${counts.object})` },
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Updates the active type filter when the user picks a different radio option.
|
|
43
|
-
*
|
|
44
|
-
* @param {string} value - The chosen filter value, expected to be `'all'` or a `Field['type']`.
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* handleFilterChange('number') // narrows the table to number fields
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
|
-
function handleFilterChange(value: string): void {
|
|
51
|
-
if (value === 'all' || value === 'text' || value === 'number' || value === 'boolean' || value === 'object') {
|
|
52
|
-
filter = value
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
</script>
|
|
56
|
-
|
|
57
|
-
<Stack padding="medium">
|
|
58
|
-
<Card sectioned>
|
|
59
|
-
<Stack direction="horizontal" gap="small" align="space-between" justify="center" padding="medium">
|
|
60
|
-
<RadioGroup
|
|
61
|
-
label="Filter by type"
|
|
62
|
-
name="field-type-filter"
|
|
63
|
-
options={filterOptions}
|
|
64
|
-
value={filter}
|
|
65
|
-
onchange={handleFilterChange}
|
|
66
|
-
orientation="horizontal"
|
|
67
|
-
/>
|
|
68
|
-
</Stack>
|
|
69
|
-
<DataTable {columns} rows={visibleFields}>
|
|
70
|
-
{#snippet row(field)}
|
|
71
|
-
<FieldTableRow {field} columnCount={columns.length} />
|
|
72
|
-
{/snippet}
|
|
73
|
-
{#snippet empty()}
|
|
74
|
-
<EmptyState message="No fields registered" />
|
|
75
|
-
{/snippet}
|
|
76
|
-
</DataTable>
|
|
77
|
-
</Card>
|
|
78
|
-
</Stack>
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
/** @file Devtools screen for the current visitor session — id, isUser flag, and identifiers map. */
|
|
3
|
-
import { getPicked } from '../../../../hooks'
|
|
4
|
-
import { Badge, Card, CodeBlock, DataTable, EmptyState, Stack, Text } from '../../ui'
|
|
5
|
-
|
|
6
|
-
const client = getPicked()
|
|
7
|
-
const sessionStore = client.session.store
|
|
8
|
-
|
|
9
|
-
$: identifierEntries = Object.entries($sessionStore.identifiers ?? {})
|
|
10
|
-
|
|
11
|
-
const overviewColumns = [
|
|
12
|
-
{ key: 'label', header: 'Property' },
|
|
13
|
-
{ key: 'value', header: 'Value' },
|
|
14
|
-
]
|
|
15
|
-
|
|
16
|
-
const identifierColumns = [
|
|
17
|
-
{ key: 'key', header: 'Key' },
|
|
18
|
-
{ key: 'value', header: 'Value' },
|
|
19
|
-
]
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<Stack direction="vertical" gap="medium" padding="medium">
|
|
23
|
-
<Text size="caption" color="subdued">
|
|
24
|
-
The current visitor session as tracked by the SDK. Persisted to localStorage when available.
|
|
25
|
-
</Text>
|
|
26
|
-
|
|
27
|
-
<Card sectioned>
|
|
28
|
-
<DataTable columns={overviewColumns} rows={[{ label: 'Session ID', value: $sessionStore.id }]}>
|
|
29
|
-
{#snippet row(item)}
|
|
30
|
-
<tr>
|
|
31
|
-
<td><Text size="small" weight="medium">{item.label}</Text></td>
|
|
32
|
-
<td><CodeBlock code={item.value} inline /></td>
|
|
33
|
-
</tr>
|
|
34
|
-
{/snippet}
|
|
35
|
-
</DataTable>
|
|
36
|
-
<Stack direction="horizontal" gap="small" padding="medium">
|
|
37
|
-
<Text size="small" weight="medium">Identified as user:</Text>
|
|
38
|
-
{#if $sessionStore.isUser === true}
|
|
39
|
-
<Badge status="success" size="small">true</Badge>
|
|
40
|
-
{:else if $sessionStore.isUser === false}
|
|
41
|
-
<Badge status="default" size="small">false</Badge>
|
|
42
|
-
{:else}
|
|
43
|
-
<Badge status="default" size="small">unset</Badge>
|
|
44
|
-
{/if}
|
|
45
|
-
</Stack>
|
|
46
|
-
</Card>
|
|
47
|
-
|
|
48
|
-
<Card sectioned>
|
|
49
|
-
{#snippet header()}
|
|
50
|
-
<Text size="caption" weight="semibold" color="subdued">
|
|
51
|
-
IDENTIFIERS ({identifierEntries.length})
|
|
52
|
-
</Text>
|
|
53
|
-
{/snippet}
|
|
54
|
-
<DataTable columns={identifierColumns} rows={identifierEntries}>
|
|
55
|
-
{#snippet row(entry)}
|
|
56
|
-
<tr>
|
|
57
|
-
<td><CodeBlock code={entry[0]} inline /></td>
|
|
58
|
-
<td><CodeBlock code={entry[1]} inline /></td>
|
|
59
|
-
</tr>
|
|
60
|
-
{/snippet}
|
|
61
|
-
{#snippet empty()}
|
|
62
|
-
<EmptyState message="No identifiers set" />
|
|
63
|
-
{/snippet}
|
|
64
|
-
</DataTable>
|
|
65
|
-
</Card>
|
|
66
|
-
</Stack>
|