@aiaiai-pt/design-system 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a ValueSourceValue into wire form ready to persist.
|
|
3
|
+
*
|
|
4
|
+
* @param {ValueSourceValue} v
|
|
5
|
+
* @returns {SerializedValueSource}
|
|
6
|
+
*/
|
|
7
|
+
export function serializeValueSource(v: ValueSourceValue): SerializedValueSource;
|
|
8
|
+
/**
|
|
9
|
+
* Convert wire form back into a ValueSourceValue.
|
|
10
|
+
* Returns null when value is null/undefined and no functionArgs were supplied.
|
|
11
|
+
*
|
|
12
|
+
* @param {unknown} wire
|
|
13
|
+
* @param {Record<string, unknown> | null | undefined} [functionArgs]
|
|
14
|
+
* @returns {ValueSourceValue | null}
|
|
15
|
+
*/
|
|
16
|
+
export function parseValueSource(wire: unknown, functionArgs?: Record<string, unknown> | null | undefined): ValueSourceValue | null;
|
|
17
|
+
export namespace _internal {
|
|
18
|
+
export { CREATED_REF_RE };
|
|
19
|
+
}
|
|
20
|
+
export type ValueSourceMode = "literal" | "parameter" | "entity-field" | "user-field" | "now" | "source-id" | "created-field" | "config-list" | "function" | "expression";
|
|
21
|
+
export type ValueSourceValue = ({
|
|
22
|
+
mode: "literal";
|
|
23
|
+
value: string | number | boolean | null;
|
|
24
|
+
} | {
|
|
25
|
+
mode: "parameter";
|
|
26
|
+
key: string;
|
|
27
|
+
} | {
|
|
28
|
+
mode: "entity-field";
|
|
29
|
+
field: string;
|
|
30
|
+
} | {
|
|
31
|
+
mode: "user-field";
|
|
32
|
+
key: string;
|
|
33
|
+
} | {
|
|
34
|
+
mode: "now";
|
|
35
|
+
} | {
|
|
36
|
+
mode: "source-id";
|
|
37
|
+
} | {
|
|
38
|
+
mode: "created-field";
|
|
39
|
+
index: number;
|
|
40
|
+
field: string;
|
|
41
|
+
} | {
|
|
42
|
+
mode: "config-list";
|
|
43
|
+
configType: string;
|
|
44
|
+
} | {
|
|
45
|
+
mode: "function";
|
|
46
|
+
name: string;
|
|
47
|
+
args: Record<string, ValueSourceValue>;
|
|
48
|
+
} | {
|
|
49
|
+
mode: "expression";
|
|
50
|
+
expr: string;
|
|
51
|
+
});
|
|
52
|
+
export type SerializedValueSource = {
|
|
53
|
+
/**
|
|
54
|
+
* The wire scalar/string going into the slot
|
|
55
|
+
* (e.g. ActionEdit.value, ParamValuesSource.config_query.filter[key]).
|
|
56
|
+
*/
|
|
57
|
+
value: unknown;
|
|
58
|
+
/**
|
|
59
|
+
* Resolved function args dict — only set when v.mode === 'function'.
|
|
60
|
+
* Values are wire scalars/strings (recursively serialized).
|
|
61
|
+
*/
|
|
62
|
+
functionArgs?: Record<string, unknown> | undefined;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Helpers for ValueSourcePicker — translate between the picker's
|
|
66
|
+
* structured ValueSourceValue object and the wire format the action
|
|
67
|
+
* engine reads (admin-api/app/actions/refs.py and edits.py).
|
|
68
|
+
*
|
|
69
|
+
* Mode catalog and wire formats — see
|
|
70
|
+
* dev_docs/specs/action-editor-parity-value-source-picker.md §3.
|
|
71
|
+
*/
|
|
72
|
+
/**
|
|
73
|
+
* @typedef {'literal' | 'parameter' | 'entity-field' | 'user-field'
|
|
74
|
+
* | 'now' | 'source-id' | 'created-field'
|
|
75
|
+
* | 'config-list' | 'function' | 'expression'} ValueSourceMode
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* @typedef {(
|
|
79
|
+
* { mode: 'literal', value: string | number | boolean | null }
|
|
80
|
+
* | { mode: 'parameter', key: string }
|
|
81
|
+
* | { mode: 'entity-field', field: string }
|
|
82
|
+
* | { mode: 'user-field', key: string }
|
|
83
|
+
* | { mode: 'now' }
|
|
84
|
+
* | { mode: 'source-id' }
|
|
85
|
+
* | { mode: 'created-field', index: number, field: string }
|
|
86
|
+
* | { mode: 'config-list', configType: string }
|
|
87
|
+
* | { mode: 'function', name: string, args: Record<string, ValueSourceValue> }
|
|
88
|
+
* | { mode: 'expression', expr: string }
|
|
89
|
+
* )} ValueSourceValue
|
|
90
|
+
*/
|
|
91
|
+
/**
|
|
92
|
+
* @typedef {Object} SerializedValueSource
|
|
93
|
+
* @property {unknown} value
|
|
94
|
+
* The wire scalar/string going into the slot
|
|
95
|
+
* (e.g. ActionEdit.value, ParamValuesSource.config_query.filter[key]).
|
|
96
|
+
* @property {Record<string, unknown> | undefined} [functionArgs]
|
|
97
|
+
* Resolved function args dict — only set when v.mode === 'function'.
|
|
98
|
+
* Values are wire scalars/strings (recursively serialized).
|
|
99
|
+
*/
|
|
100
|
+
declare const CREATED_REF_RE: RegExp;
|
|
101
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for ValueSourcePicker — translate between the picker's
|
|
3
|
+
* structured ValueSourceValue object and the wire format the action
|
|
4
|
+
* engine reads (admin-api/app/actions/refs.py and edits.py).
|
|
5
|
+
*
|
|
6
|
+
* Mode catalog and wire formats — see
|
|
7
|
+
* dev_docs/specs/action-editor-parity-value-source-picker.md §3.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {'literal' | 'parameter' | 'entity-field' | 'user-field'
|
|
12
|
+
* | 'now' | 'source-id' | 'created-field'
|
|
13
|
+
* | 'config-list' | 'function' | 'expression'} ValueSourceMode
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {(
|
|
18
|
+
* { mode: 'literal', value: string | number | boolean | null }
|
|
19
|
+
* | { mode: 'parameter', key: string }
|
|
20
|
+
* | { mode: 'entity-field', field: string }
|
|
21
|
+
* | { mode: 'user-field', key: string }
|
|
22
|
+
* | { mode: 'now' }
|
|
23
|
+
* | { mode: 'source-id' }
|
|
24
|
+
* | { mode: 'created-field', index: number, field: string }
|
|
25
|
+
* | { mode: 'config-list', configType: string }
|
|
26
|
+
* | { mode: 'function', name: string, args: Record<string, ValueSourceValue> }
|
|
27
|
+
* | { mode: 'expression', expr: string }
|
|
28
|
+
* )} ValueSourceValue
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} SerializedValueSource
|
|
33
|
+
* @property {unknown} value
|
|
34
|
+
* The wire scalar/string going into the slot
|
|
35
|
+
* (e.g. ActionEdit.value, ParamValuesSource.config_query.filter[key]).
|
|
36
|
+
* @property {Record<string, unknown> | undefined} [functionArgs]
|
|
37
|
+
* Resolved function args dict — only set when v.mode === 'function'.
|
|
38
|
+
* Values are wire scalars/strings (recursively serialized).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const CREATED_REF_RE = /^\$created\.(\d+)\.(\w+)$/;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert a ValueSourceValue into wire form ready to persist.
|
|
45
|
+
*
|
|
46
|
+
* @param {ValueSourceValue} v
|
|
47
|
+
* @returns {SerializedValueSource}
|
|
48
|
+
*/
|
|
49
|
+
export function serializeValueSource(v) {
|
|
50
|
+
switch (v.mode) {
|
|
51
|
+
case "literal":
|
|
52
|
+
return { value: v.value };
|
|
53
|
+
case "parameter":
|
|
54
|
+
return { value: `$parameters.${v.key}` };
|
|
55
|
+
case "entity-field":
|
|
56
|
+
return { value: `$entity.${v.field}` };
|
|
57
|
+
case "user-field":
|
|
58
|
+
return { value: `$user.${v.key}` };
|
|
59
|
+
case "now":
|
|
60
|
+
return { value: "$now" };
|
|
61
|
+
case "source-id":
|
|
62
|
+
return { value: "$source.id" };
|
|
63
|
+
case "created-field":
|
|
64
|
+
return { value: `$created.${v.index}.${v.field}` };
|
|
65
|
+
case "config-list":
|
|
66
|
+
return { value: `$config.${v.configType}` };
|
|
67
|
+
case "expression":
|
|
68
|
+
return { value: `$expr(${v.expr})` };
|
|
69
|
+
case "function": {
|
|
70
|
+
/** @type {Record<string, unknown>} */
|
|
71
|
+
const functionArgs = {};
|
|
72
|
+
for (const [argKey, argValue] of Object.entries(v.args)) {
|
|
73
|
+
functionArgs[argKey] = serializeValueSource(argValue).value;
|
|
74
|
+
}
|
|
75
|
+
return { value: `$function.${v.name}`, functionArgs };
|
|
76
|
+
}
|
|
77
|
+
default: {
|
|
78
|
+
const exhaustive = /** @type {never} */ (v);
|
|
79
|
+
throw new Error(
|
|
80
|
+
`serializeValueSource: unknown mode ${JSON.stringify(exhaustive)}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert wire form back into a ValueSourceValue.
|
|
88
|
+
* Returns null when value is null/undefined and no functionArgs were supplied.
|
|
89
|
+
*
|
|
90
|
+
* @param {unknown} wire
|
|
91
|
+
* @param {Record<string, unknown> | null | undefined} [functionArgs]
|
|
92
|
+
* @returns {ValueSourceValue | null}
|
|
93
|
+
*/
|
|
94
|
+
export function parseValueSource(wire, functionArgs) {
|
|
95
|
+
if (wire === undefined) {
|
|
96
|
+
// Slot was absent from the stored JSON — picker shows empty.
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if (wire === null) {
|
|
100
|
+
// Engine treats null wire as literal null (refs.py:42 falls through).
|
|
101
|
+
return { mode: "literal", value: null };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof wire !== "string") {
|
|
105
|
+
// Non-string scalar (number / bool) → literal as-is.
|
|
106
|
+
return { mode: "literal", value: /** @type {any} */ (wire) };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Bare $now / $source.id (exact match).
|
|
110
|
+
if (wire === "$now") return { mode: "now" };
|
|
111
|
+
if (wire === "$source.id") return { mode: "source-id" };
|
|
112
|
+
|
|
113
|
+
// Prefixed refs.
|
|
114
|
+
if (wire.startsWith("$parameters.")) {
|
|
115
|
+
return { mode: "parameter", key: wire.slice("$parameters.".length) };
|
|
116
|
+
}
|
|
117
|
+
if (wire.startsWith("$entity.")) {
|
|
118
|
+
return { mode: "entity-field", field: wire.slice("$entity.".length) };
|
|
119
|
+
}
|
|
120
|
+
if (wire.startsWith("$user.")) {
|
|
121
|
+
return { mode: "user-field", key: wire.slice("$user.".length) };
|
|
122
|
+
}
|
|
123
|
+
if (wire.startsWith("$config.")) {
|
|
124
|
+
return { mode: "config-list", configType: wire.slice("$config.".length) };
|
|
125
|
+
}
|
|
126
|
+
if (wire.startsWith("$function.")) {
|
|
127
|
+
const name = wire.slice("$function.".length);
|
|
128
|
+
/** @type {Record<string, ValueSourceValue>} */
|
|
129
|
+
const args = {};
|
|
130
|
+
if (functionArgs) {
|
|
131
|
+
for (const [argKey, argWire] of Object.entries(functionArgs)) {
|
|
132
|
+
const parsed = parseValueSource(argWire);
|
|
133
|
+
if (parsed !== null) {
|
|
134
|
+
args[argKey] = parsed;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { mode: "function", name, args };
|
|
139
|
+
}
|
|
140
|
+
if (wire.startsWith("$expr(") && wire.endsWith(")")) {
|
|
141
|
+
return { mode: "expression", expr: wire.slice("$expr(".length, -1) };
|
|
142
|
+
}
|
|
143
|
+
const createdMatch = CREATED_REF_RE.exec(wire);
|
|
144
|
+
if (createdMatch) {
|
|
145
|
+
return {
|
|
146
|
+
mode: "created-field",
|
|
147
|
+
index: Number.parseInt(createdMatch[1], 10),
|
|
148
|
+
field: createdMatch[2],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// No recognized prefix → literal string. Matches engine's fall-through
|
|
153
|
+
// in refs.py:75 (`return value`).
|
|
154
|
+
return { mode: "literal", value: wire };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export const _internal = { CREATED_REF_RE };
|
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component ValueSourcePicker
|
|
3
|
+
|
|
4
|
+
One picker for every action-engine slot that accepts "literal-or-reference".
|
|
5
|
+
Authoring affordance for action editors (Foundry-style declarative actions):
|
|
6
|
+
every Edit value, Create field, function arg, and config-query filter shares
|
|
7
|
+
this control. Wire format matches admin-api/app/actions/refs.py byte-for-byte.
|
|
8
|
+
|
|
9
|
+
Pair with the helpers shipped alongside (ValueSourcePicker.helpers.js):
|
|
10
|
+
serializeValueSource(v) → { value, functionArgs? }
|
|
11
|
+
parseValueSource(wire, functionArgs?) → ValueSourceValue | null
|
|
12
|
+
|
|
13
|
+
Allowed modes are narrowed per consumer — see the per-consumer matrix in
|
|
14
|
+
the consumer's spec (see issue #28 of the action-editor parity work).
|
|
15
|
+
|
|
16
|
+
@example Edits' value slot
|
|
17
|
+
<ValueSourcePicker
|
|
18
|
+
label="VALUE"
|
|
19
|
+
bind:value={editValue}
|
|
20
|
+
allowed={['literal', 'parameter', 'function', 'now', 'expression']}
|
|
21
|
+
context={{ parameters, functions }}
|
|
22
|
+
expectedType={field.type}
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
@example Function arg (recursive — depth 2)
|
|
26
|
+
<ValueSourcePicker
|
|
27
|
+
label={argName}
|
|
28
|
+
bind:value={argValue}
|
|
29
|
+
allowed={['literal', 'parameter', 'entity-field', 'user-field', 'config-list']}
|
|
30
|
+
context={{ parameters, entitySchema, userFields, configTypes }}
|
|
31
|
+
/>
|
|
32
|
+
-->
|
|
33
|
+
<script module>
|
|
34
|
+
let _vspUid = 0;
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<script>
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {import('./ValueSourcePicker.helpers.js').ValueSourceMode} ValueSourceMode
|
|
40
|
+
* @typedef {import('./ValueSourcePicker.helpers.js').ValueSourceValue} ValueSourceValue
|
|
41
|
+
*
|
|
42
|
+
* @typedef {{ key: string, label: string, type: string }} ParameterField
|
|
43
|
+
* @typedef {{ name: string, label?: string, argSchema: Record<string, SchemaField> }} FunctionDefinition
|
|
44
|
+
* @typedef {{ key: string, label: string, description?: string }} UserField
|
|
45
|
+
* @typedef {{ index: number, entityType: string, fields: string[] }} PriorCreate
|
|
46
|
+
* @typedef {{ code: string, label: string }} ConfigType
|
|
47
|
+
* @typedef {{ type: string, enum?: string[], format?: string, required?: boolean }} SchemaField
|
|
48
|
+
*
|
|
49
|
+
* @typedef {Object} ValueSourceContext
|
|
50
|
+
* @property {ParameterField[]} [parameters]
|
|
51
|
+
* @property {{ type: string, properties: Record<string, SchemaField> }} [entitySchema]
|
|
52
|
+
* @property {UserField[]} [userFields]
|
|
53
|
+
* @property {FunctionDefinition[]} [functions]
|
|
54
|
+
* @property {PriorCreate[]} [priorCreates]
|
|
55
|
+
* @property {ConfigType[]} [configTypes]
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
import Select from "./Select.svelte";
|
|
59
|
+
import Combobox from "./Combobox.svelte";
|
|
60
|
+
import Input from "./Input.svelte";
|
|
61
|
+
import Badge from "./Badge.svelte";
|
|
62
|
+
import Button from "./Button.svelte";
|
|
63
|
+
import Label from "./Label.svelte";
|
|
64
|
+
import ValueSourcePicker from "./ValueSourcePicker.svelte";
|
|
65
|
+
|
|
66
|
+
let {
|
|
67
|
+
/** @type {ValueSourceValue | null} */
|
|
68
|
+
value = $bindable(null),
|
|
69
|
+
/** @type {((next: ValueSourceValue | null) => void) | undefined} */
|
|
70
|
+
onchange = undefined,
|
|
71
|
+
/** @type {ValueSourceMode[]} */
|
|
72
|
+
allowed = [
|
|
73
|
+
"literal",
|
|
74
|
+
"parameter",
|
|
75
|
+
"entity-field",
|
|
76
|
+
"user-field",
|
|
77
|
+
"now",
|
|
78
|
+
"source-id",
|
|
79
|
+
"created-field",
|
|
80
|
+
"config-list",
|
|
81
|
+
"function",
|
|
82
|
+
"expression",
|
|
83
|
+
],
|
|
84
|
+
/** @type {ValueSourceContext} */
|
|
85
|
+
context = {},
|
|
86
|
+
/** @type {string | undefined} */
|
|
87
|
+
expectedType = undefined,
|
|
88
|
+
/** @type {string | undefined} */
|
|
89
|
+
label = undefined,
|
|
90
|
+
/** @type {boolean} */
|
|
91
|
+
required = false,
|
|
92
|
+
/** @type {boolean} */
|
|
93
|
+
disabled = false,
|
|
94
|
+
/** @type {string | undefined} */
|
|
95
|
+
id = undefined,
|
|
96
|
+
/** @type {string} */
|
|
97
|
+
class: className = "",
|
|
98
|
+
...rest
|
|
99
|
+
} = $props();
|
|
100
|
+
|
|
101
|
+
const uid = `vsp-${_vspUid++}`;
|
|
102
|
+
const groupId = $derived(id ?? uid);
|
|
103
|
+
|
|
104
|
+
const MODE_LABEL = /** @type {Record<ValueSourceMode, string>} */ ({
|
|
105
|
+
literal: "Literal",
|
|
106
|
+
parameter: "Parameter",
|
|
107
|
+
"entity-field": "Entity field",
|
|
108
|
+
"user-field": "User field",
|
|
109
|
+
now: "Current time",
|
|
110
|
+
"source-id": "Source entity id",
|
|
111
|
+
"created-field": "Prior-created entity",
|
|
112
|
+
"config-list": "Config rows",
|
|
113
|
+
function: "Function",
|
|
114
|
+
expression: "Expression",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const MODE_DESCRIPTION = /** @type {Record<ValueSourceMode, string>} */ ({
|
|
118
|
+
literal: "A typed-in value",
|
|
119
|
+
parameter: "Value of an action input",
|
|
120
|
+
"entity-field": "A field of the source entity",
|
|
121
|
+
"user-field": "A claim from the caller's JWT",
|
|
122
|
+
now: "ISO 8601 UTC timestamp at execution time",
|
|
123
|
+
"source-id": "ID of the entity being acted upon",
|
|
124
|
+
"created-field": "Field of an entity created earlier in this action",
|
|
125
|
+
"config-list": "List of org-scoped config rows",
|
|
126
|
+
function: "Result of a registered action function",
|
|
127
|
+
expression: "Inline arithmetic over $entity / $parameters / $user",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const modeOptions = $derived(
|
|
131
|
+
allowed.map((mode) => ({ value: mode, label: MODE_LABEL[mode] })),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const currentMode = $derived(value?.mode ?? null);
|
|
135
|
+
|
|
136
|
+
const isStoredModeForbidden = $derived(
|
|
137
|
+
currentMode !== null && !allowed.includes(currentMode),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
function emit(next) {
|
|
141
|
+
value = next;
|
|
142
|
+
onchange?.(next);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function pickMode(/** @type {string} */ next) {
|
|
146
|
+
const mode = /** @type {ValueSourceMode} */ (next);
|
|
147
|
+
if (mode === currentMode) return;
|
|
148
|
+
emit(makeBlankForMode(mode));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @returns {ValueSourceValue} */
|
|
152
|
+
function makeBlankForMode(/** @type {ValueSourceMode} */ mode) {
|
|
153
|
+
switch (mode) {
|
|
154
|
+
case "literal":
|
|
155
|
+
return { mode, value: "" };
|
|
156
|
+
case "parameter":
|
|
157
|
+
return { mode, key: "" };
|
|
158
|
+
case "entity-field":
|
|
159
|
+
return { mode, field: "" };
|
|
160
|
+
case "user-field":
|
|
161
|
+
return { mode, key: "" };
|
|
162
|
+
case "now":
|
|
163
|
+
return { mode };
|
|
164
|
+
case "source-id":
|
|
165
|
+
return { mode };
|
|
166
|
+
case "created-field":
|
|
167
|
+
return { mode, index: 0, field: "id" };
|
|
168
|
+
case "config-list":
|
|
169
|
+
return { mode, configType: "" };
|
|
170
|
+
case "function":
|
|
171
|
+
return { mode, name: "", args: {} };
|
|
172
|
+
case "expression":
|
|
173
|
+
return { mode, expr: "" };
|
|
174
|
+
default: {
|
|
175
|
+
const exhaustive = /** @type {never} */ (mode);
|
|
176
|
+
throw new Error(`makeBlankForMode: ${String(exhaustive)}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function clear() {
|
|
182
|
+
emit(null);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ----------- per-mode helpers -----------
|
|
186
|
+
|
|
187
|
+
function paramOptions() {
|
|
188
|
+
return (context.parameters ?? []).map((p) => ({
|
|
189
|
+
value: p.key,
|
|
190
|
+
label: `${p.label || p.key}`,
|
|
191
|
+
description: p.type,
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
function entityFieldOptions() {
|
|
195
|
+
const props = context.entitySchema?.properties ?? {};
|
|
196
|
+
return Object.entries(props).map(([key, schema]) => ({
|
|
197
|
+
value: key,
|
|
198
|
+
label: key,
|
|
199
|
+
description: schema.type,
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
function userFieldOptions() {
|
|
203
|
+
return (context.userFields ?? []).map((u) => ({
|
|
204
|
+
value: u.key,
|
|
205
|
+
label: u.label,
|
|
206
|
+
description: u.description,
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
function functionOptions() {
|
|
210
|
+
return (context.functions ?? []).map((fn) => ({
|
|
211
|
+
value: fn.name,
|
|
212
|
+
label: fn.label || fn.name,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
function priorCreateOptions() {
|
|
216
|
+
return (context.priorCreates ?? []).map((c) => ({
|
|
217
|
+
value: String(c.index),
|
|
218
|
+
label: `${c.index}: ${c.entityType}`,
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
function priorCreateFieldOptions() {
|
|
222
|
+
if (value?.mode !== "created-field") return [];
|
|
223
|
+
const c = (context.priorCreates ?? []).find(
|
|
224
|
+
(p) => p.index === value.index,
|
|
225
|
+
);
|
|
226
|
+
return (c?.fields ?? ["id"]).map((f) => ({ value: f, label: f }));
|
|
227
|
+
}
|
|
228
|
+
function configTypeOptions() {
|
|
229
|
+
return (context.configTypes ?? []).map((c) => ({
|
|
230
|
+
value: c.code,
|
|
231
|
+
label: c.label || c.code,
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// dangling-ref detector — true when the stored ref names something not in context
|
|
236
|
+
const isDangling = $derived.by(() => {
|
|
237
|
+
if (!value) return false;
|
|
238
|
+
switch (value.mode) {
|
|
239
|
+
case "parameter":
|
|
240
|
+
return (
|
|
241
|
+
value.key !== "" &&
|
|
242
|
+
!(context.parameters ?? []).some((p) => p.key === value.key)
|
|
243
|
+
);
|
|
244
|
+
case "entity-field":
|
|
245
|
+
return (
|
|
246
|
+
value.field !== "" &&
|
|
247
|
+
!((context.entitySchema?.properties ?? {})[value.field])
|
|
248
|
+
);
|
|
249
|
+
case "user-field":
|
|
250
|
+
return (
|
|
251
|
+
value.key !== "" &&
|
|
252
|
+
!(context.userFields ?? []).some((u) => u.key === value.key)
|
|
253
|
+
);
|
|
254
|
+
case "function":
|
|
255
|
+
return (
|
|
256
|
+
value.name !== "" &&
|
|
257
|
+
!(context.functions ?? []).some((fn) => fn.name === value.name)
|
|
258
|
+
);
|
|
259
|
+
case "config-list":
|
|
260
|
+
return (
|
|
261
|
+
value.configType !== "" &&
|
|
262
|
+
!(context.configTypes ?? []).some((c) => c.code === value.configType)
|
|
263
|
+
);
|
|
264
|
+
case "created-field":
|
|
265
|
+
return !((context.priorCreates ?? []).some((p) => p.index === value.index));
|
|
266
|
+
default:
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// type-mismatch detector — yellow soft warn (never blocks submit)
|
|
272
|
+
const typeMismatch = $derived.by(() => {
|
|
273
|
+
if (!expectedType || !value) return false;
|
|
274
|
+
switch (value.mode) {
|
|
275
|
+
case "parameter": {
|
|
276
|
+
const p = (context.parameters ?? []).find((pp) => pp.key === value.key);
|
|
277
|
+
return p ? !typesCompatible(p.type, expectedType) : false;
|
|
278
|
+
}
|
|
279
|
+
case "entity-field": {
|
|
280
|
+
const f = (context.entitySchema?.properties ?? {})[value.field];
|
|
281
|
+
return f ? !typesCompatible(f.type, expectedType) : false;
|
|
282
|
+
}
|
|
283
|
+
case "now":
|
|
284
|
+
return !typesCompatible("datetime", expectedType);
|
|
285
|
+
case "config-list":
|
|
286
|
+
return !typesCompatible("list", expectedType);
|
|
287
|
+
default:
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
function typesCompatible(/** @type {string} */ a, /** @type {string} */ b) {
|
|
293
|
+
if (a === b) return true;
|
|
294
|
+
// engine coerces freely between string ↔ number / datetime;
|
|
295
|
+
// only flag obvious mismatches.
|
|
296
|
+
const numericLike = new Set(["int", "float", "number"]);
|
|
297
|
+
if (numericLike.has(a) && numericLike.has(b)) return true;
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// function-arg child editor — each arg is itself a ValueSourcePicker.
|
|
302
|
+
function updateFunctionArg(/** @type {string} */ key, /** @type {ValueSourceValue | null} */ next) {
|
|
303
|
+
if (value?.mode !== "function") return;
|
|
304
|
+
const args = { ...value.args };
|
|
305
|
+
if (next === null) delete args[key];
|
|
306
|
+
else args[key] = next;
|
|
307
|
+
emit({ ...value, args });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function functionArgAllowed() {
|
|
311
|
+
// Args use the four context-ref modes plus literal and config-list.
|
|
312
|
+
// Function-as-arg is forbidden by the engine's resolver layering — depth ≤ 2.
|
|
313
|
+
return /** @type {ValueSourceMode[]} */ ([
|
|
314
|
+
"literal",
|
|
315
|
+
"parameter",
|
|
316
|
+
"entity-field",
|
|
317
|
+
"user-field",
|
|
318
|
+
"config-list",
|
|
319
|
+
]);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function functionDef() {
|
|
323
|
+
if (value?.mode !== "function") return null;
|
|
324
|
+
return (context.functions ?? []).find((fn) => fn.name === value.name) ?? null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function literalPlaceholder(/** @type {string | undefined} */ t) {
|
|
328
|
+
switch (t) {
|
|
329
|
+
case "int":
|
|
330
|
+
case "float":
|
|
331
|
+
case "number":
|
|
332
|
+
return "0";
|
|
333
|
+
case "bool":
|
|
334
|
+
return "true";
|
|
335
|
+
case "datetime":
|
|
336
|
+
return "2026-01-01T00:00:00Z";
|
|
337
|
+
case "uuid":
|
|
338
|
+
return "00000000-0000-0000-0000-000000000000";
|
|
339
|
+
default:
|
|
340
|
+
return "Type a value";
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function coerceLiteral(/** @type {string} */ raw, /** @type {string | undefined} */ t) {
|
|
345
|
+
switch (t) {
|
|
346
|
+
case "int":
|
|
347
|
+
return raw === "" ? null : Number.parseInt(raw, 10);
|
|
348
|
+
case "float":
|
|
349
|
+
case "number":
|
|
350
|
+
return raw === "" ? null : Number.parseFloat(raw);
|
|
351
|
+
case "bool":
|
|
352
|
+
return raw === "true";
|
|
353
|
+
default:
|
|
354
|
+
return raw;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
</script>
|
|
358
|
+
|
|
359
|
+
<div
|
|
360
|
+
class="vsp {className}"
|
|
361
|
+
class:vsp-disabled={disabled}
|
|
362
|
+
role="group"
|
|
363
|
+
aria-labelledby={label ? `${groupId}-label` : undefined}
|
|
364
|
+
{...rest}
|
|
365
|
+
>
|
|
366
|
+
{#if label}
|
|
367
|
+
<Label for={`${groupId}-mode`} id={`${groupId}-label`}>
|
|
368
|
+
{label}{#if required}<span class="vsp-required" aria-hidden="true">*</span>{/if}
|
|
369
|
+
</Label>
|
|
370
|
+
{/if}
|
|
371
|
+
|
|
372
|
+
<div class="vsp-row">
|
|
373
|
+
<div class="vsp-mode">
|
|
374
|
+
<Select
|
|
375
|
+
id={`${groupId}-mode`}
|
|
376
|
+
size="sm"
|
|
377
|
+
placeholder={value === null ? "Pick a source" : undefined}
|
|
378
|
+
value={currentMode ?? ""}
|
|
379
|
+
options={modeOptions}
|
|
380
|
+
onchange={pickMode}
|
|
381
|
+
{disabled}
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<div class="vsp-detail">
|
|
386
|
+
{#if isStoredModeForbidden && value}
|
|
387
|
+
<div class="vsp-error-row">
|
|
388
|
+
<Badge variant="error">no longer allowed: {MODE_LABEL[value.mode]}</Badge>
|
|
389
|
+
<Button variant="ghost" size="sm" onclick={clear}>Clear</Button>
|
|
390
|
+
</div>
|
|
391
|
+
{:else if value === null}
|
|
392
|
+
<span class="vsp-placeholder">{MODE_DESCRIPTION[allowed[0]] ?? ""}</span>
|
|
393
|
+
{:else if value.mode === "literal"}
|
|
394
|
+
<Input
|
|
395
|
+
size="sm"
|
|
396
|
+
placeholder={literalPlaceholder(expectedType)}
|
|
397
|
+
value={String(value.value ?? "")}
|
|
398
|
+
oninput={(e) => emit({ mode: "literal", value: coerceLiteral(/** @type {HTMLInputElement} */ (e.target).value, expectedType) })}
|
|
399
|
+
{disabled}
|
|
400
|
+
/>
|
|
401
|
+
{:else if value.mode === "parameter"}
|
|
402
|
+
<Combobox
|
|
403
|
+
size="sm"
|
|
404
|
+
placeholder="Pick a parameter"
|
|
405
|
+
items={paramOptions()}
|
|
406
|
+
value={value.key}
|
|
407
|
+
onchange={(k) => emit({ mode: "parameter", key: k })}
|
|
408
|
+
{disabled}
|
|
409
|
+
/>
|
|
410
|
+
{:else if value.mode === "entity-field"}
|
|
411
|
+
<Combobox
|
|
412
|
+
size="sm"
|
|
413
|
+
placeholder="Pick a field"
|
|
414
|
+
items={entityFieldOptions()}
|
|
415
|
+
value={value.field}
|
|
416
|
+
onchange={(f) => emit({ mode: "entity-field", field: f })}
|
|
417
|
+
{disabled}
|
|
418
|
+
/>
|
|
419
|
+
{:else if value.mode === "user-field"}
|
|
420
|
+
<Combobox
|
|
421
|
+
size="sm"
|
|
422
|
+
placeholder="Pick a user claim"
|
|
423
|
+
items={userFieldOptions()}
|
|
424
|
+
value={value.key}
|
|
425
|
+
onchange={(k) => emit({ mode: "user-field", key: k })}
|
|
426
|
+
{disabled}
|
|
427
|
+
/>
|
|
428
|
+
{:else if value.mode === "now"}
|
|
429
|
+
<Badge variant="info">$now — current UTC timestamp</Badge>
|
|
430
|
+
{:else if value.mode === "source-id"}
|
|
431
|
+
<Badge variant="info">$source.id — id of the entity being acted upon</Badge>
|
|
432
|
+
{:else if value.mode === "config-list"}
|
|
433
|
+
<Combobox
|
|
434
|
+
size="sm"
|
|
435
|
+
placeholder="Pick a config type"
|
|
436
|
+
items={configTypeOptions()}
|
|
437
|
+
value={value.configType}
|
|
438
|
+
onchange={(c) => emit({ mode: "config-list", configType: c })}
|
|
439
|
+
{disabled}
|
|
440
|
+
/>
|
|
441
|
+
<Badge variant="neutral">list</Badge>
|
|
442
|
+
{:else if value.mode === "expression"}
|
|
443
|
+
<Input
|
|
444
|
+
size="sm"
|
|
445
|
+
placeholder="$entity.X + 1"
|
|
446
|
+
value={value.expr}
|
|
447
|
+
oninput={(e) => emit({ mode: "expression", expr: /** @type {HTMLInputElement} */ (e.target).value })}
|
|
448
|
+
{disabled}
|
|
449
|
+
/>
|
|
450
|
+
{:else if value.mode === "created-field"}
|
|
451
|
+
<div class="vsp-inline">
|
|
452
|
+
<Combobox
|
|
453
|
+
size="sm"
|
|
454
|
+
placeholder="prior create"
|
|
455
|
+
items={priorCreateOptions()}
|
|
456
|
+
value={String(value.index)}
|
|
457
|
+
onchange={(i) => emit({ mode: "created-field", index: Number(i), field: value.field })}
|
|
458
|
+
{disabled}
|
|
459
|
+
/>
|
|
460
|
+
<Combobox
|
|
461
|
+
size="sm"
|
|
462
|
+
placeholder="field"
|
|
463
|
+
items={priorCreateFieldOptions()}
|
|
464
|
+
value={value.field}
|
|
465
|
+
onchange={(f) => emit({ mode: "created-field", index: value.index, field: f })}
|
|
466
|
+
{disabled}
|
|
467
|
+
/>
|
|
468
|
+
</div>
|
|
469
|
+
{:else if value.mode === "function"}
|
|
470
|
+
<Combobox
|
|
471
|
+
size="sm"
|
|
472
|
+
placeholder="Pick a function"
|
|
473
|
+
items={functionOptions()}
|
|
474
|
+
value={value.name}
|
|
475
|
+
onchange={(n) => emit({ mode: "function", name: n, args: {} })}
|
|
476
|
+
{disabled}
|
|
477
|
+
/>
|
|
478
|
+
{/if}
|
|
479
|
+
|
|
480
|
+
{#if value && !isStoredModeForbidden}
|
|
481
|
+
{#if isDangling}
|
|
482
|
+
<Badge variant="error">removed from context</Badge>
|
|
483
|
+
<Button variant="ghost" size="sm" onclick={clear}>Clear</Button>
|
|
484
|
+
{:else if typeMismatch}
|
|
485
|
+
<Badge variant="warning">type mismatch · expected {expectedType}</Badge>
|
|
486
|
+
{/if}
|
|
487
|
+
{/if}
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
491
|
+
{#if value?.mode === "function" && value.name}
|
|
492
|
+
{@const fn = functionDef()}
|
|
493
|
+
{#if fn}
|
|
494
|
+
<div class="vsp-args" role="group" aria-label={`${value.name} args`}>
|
|
495
|
+
<span class="vsp-args-title">Args</span>
|
|
496
|
+
{#each Object.entries(fn.argSchema) as [argKey, argSchema]}
|
|
497
|
+
<ValueSourcePicker
|
|
498
|
+
label={argKey}
|
|
499
|
+
value={value.args[argKey] ?? null}
|
|
500
|
+
allowed={functionArgAllowed()}
|
|
501
|
+
{context}
|
|
502
|
+
expectedType={argSchema.type}
|
|
503
|
+
onchange={(next) => updateFunctionArg(argKey, next)}
|
|
504
|
+
{disabled}
|
|
505
|
+
/>
|
|
506
|
+
{/each}
|
|
507
|
+
</div>
|
|
508
|
+
{:else}
|
|
509
|
+
<Badge variant="error">function "{value.name}" not in registry</Badge>
|
|
510
|
+
{/if}
|
|
511
|
+
{/if}
|
|
512
|
+
</div>
|
|
513
|
+
|
|
514
|
+
<style>
|
|
515
|
+
.vsp {
|
|
516
|
+
display: flex;
|
|
517
|
+
flex-direction: column;
|
|
518
|
+
gap: var(--space-xs);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.vsp-required {
|
|
522
|
+
color: var(--color-text-error);
|
|
523
|
+
margin-inline-start: var(--space-3xs);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.vsp-row {
|
|
527
|
+
display: grid;
|
|
528
|
+
grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
|
|
529
|
+
gap: var(--space-sm);
|
|
530
|
+
align-items: center;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.vsp-mode {
|
|
534
|
+
min-width: 0;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.vsp-detail {
|
|
538
|
+
display: flex;
|
|
539
|
+
flex-wrap: wrap;
|
|
540
|
+
gap: var(--space-2xs);
|
|
541
|
+
align-items: center;
|
|
542
|
+
min-width: 0;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.vsp-detail :global(.input-group) {
|
|
546
|
+
flex: 1 1 auto;
|
|
547
|
+
min-width: 0;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.vsp-error-row {
|
|
551
|
+
display: flex;
|
|
552
|
+
gap: var(--space-2xs);
|
|
553
|
+
align-items: center;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.vsp-placeholder {
|
|
557
|
+
color: var(--color-text-muted);
|
|
558
|
+
font-family: var(--type-body-font);
|
|
559
|
+
font-size: var(--type-body-size);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.vsp-inline {
|
|
563
|
+
display: flex;
|
|
564
|
+
gap: var(--space-2xs);
|
|
565
|
+
flex: 1 1 auto;
|
|
566
|
+
min-width: 0;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.vsp-args {
|
|
570
|
+
margin-inline-start: var(--space-md);
|
|
571
|
+
padding: var(--space-sm);
|
|
572
|
+
border: var(--border-width) solid var(--color-border-subtle);
|
|
573
|
+
border-radius: var(--radius-md);
|
|
574
|
+
background: var(--color-surface-secondary);
|
|
575
|
+
display: flex;
|
|
576
|
+
flex-direction: column;
|
|
577
|
+
gap: var(--space-sm);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.vsp-args-title {
|
|
581
|
+
font-family: var(--type-label-font);
|
|
582
|
+
font-size: var(--type-label-size);
|
|
583
|
+
letter-spacing: var(--type-label-tracking);
|
|
584
|
+
text-transform: uppercase;
|
|
585
|
+
color: var(--color-text-muted);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.vsp-disabled {
|
|
589
|
+
opacity: 0.6;
|
|
590
|
+
pointer-events: none;
|
|
591
|
+
}
|
|
592
|
+
</style>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export default ValueSourcePicker;
|
|
2
|
+
export type ValueSourceMode = import("./ValueSourcePicker.helpers.js").ValueSourceMode;
|
|
3
|
+
export type ValueSourceValue = import("./ValueSourcePicker.helpers.js").ValueSourceValue;
|
|
4
|
+
export type ParameterField = {
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
type: string;
|
|
8
|
+
};
|
|
9
|
+
export type FunctionDefinition = {
|
|
10
|
+
name: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
argSchema: Record<string, SchemaField>;
|
|
13
|
+
};
|
|
14
|
+
export type UserField = {
|
|
15
|
+
key: string;
|
|
16
|
+
label: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
};
|
|
19
|
+
export type PriorCreate = {
|
|
20
|
+
index: number;
|
|
21
|
+
entityType: string;
|
|
22
|
+
fields: string[];
|
|
23
|
+
};
|
|
24
|
+
export type ConfigType = {
|
|
25
|
+
code: string;
|
|
26
|
+
label: string;
|
|
27
|
+
};
|
|
28
|
+
export type SchemaField = {
|
|
29
|
+
type: string;
|
|
30
|
+
enum?: string[];
|
|
31
|
+
format?: string;
|
|
32
|
+
required?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type ValueSourceContext = {
|
|
35
|
+
parameters?: ParameterField[];
|
|
36
|
+
entitySchema?: {
|
|
37
|
+
type: string;
|
|
38
|
+
properties: Record<string, SchemaField>;
|
|
39
|
+
};
|
|
40
|
+
userFields?: UserField[];
|
|
41
|
+
functions?: FunctionDefinition[];
|
|
42
|
+
priorCreates?: PriorCreate[];
|
|
43
|
+
configTypes?: ConfigType[];
|
|
44
|
+
};
|
|
45
|
+
type ValueSourcePicker = {
|
|
46
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
47
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* ValueSourcePicker
|
|
51
|
+
*
|
|
52
|
+
* One picker for every action-engine slot that accepts "literal-or-reference".
|
|
53
|
+
* Authoring affordance for action editors (Foundry-style declarative actions):
|
|
54
|
+
* every Edit value, Create field, function arg, and config-query filter shares
|
|
55
|
+
* this control. Wire format matches admin-api/app/actions/refs.py byte-for-byte.
|
|
56
|
+
*
|
|
57
|
+
* Pair with the helpers shipped alongside (ValueSourcePicker.helpers.js):
|
|
58
|
+
* serializeValueSource(v) → { value, functionArgs? }
|
|
59
|
+
* parseValueSource(wire, functionArgs?) → ValueSourceValue | null
|
|
60
|
+
*
|
|
61
|
+
* Allowed modes are narrowed per consumer — see the per-consumer matrix in
|
|
62
|
+
* the consumer's spec (see issue #28 of the action-editor parity work).
|
|
63
|
+
*
|
|
64
|
+
* @example Edits' value slot
|
|
65
|
+
* <ValueSourcePicker
|
|
66
|
+
* label="VALUE"
|
|
67
|
+
* bind:value={editValue}
|
|
68
|
+
* allowed={['literal', 'parameter', 'function', 'now', 'expression']}
|
|
69
|
+
* context={{ parameters, functions }}
|
|
70
|
+
* expectedType={field.type}
|
|
71
|
+
* />
|
|
72
|
+
*
|
|
73
|
+
* @example Function arg (recursive — depth 2)
|
|
74
|
+
* <ValueSourcePicker
|
|
75
|
+
* label={argName}
|
|
76
|
+
* bind:value={argValue}
|
|
77
|
+
* allowed={['literal', 'parameter', 'entity-field', 'user-field', 'config-list']}
|
|
78
|
+
* context={{ parameters, entitySchema, userFields, configTypes }}
|
|
79
|
+
* />
|
|
80
|
+
*/
|
|
81
|
+
declare const ValueSourcePicker: import("svelte").Component<{
|
|
82
|
+
value?: any;
|
|
83
|
+
onchange?: any;
|
|
84
|
+
allowed?: any[];
|
|
85
|
+
context?: Record<string, any>;
|
|
86
|
+
expectedType?: any;
|
|
87
|
+
label?: any;
|
|
88
|
+
required?: boolean;
|
|
89
|
+
disabled?: boolean;
|
|
90
|
+
id?: any;
|
|
91
|
+
class?: string;
|
|
92
|
+
} & Record<string, any>, {}, "value">;
|
|
93
|
+
import ValueSourcePicker from "./ValueSourcePicker.svelte";
|
|
94
|
+
type $$ComponentProps = {
|
|
95
|
+
value?: any;
|
|
96
|
+
onchange?: any;
|
|
97
|
+
allowed?: any[];
|
|
98
|
+
context?: Record<string, any>;
|
|
99
|
+
expectedType?: any;
|
|
100
|
+
label?: any;
|
|
101
|
+
required?: boolean;
|
|
102
|
+
disabled?: boolean;
|
|
103
|
+
id?: any;
|
|
104
|
+
class?: string;
|
|
105
|
+
} & Record<string, any>;
|
package/components/index.js
CHANGED
|
@@ -76,6 +76,11 @@ export { default as BottomNavItem } from "./BottomNavItem.svelte";
|
|
|
76
76
|
|
|
77
77
|
// Complex
|
|
78
78
|
export { default as Stepper } from "./Stepper.svelte";
|
|
79
|
+
export { default as ValueSourcePicker } from "./ValueSourcePicker.svelte";
|
|
80
|
+
export {
|
|
81
|
+
serializeValueSource,
|
|
82
|
+
parseValueSource,
|
|
83
|
+
} from "./ValueSourcePicker.helpers.js";
|
|
79
84
|
export { default as CodeBlock } from "./CodeBlock.svelte";
|
|
80
85
|
export { default as CodeEditor } from "./CodeEditor.svelte";
|
|
81
86
|
export { default as CollapsibleSection } from "./CollapsibleSection.svelte";
|