@dreamboard-games/ui-sdk 0.0.42 → 0.0.45
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/ActionButton.d.ts.map +1 -1
- package/dist/components/ActionButton.js +2 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.d.ts.map +1 -1
- package/dist/components/DiceRoller.d.ts +3 -2
- package/dist/components/DiceRoller.d.ts.map +1 -1
- package/dist/components/DiceRoller.js +4 -13
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/ErrorBoundary.js +94 -2
- package/dist/components/InteractionForm.d.ts +1 -1
- package/dist/components/InteractionForm.d.ts.map +1 -1
- package/dist/components/InteractionForm.js +29 -15
- package/dist/components/PrimaryActionButton.d.ts.map +1 -1
- package/dist/components/PrimaryActionButton.js +7 -6
- package/dist/components/ResourceCounter.d.ts +59 -25
- package/dist/components/ResourceCounter.d.ts.map +1 -1
- package/dist/components/ResourceCounter.js +106 -115
- package/dist/components/Toast.d.ts +13 -6
- package/dist/components/Toast.d.ts.map +1 -1
- package/dist/components/Toast.js +10 -5
- package/dist/components/board/HexGrid.js +6 -6
- package/dist/components/board/target-layer.d.ts +18 -2
- package/dist/components/board/target-layer.d.ts.map +1 -1
- package/dist/components/board/target-layer.js +20 -3
- package/dist/components/index.d.ts +3 -4
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -4
- package/dist/components/surfaces/InboxSurface.d.ts.map +1 -1
- package/dist/components/surfaces/InboxSurface.js +2 -6
- package/dist/components/surfaces/PlayerCardsSurface.js +2 -2
- package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts +7 -0
- package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts.map +1 -0
- package/dist/components/surfaces/internal/CardZoneRoutedForm.js +9 -0
- package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts.map +1 -1
- package/dist/components/surfaces/internal/DefaultInteractionButton.js +5 -8
- package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts +2 -2
- package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts.map +1 -1
- package/dist/components/surfaces/internal/useCardZoneInteractions.js +19 -43
- package/dist/context/InteractionDraftContext.d.ts +11 -2
- package/dist/context/InteractionDraftContext.d.ts.map +1 -1
- package/dist/context/InteractionDraftContext.js +41 -4
- package/dist/defaults/components.d.ts +0 -5
- package/dist/defaults/components.d.ts.map +1 -1
- package/dist/defaults/components.js +7 -11
- package/dist/hooks/useBoardInteractions.d.ts +35 -12
- package/dist/hooks/useBoardInteractions.d.ts.map +1 -1
- package/dist/hooks/useBoardInteractions.js +186 -82
- package/dist/hooks/useInteractionHandle.d.ts +1 -1
- package/dist/hooks/useInteractionHandle.d.ts.map +1 -1
- package/dist/hooks/useInteractionHandle.js +12 -27
- package/dist/index.d.ts +11 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -14
- package/dist/primitives/board.d.ts +53 -3
- package/dist/primitives/board.d.ts.map +1 -1
- package/dist/primitives/board.js +65 -41
- package/dist/primitives/dialog-lifecycle.d.ts +17 -0
- package/dist/primitives/dialog-lifecycle.d.ts.map +1 -0
- package/dist/primitives/dialog-lifecycle.js +24 -0
- package/dist/primitives/dice.d.ts +31 -0
- package/dist/primitives/dice.d.ts.map +1 -0
- package/dist/primitives/dice.js +33 -0
- package/dist/primitives/game.d.ts +55 -0
- package/dist/primitives/game.d.ts.map +1 -0
- package/dist/primitives/game.js +101 -0
- package/dist/primitives/index.d.ts +7 -4
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +7 -4
- package/dist/primitives/interaction-form-binding.d.ts +12 -0
- package/dist/primitives/interaction-form-binding.d.ts.map +1 -0
- package/dist/primitives/interaction-form-binding.js +14 -0
- package/dist/primitives/interaction-submit.d.ts +23 -0
- package/dist/primitives/interaction-submit.d.ts.map +1 -0
- package/dist/primitives/interaction-submit.js +41 -0
- package/dist/primitives/interaction.d.ts +76 -6
- package/dist/primitives/interaction.d.ts.map +1 -1
- package/dist/primitives/interaction.js +210 -26
- package/dist/primitives/player-roster.d.ts +2 -1
- package/dist/primitives/player-roster.d.ts.map +1 -1
- package/dist/primitives/prompt.d.ts +36 -11
- package/dist/primitives/prompt.d.ts.map +1 -1
- package/dist/primitives/prompt.js +29 -17
- package/dist/primitives/ui.d.ts +9 -0
- package/dist/primitives/ui.d.ts.map +1 -0
- package/dist/primitives/ui.js +7 -0
- package/dist/primitives/zone.d.ts +111 -5
- package/dist/primitives/zone.d.ts.map +1 -1
- package/dist/primitives/zone.js +349 -9
- package/dist/reducer.d.ts +2 -14
- package/dist/reducer.d.ts.map +1 -1
- package/dist/reducer.js +1 -14
- package/dist/runtime/createPluginRuntimeAPI.js +1 -1
- package/dist/types/hex-color.d.ts +7 -0
- package/dist/types/hex-color.d.ts.map +1 -0
- package/dist/types/hex-color.js +13 -0
- package/dist/types/player-state.d.ts +28 -14
- package/dist/types/player-state.d.ts.map +1 -1
- package/dist/types/plugin-state.d.ts +9 -3
- package/dist/types/plugin-state.d.ts.map +1 -1
- package/dist/ui-contract.d.ts +119 -14
- package/dist/ui-contract.d.ts.map +1 -1
- package/dist/ui-contract.js +4 -3
- package/dist/ui-sdk.d.ts +1637 -1245
- package/dist/utils/interaction-inputs.d.ts +8 -5
- package/dist/utils/interaction-inputs.d.ts.map +1 -1
- package/dist/utils/interaction-inputs.js +82 -14
- package/dist/utils/interaction-router.d.ts +31 -0
- package/dist/utils/interaction-router.d.ts.map +1 -0
- package/dist/utils/interaction-router.js +114 -0
- package/package.json +2 -2
- package/src/components/ActionButton.tsx +2 -1
- package/src/components/Card.tsx +1 -1
- package/src/components/DiceRoller.tsx +13 -22
- package/src/components/ErrorBoundary.test.tsx +19 -0
- package/src/components/ErrorBoundary.tsx +113 -24
- package/src/components/InteractionForm.test.tsx +24 -0
- package/src/components/InteractionForm.tsx +48 -23
- package/src/components/PrimaryActionButton.tsx +19 -5
- package/src/components/ResourceCounter.test.tsx +13 -13
- package/src/components/ResourceCounter.tsx +238 -244
- package/src/components/Toast.tsx +23 -10
- package/src/components/__fixtures__/ResourceCounter.fixture.tsx +70 -169
- package/src/components/board/HexGrid.tsx +6 -6
- package/src/components/board/target-layer.ts +44 -5
- package/src/components/index.ts +17 -10
- package/src/components/surfaces/InboxSurface.tsx +7 -5
- package/src/components/surfaces/PlayerCardsSurface.tsx +6 -6
- package/src/components/surfaces/internal/CardZoneRoutedForm.tsx +35 -0
- package/src/components/surfaces/internal/DefaultInteractionButton.tsx +17 -7
- package/src/components/surfaces/internal/useCardZoneInteractions.ts +25 -67
- package/src/context/InteractionDraftContext.tsx +51 -5
- package/src/defaults/components.tsx +12 -50
- package/src/defaults/defaults.test.tsx +1 -50
- package/src/hooks/useBoardInteractions.test.tsx +240 -17
- package/src/hooks/useBoardInteractions.ts +330 -105
- package/src/hooks/useInteractionHandle.ts +23 -28
- package/src/index.test.ts +60 -40
- package/src/index.ts +30 -36
- package/src/primitives/board.test.tsx +73 -0
- package/src/primitives/board.tsx +191 -40
- package/src/primitives/dialog-lifecycle.ts +58 -0
- package/src/primitives/dice.test.tsx +47 -0
- package/src/primitives/dice.tsx +79 -0
- package/src/primitives/game.test.tsx +98 -0
- package/src/primitives/game.tsx +213 -0
- package/src/primitives/index.ts +84 -0
- package/src/primitives/interaction-form-binding.tsx +56 -0
- package/src/primitives/interaction-submit.ts +90 -0
- package/src/primitives/interaction.test.tsx +396 -0
- package/src/primitives/interaction.tsx +451 -31
- package/src/primitives/player-roster.tsx +2 -1
- package/src/primitives/prompt.test.tsx +94 -3
- package/src/primitives/prompt.tsx +87 -48
- package/src/primitives/ui.test.tsx +131 -0
- package/src/primitives/ui.tsx +13 -0
- package/src/primitives/zone.test.tsx +305 -0
- package/src/primitives/zone.tsx +660 -12
- package/src/reducer.ts +7 -20
- package/src/runtime/createPluginRuntimeAPI.ts +1 -1
- package/src/types/hex-color.ts +20 -0
- package/src/types/player-state.ts +36 -18
- package/src/types/plugin-state.ts +10 -3
- package/src/ui-contract.ts +253 -21
- package/src/utils/interaction-inputs.test.ts +400 -0
- package/src/utils/interaction-inputs.ts +113 -11
- package/src/utils/interaction-router.ts +200 -0
|
@@ -2,6 +2,9 @@ import type { InteractionDescriptor, InteractionInputDescriptor, InputSelection
|
|
|
2
2
|
export type BoardTargetKind = "edge" | "vertex" | "space" | "tile";
|
|
3
3
|
export declare function interactionInputKeys(descriptor: Pick<InteractionDescriptor, "inputs">): string[];
|
|
4
4
|
export declare function applyInteractionInputDefaults<Params extends Record<string, unknown>>(descriptor: Pick<InteractionDescriptor, "inputs">, params: Readonly<Partial<Params>>): Partial<Params>;
|
|
5
|
+
export declare function resolveInputDomain(input: InteractionInputDescriptor, params: Readonly<Record<string, unknown>>): InteractionInputDescriptor;
|
|
6
|
+
export declare function resolveInteractionInputs(descriptor: Pick<InteractionDescriptor, "inputs">, params: Readonly<Record<string, unknown>>): InteractionInputDescriptor[];
|
|
7
|
+
export declare function dependentInputKeys(descriptor: Pick<InteractionDescriptor, "inputs">, changedKey: string): string[];
|
|
5
8
|
export declare function validateInteractionInputDomains(descriptor: Pick<InteractionDescriptor, "inputs">, params: Readonly<Record<string, unknown>>): Partial<Record<string, readonly string[]>>;
|
|
6
9
|
export declare function isInputValueReady(input: InteractionInputDescriptor, value: unknown): boolean;
|
|
7
10
|
export declare function isManyInput(input: InteractionInputDescriptor): boolean;
|
|
@@ -11,12 +14,12 @@ export declare function isManyTargetSelectable(input: InteractionInputDescriptor
|
|
|
11
14
|
export declare function mergeInteractionFieldErrors(...sources: Array<Partial<Record<string, readonly string[]>>>): Partial<Record<string, readonly string[]>>;
|
|
12
15
|
export declare function hasInteractionFieldErrors(fieldErrors: Partial<Record<string, readonly string[]>>): boolean;
|
|
13
16
|
export declare function inputByKey(descriptor: Pick<InteractionDescriptor, "inputs">, key: string): InteractionInputDescriptor | undefined;
|
|
14
|
-
export declare function inputByTarget(descriptor: Pick<InteractionDescriptor, "inputs">, targetKind: BoardTargetKind | "card", targetId: string): InteractionInputDescriptor | null;
|
|
15
|
-
export declare function eligibleTargetsForInput(descriptor: Pick<InteractionDescriptor, "inputs">, key: string): readonly string[] | undefined;
|
|
16
|
-
export declare function eligibleTargetsByInput(descriptor: Pick<InteractionDescriptor, "inputs"
|
|
17
|
-
export declare function eligibleTargetsByBoardKind(descriptor: Pick<InteractionDescriptor, "inputs"
|
|
17
|
+
export declare function inputByTarget(descriptor: Pick<InteractionDescriptor, "inputs">, targetKind: BoardTargetKind | "card", targetId: string, params?: Readonly<Record<string, unknown>>): InteractionInputDescriptor | null;
|
|
18
|
+
export declare function eligibleTargetsForInput(descriptor: Pick<InteractionDescriptor, "inputs">, key: string, params?: Readonly<Record<string, unknown>>): readonly string[] | undefined;
|
|
19
|
+
export declare function eligibleTargetsByInput(descriptor: Pick<InteractionDescriptor, "inputs">, params?: Readonly<Record<string, unknown>>): Record<string, readonly string[]>;
|
|
20
|
+
export declare function eligibleTargetsByBoardKind(descriptor: Pick<InteractionDescriptor, "inputs">, params?: Readonly<Record<string, unknown>>): Partial<Record<BoardTargetKind, readonly string[]>>;
|
|
18
21
|
export declare function hasBoardTargetInput(descriptor: Pick<InteractionDescriptor, "inputs">): boolean;
|
|
19
22
|
export declare function hasCardTargetInput(descriptor: Pick<InteractionDescriptor, "inputs">): boolean;
|
|
20
23
|
export declare function interactionArmScope(descriptor: Pick<InteractionDescriptor, "inputs" | "interactionKey" | "zoneId">): string;
|
|
21
|
-
export declare function inputKeyForTarget(descriptor: Pick<InteractionDescriptor, "inputs">, targetKind: BoardTargetKind | "card", targetId: string): string | null;
|
|
24
|
+
export declare function inputKeyForTarget(descriptor: Pick<InteractionDescriptor, "inputs">, targetKind: BoardTargetKind | "card", targetId: string, params?: Readonly<Record<string, unknown>>): string | null;
|
|
22
25
|
//# sourceMappingURL=interaction-inputs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interaction-inputs.d.ts","sourceRoot":"","sources":["../../src/utils/interaction-inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,0BAA0B,EAC1B,cAAc,EACf,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEnE,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,GAChD,MAAM,EAAE,CAEV;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEtC,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,EACjD,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAChC,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,EACjD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"interaction-inputs.d.ts","sourceRoot":"","sources":["../../src/utils/interaction-inputs.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,9 +12,52 @@ export function applyInteractionInputDefaults(descriptor, params) {
|
|
|
12
12
|
}
|
|
13
13
|
return next;
|
|
14
14
|
}
|
|
15
|
+
export function resolveInputDomain(input, params) {
|
|
16
|
+
const dependencies = input.domain.dependsOn ?? [];
|
|
17
|
+
if (dependencies.length === 0)
|
|
18
|
+
return input;
|
|
19
|
+
const caseMatch = input.domain.dependentCases?.find((candidate) => dependencies.every((key) => params[key] !== undefined &&
|
|
20
|
+
params[key] !== null &&
|
|
21
|
+
String(params[key]) === candidate.when[key]));
|
|
22
|
+
if (!caseMatch)
|
|
23
|
+
return input;
|
|
24
|
+
return {
|
|
25
|
+
...input,
|
|
26
|
+
domain: {
|
|
27
|
+
...caseMatch.domain,
|
|
28
|
+
dependsOn: dependencies,
|
|
29
|
+
dependentCases: input.domain.dependentCases,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function resolveInteractionInputs(descriptor, params) {
|
|
34
|
+
return descriptor.inputs.map((input) => resolveInputDomain(input, params));
|
|
35
|
+
}
|
|
36
|
+
export function dependentInputKeys(descriptor, changedKey) {
|
|
37
|
+
const dependentsByKey = new Map();
|
|
38
|
+
for (const input of descriptor.inputs) {
|
|
39
|
+
for (const dependency of input.domain.dependsOn ?? []) {
|
|
40
|
+
dependentsByKey.set(dependency, [
|
|
41
|
+
...(dependentsByKey.get(dependency) ?? []),
|
|
42
|
+
input.key,
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const result = [];
|
|
47
|
+
const queue = [...(dependentsByKey.get(changedKey) ?? [])];
|
|
48
|
+
while (queue.length > 0) {
|
|
49
|
+
const key = queue.shift();
|
|
50
|
+
if (!key || result.includes(key))
|
|
51
|
+
continue;
|
|
52
|
+
result.push(key);
|
|
53
|
+
queue.push(...(dependentsByKey.get(key) ?? []));
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
15
57
|
export function validateInteractionInputDomains(descriptor, params) {
|
|
16
58
|
const fieldErrors = {};
|
|
17
|
-
for (const
|
|
59
|
+
for (const rawInput of descriptor.inputs) {
|
|
60
|
+
const input = resolveInputDomain(rawInput, params);
|
|
18
61
|
const value = params[input.key];
|
|
19
62
|
if (value === undefined || value === null)
|
|
20
63
|
continue;
|
|
@@ -37,6 +80,17 @@ export function validateInteractionInputDomains(descriptor, params) {
|
|
|
37
80
|
if (value.length > max) {
|
|
38
81
|
pushFieldError(fieldErrors, input.key, `Choose at most ${max} ${pluralize("option", max)}.`);
|
|
39
82
|
}
|
|
83
|
+
const allowed = new Set(input.domain.choices?.map((choice) => choice.value));
|
|
84
|
+
if (allowed.size > 0 &&
|
|
85
|
+
value.some((item) => !allowed.has(String(item)))) {
|
|
86
|
+
pushFieldError(fieldErrors, input.key, "Selected choice is not eligible.");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (input.domain.type === "choice") {
|
|
90
|
+
const allowed = new Set(input.domain.choices?.map((choice) => choice.value));
|
|
91
|
+
if (allowed.size > 0 && !allowed.has(value)) {
|
|
92
|
+
pushFieldError(fieldErrors, input.key, "Selected choice is not eligible.");
|
|
93
|
+
}
|
|
40
94
|
}
|
|
41
95
|
if (input.domain.type === "target") {
|
|
42
96
|
const values = valuesForSelection(input.domain.selection, value);
|
|
@@ -111,8 +165,9 @@ export function hasInteractionFieldErrors(fieldErrors) {
|
|
|
111
165
|
export function inputByKey(descriptor, key) {
|
|
112
166
|
return descriptor.inputs.find((input) => input.key === key);
|
|
113
167
|
}
|
|
114
|
-
export function inputByTarget(descriptor, targetKind, targetId) {
|
|
115
|
-
for (const
|
|
168
|
+
export function inputByTarget(descriptor, targetKind, targetId, params = {}) {
|
|
169
|
+
for (const rawInput of descriptor.inputs) {
|
|
170
|
+
const input = resolveInputDomain(rawInput, params);
|
|
116
171
|
if (input.domain.type !== "target")
|
|
117
172
|
continue;
|
|
118
173
|
if (input.domain.targetKind !== targetKind)
|
|
@@ -122,26 +177,29 @@ export function inputByTarget(descriptor, targetKind, targetId) {
|
|
|
122
177
|
}
|
|
123
178
|
return null;
|
|
124
179
|
}
|
|
125
|
-
export function eligibleTargetsForInput(descriptor, key) {
|
|
126
|
-
const
|
|
180
|
+
export function eligibleTargetsForInput(descriptor, key, params = {}) {
|
|
181
|
+
const rawInput = inputByKey(descriptor, key);
|
|
182
|
+
const domain = rawInput ? resolveInputDomain(rawInput, params).domain : null;
|
|
127
183
|
return domain?.type === "target" ? domain.eligibleTargets : undefined;
|
|
128
184
|
}
|
|
129
|
-
export function eligibleTargetsByInput(descriptor) {
|
|
130
|
-
return Object.fromEntries(descriptor.inputs.flatMap((
|
|
185
|
+
export function eligibleTargetsByInput(descriptor, params = {}) {
|
|
186
|
+
return Object.fromEntries(descriptor.inputs.flatMap((rawInput) => {
|
|
187
|
+
const input = resolveInputDomain(rawInput, params);
|
|
131
188
|
const targets = input.domain.type === "target"
|
|
132
189
|
? input.domain.eligibleTargets
|
|
133
190
|
: undefined;
|
|
134
191
|
return targets ? [[input.key, targets]] : [];
|
|
135
192
|
}));
|
|
136
193
|
}
|
|
137
|
-
export function eligibleTargetsByBoardKind(descriptor) {
|
|
194
|
+
export function eligibleTargetsByBoardKind(descriptor, params = {}) {
|
|
138
195
|
const result = {
|
|
139
196
|
edge: new Set(),
|
|
140
197
|
vertex: new Set(),
|
|
141
198
|
space: new Set(),
|
|
142
199
|
tile: new Set(),
|
|
143
200
|
};
|
|
144
|
-
for (const
|
|
201
|
+
for (const rawInput of descriptor.inputs) {
|
|
202
|
+
const input = resolveInputDomain(rawInput, params);
|
|
145
203
|
if (input.domain.type !== "target")
|
|
146
204
|
continue;
|
|
147
205
|
const targetKind = input.domain.targetKind;
|
|
@@ -154,14 +212,13 @@ export function eligibleTargetsByBoardKind(descriptor) {
|
|
|
154
212
|
return Object.fromEntries(Object.entries(result).flatMap(([kind, ids]) => ids.size > 0 ? [[kind, [...ids]]] : []));
|
|
155
213
|
}
|
|
156
214
|
export function hasBoardTargetInput(descriptor) {
|
|
157
|
-
return
|
|
215
|
+
return boardTargetKindsOf(descriptor).length > 0;
|
|
158
216
|
}
|
|
159
217
|
export function hasCardTargetInput(descriptor) {
|
|
160
218
|
return descriptor.inputs.some((input) => input.domain.type === "target" && input.domain.targetKind === "card");
|
|
161
219
|
}
|
|
162
220
|
export function interactionArmScope(descriptor) {
|
|
163
|
-
const
|
|
164
|
-
const boardKinds = Object.keys(byKind).filter((kind) => byKind[kind] !== undefined);
|
|
221
|
+
const boardKinds = boardTargetKindsOf(descriptor);
|
|
165
222
|
if (boardKinds.length === 1)
|
|
166
223
|
return `board:${boardKinds[0]}`;
|
|
167
224
|
if (boardKinds.length > 1)
|
|
@@ -170,8 +227,19 @@ export function interactionArmScope(descriptor) {
|
|
|
170
227
|
return `zone:${descriptor.zoneId}`;
|
|
171
228
|
return `interaction:${descriptor.interactionKey}`;
|
|
172
229
|
}
|
|
173
|
-
export function inputKeyForTarget(descriptor, targetKind, targetId) {
|
|
174
|
-
return inputByTarget(descriptor, targetKind, targetId)?.key ?? null;
|
|
230
|
+
export function inputKeyForTarget(descriptor, targetKind, targetId, params = {}) {
|
|
231
|
+
return inputByTarget(descriptor, targetKind, targetId, params)?.key ?? null;
|
|
232
|
+
}
|
|
233
|
+
function boardTargetKindsOf(descriptor) {
|
|
234
|
+
const kinds = new Set();
|
|
235
|
+
for (const input of descriptor.inputs) {
|
|
236
|
+
if (input.domain.type !== "target")
|
|
237
|
+
continue;
|
|
238
|
+
if (isBoardTargetKind(input.domain.targetKind)) {
|
|
239
|
+
kinds.add(input.domain.targetKind);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return [...kinds];
|
|
175
243
|
}
|
|
176
244
|
function validateInputSelection(input, value) {
|
|
177
245
|
const selection = input.domain.selection;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { InteractionUiStore } from "../context/InteractionDraftContext.js";
|
|
2
|
+
import type { InteractionDescriptor } from "../types/plugin-state.js";
|
|
3
|
+
export interface InteractionDraftMutation {
|
|
4
|
+
key: string;
|
|
5
|
+
value: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface InteractionDraftReadiness {
|
|
8
|
+
values: Record<string, unknown>;
|
|
9
|
+
missingInputs: readonly string[];
|
|
10
|
+
readyFrontier: readonly string[];
|
|
11
|
+
blockedInputs: readonly string[];
|
|
12
|
+
fieldErrors: Partial<Record<string, readonly string[]>>;
|
|
13
|
+
ready: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface RoutedInteractionTarget {
|
|
16
|
+
inputKey: string;
|
|
17
|
+
value: string;
|
|
18
|
+
extraInputs?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface RoutedInteractionTargetResult {
|
|
21
|
+
params: Record<string, unknown>;
|
|
22
|
+
readiness: InteractionDraftReadiness;
|
|
23
|
+
}
|
|
24
|
+
export declare function applyInteractionDraftMutation(store: Pick<InteractionUiStore, "getDraft" | "setInput" | "clearInput">, descriptor: InteractionDescriptor, mutations: readonly InteractionDraftMutation[]): Record<string, unknown>;
|
|
25
|
+
export declare function getInteractionDraftReadiness(descriptor: InteractionDescriptor, draft: Readonly<Record<string, unknown>>): InteractionDraftReadiness;
|
|
26
|
+
export declare function routeInteractionTarget(store: Pick<InteractionUiStore, "getDraft" | "setInput" | "clearInput">, descriptor: InteractionDescriptor, target: RoutedInteractionTarget): RoutedInteractionTargetResult;
|
|
27
|
+
export declare function shouldRouteInteractionPending(descriptor: InteractionDescriptor, readiness: Pick<InteractionDraftReadiness, "ready">): boolean;
|
|
28
|
+
export declare function markInteractionPending(store: Pick<InteractionUiStore, "arm" | "setPendingInteraction">, descriptor: InteractionDescriptor): void;
|
|
29
|
+
export declare function clearInteractionRoute(store: Pick<InteractionUiStore, "clearInput" | "getArmed" | "arm" | "getPendingInteraction" | "setPendingInteraction">, descriptor: InteractionDescriptor): void;
|
|
30
|
+
export declare function claimInteractionSubmit(store: Pick<InteractionUiStore, "claimSubmitting">, descriptor: InteractionDescriptor): boolean;
|
|
31
|
+
//# sourceMappingURL=interaction-router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interaction-router.d.ts","sourceRoot":"","sources":["../../src/utils/interaction-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AActE,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IACxD,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC,EACvE,UAAU,EAAE,qBAAqB,EACjC,SAAS,EAAE,SAAS,wBAAwB,EAAE,GAC7C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgCzB;AAeD,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,yBAAyB,CAoC3B;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC,EACvE,UAAU,EAAE,qBAAqB,EACjC,MAAM,EAAE,uBAAuB,GAC9B,6BAA6B,CAoB/B;AAED,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,qBAAqB,EACjC,SAAS,EAAE,IAAI,CAAC,yBAAyB,EAAE,OAAO,CAAC,GAClD,OAAO,CAET;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,uBAAuB,CAAC,EAChE,UAAU,EAAE,qBAAqB,GAChC,IAAI,CAGN;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,IAAI,CACT,kBAAkB,EAChB,YAAY,GACZ,UAAU,GACV,KAAK,GACL,uBAAuB,GACvB,uBAAuB,CAC1B,EACD,UAAU,EAAE,qBAAqB,GAChC,IAAI,CASN;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,EAClD,UAAU,EAAE,qBAAqB,GAChC,OAAO,CAET"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { applyInteractionInputDefaults, dependentInputKeys, hasInteractionFieldErrors, inputByKey, interactionArmScope, interactionInputKeys, isInputValueReady, resolveInputDomain, toggleManyValue, validateInteractionInputDomains, } from "./interaction-inputs.js";
|
|
2
|
+
export function applyInteractionDraftMutation(store, descriptor, mutations) {
|
|
3
|
+
const nextDraft = {
|
|
4
|
+
...store.getDraft(descriptor.interactionKey),
|
|
5
|
+
};
|
|
6
|
+
const mutatedKeys = new Set(mutations.map((mutation) => mutation.key));
|
|
7
|
+
for (const { key, value } of mutations) {
|
|
8
|
+
nextDraft[key] = value;
|
|
9
|
+
}
|
|
10
|
+
for (const { key } of mutations) {
|
|
11
|
+
for (const dependentKey of dependentInputKeys(descriptor, key)) {
|
|
12
|
+
if (mutatedKeys.has(dependentKey))
|
|
13
|
+
continue;
|
|
14
|
+
if (shouldClearDependentInput(descriptor, nextDraft, dependentKey)) {
|
|
15
|
+
delete nextDraft[dependentKey];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (const { key, value } of mutations) {
|
|
20
|
+
store.setInput(descriptor.interactionKey, key, value);
|
|
21
|
+
}
|
|
22
|
+
for (const { key } of mutations) {
|
|
23
|
+
for (const dependentKey of dependentInputKeys(descriptor, key)) {
|
|
24
|
+
if (mutatedKeys.has(dependentKey))
|
|
25
|
+
continue;
|
|
26
|
+
if (shouldClearDependentInput(descriptor, nextDraft, dependentKey)) {
|
|
27
|
+
store.clearInput(descriptor.interactionKey, dependentKey);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return nextDraft;
|
|
32
|
+
}
|
|
33
|
+
function shouldClearDependentInput(descriptor, draft, dependentKey) {
|
|
34
|
+
const input = inputByKey(descriptor, dependentKey);
|
|
35
|
+
if (!input)
|
|
36
|
+
return true;
|
|
37
|
+
const value = draft[dependentKey];
|
|
38
|
+
if (!isInputValueReady(resolveInputDomain(input, draft), value))
|
|
39
|
+
return true;
|
|
40
|
+
const errors = validateInteractionInputDomains(descriptor, draft);
|
|
41
|
+
return (errors[dependentKey]?.length ?? 0) > 0;
|
|
42
|
+
}
|
|
43
|
+
export function getInteractionDraftReadiness(descriptor, draft) {
|
|
44
|
+
const values = applyInteractionInputDefaults(descriptor, draft);
|
|
45
|
+
const missingInputs = interactionInputKeys(descriptor).filter((key) => {
|
|
46
|
+
const input = inputByKey(descriptor, key);
|
|
47
|
+
const value = values[key];
|
|
48
|
+
return input
|
|
49
|
+
? !isInputValueReady(resolveInputDomain(input, values), value)
|
|
50
|
+
: value === null || value === undefined;
|
|
51
|
+
});
|
|
52
|
+
const missingInputSet = new Set(missingInputs);
|
|
53
|
+
const readyFrontier = missingInputs.filter((key) => {
|
|
54
|
+
const input = inputByKey(descriptor, key);
|
|
55
|
+
if (!input)
|
|
56
|
+
return true;
|
|
57
|
+
return (input.domain.dependsOn ?? []).every((dependencyKey) => {
|
|
58
|
+
if (missingInputSet.has(dependencyKey))
|
|
59
|
+
return false;
|
|
60
|
+
const dependency = inputByKey(descriptor, dependencyKey);
|
|
61
|
+
if (!dependency)
|
|
62
|
+
return values[dependencyKey] !== undefined;
|
|
63
|
+
return isInputValueReady(resolveInputDomain(dependency, values), values[dependencyKey]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
const fieldErrors = validateInteractionInputDomains(descriptor, values);
|
|
67
|
+
return {
|
|
68
|
+
values,
|
|
69
|
+
missingInputs,
|
|
70
|
+
readyFrontier,
|
|
71
|
+
blockedInputs: missingInputs.filter((key) => !readyFrontier.includes(key)),
|
|
72
|
+
fieldErrors,
|
|
73
|
+
ready: missingInputs.length === 0 && !hasInteractionFieldErrors(fieldErrors),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function routeInteractionTarget(store, descriptor, target) {
|
|
77
|
+
const input = inputByKey(descriptor, target.inputKey);
|
|
78
|
+
const currentDraft = store.getDraft(descriptor.interactionKey);
|
|
79
|
+
const selection = input?.domain.type === "target" ? input.domain.selection : undefined;
|
|
80
|
+
const targetValue = selection?.mode === "many"
|
|
81
|
+
? toggleManyValue(currentDraft[target.inputKey], target.value, selection)
|
|
82
|
+
: target.value;
|
|
83
|
+
const params = applyInteractionDraftMutation(store, descriptor, [
|
|
84
|
+
...Object.entries(target.extraInputs ?? {}).map(([key, value]) => ({
|
|
85
|
+
key,
|
|
86
|
+
value,
|
|
87
|
+
})),
|
|
88
|
+
{ key: target.inputKey, value: targetValue },
|
|
89
|
+
]);
|
|
90
|
+
return {
|
|
91
|
+
params,
|
|
92
|
+
readiness: getInteractionDraftReadiness(descriptor, params),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function shouldRouteInteractionPending(descriptor, readiness) {
|
|
96
|
+
return descriptor.commit.mode !== "autoWhenReady" || !readiness.ready;
|
|
97
|
+
}
|
|
98
|
+
export function markInteractionPending(store, descriptor) {
|
|
99
|
+
store.arm(interactionArmScope(descriptor), descriptor.interactionKey);
|
|
100
|
+
store.setPendingInteraction(descriptor.interactionKey);
|
|
101
|
+
}
|
|
102
|
+
export function clearInteractionRoute(store, descriptor) {
|
|
103
|
+
const armScope = interactionArmScope(descriptor);
|
|
104
|
+
store.clearInput(descriptor.interactionKey);
|
|
105
|
+
if (store.getArmed(armScope) === descriptor.interactionKey) {
|
|
106
|
+
store.arm(armScope, null);
|
|
107
|
+
}
|
|
108
|
+
if (store.getPendingInteraction() === descriptor.interactionKey) {
|
|
109
|
+
store.setPendingInteraction(null);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function claimInteractionSubmit(store, descriptor) {
|
|
113
|
+
return store.claimSubmitting(descriptor.interactionKey);
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreamboard-games/ui-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.45",
|
|
4
4
|
"description": "UI SDK for dreamboard plugin development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"clean": "rm -rf dist"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@dreamboard/sdk-types": "npm:@dreamboard-games/sdk-types@0.1.
|
|
71
|
+
"@dreamboard/sdk-types": "npm:@dreamboard-games/sdk-types@0.1.2",
|
|
72
72
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
73
73
|
"@radix-ui/react-dialog": "^1.1.14",
|
|
74
74
|
"@radix-ui/react-label": "^2.1.6",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
|
8
8
|
import type { InteractionHandle } from "../hooks/useInteractionHandle.js";
|
|
9
|
+
import { submitInteraction } from "../primitives/interaction-submit.js";
|
|
9
10
|
import { interactionLabel } from "../utils/interaction-labels.js";
|
|
10
11
|
import { ThemedButton } from "./ThemedButton.js";
|
|
11
12
|
|
|
@@ -29,7 +30,7 @@ export function ActionButton({
|
|
|
29
30
|
disabled={disabled || !handle.available}
|
|
30
31
|
title={title ?? formatUnavailableReason(handle.unavailableReason)}
|
|
31
32
|
onClick={() => {
|
|
32
|
-
void handle
|
|
33
|
+
void submitInteraction(handle);
|
|
33
34
|
}}
|
|
34
35
|
{...rest}
|
|
35
36
|
>
|
package/src/components/Card.tsx
CHANGED
|
@@ -21,7 +21,7 @@ export interface CardProps<CardData extends ViewCard = ViewCard>
|
|
|
21
21
|
size?: "sm" | "md" | "lg";
|
|
22
22
|
faceDown?: boolean;
|
|
23
23
|
renderContent?: (card: CardData) => React.ReactNode;
|
|
24
|
-
onCardClick?: (cardId:
|
|
24
|
+
onCardClick?: (cardId: CardData["id"]) => void;
|
|
25
25
|
"aria-label"?: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -21,11 +21,12 @@ import type {
|
|
|
21
21
|
InteractionHandle,
|
|
22
22
|
InteractionParamsShape,
|
|
23
23
|
} from "../hooks/useInteractionHandle.js";
|
|
24
|
+
import { normalizeDiceState, type DiceValue } from "../primitives/index.js";
|
|
24
25
|
import { interactionLabel } from "../utils/interaction-labels.js";
|
|
25
26
|
import { ThemedButton } from "./ThemedButton.js";
|
|
26
27
|
|
|
27
28
|
export interface DiceRollerRenderProps {
|
|
28
|
-
values:
|
|
29
|
+
values: ReadonlyArray<number | undefined> | undefined;
|
|
29
30
|
/** Undefined if any die hasn't been rolled */
|
|
30
31
|
sum: number | undefined;
|
|
31
32
|
diceCount: number;
|
|
@@ -48,7 +49,7 @@ export interface DiceRollerRollAction<
|
|
|
48
49
|
export interface DiceRollerProps<
|
|
49
50
|
Params extends InteractionParamsShape = InteractionParamsShape,
|
|
50
51
|
> {
|
|
51
|
-
values?:
|
|
52
|
+
values?: readonly DiceValue[] | null;
|
|
52
53
|
/** Used when values not provided */
|
|
53
54
|
diceCount?: number;
|
|
54
55
|
render?: (props: DiceRollerRenderProps) => ReactNode;
|
|
@@ -72,19 +73,7 @@ export function DiceRoller<
|
|
|
72
73
|
rollAction,
|
|
73
74
|
className,
|
|
74
75
|
}: DiceRollerProps<Params>) {
|
|
75
|
-
const
|
|
76
|
-
values?.every((v) => v !== null && v !== undefined) ?? false;
|
|
77
|
-
const sum = allRolled
|
|
78
|
-
? values?.reduce((a, b) => (a ?? 0) + (b ?? 0), 0)
|
|
79
|
-
: undefined;
|
|
80
|
-
const displayCount = values?.length ?? diceCount;
|
|
81
|
-
|
|
82
|
-
const renderProps: DiceRollerRenderProps = {
|
|
83
|
-
values,
|
|
84
|
-
sum,
|
|
85
|
-
diceCount: displayCount,
|
|
86
|
-
allRolled,
|
|
87
|
-
};
|
|
76
|
+
const renderProps = normalizeDiceState({ values, count: diceCount });
|
|
88
77
|
|
|
89
78
|
return (
|
|
90
79
|
<div
|
|
@@ -97,15 +86,15 @@ export function DiceRoller<
|
|
|
97
86
|
{rollAction ? (
|
|
98
87
|
<DiceRollDialog
|
|
99
88
|
action={rollAction}
|
|
100
|
-
values={values}
|
|
101
|
-
diceCount={
|
|
89
|
+
values={renderProps.values}
|
|
90
|
+
diceCount={renderProps.diceCount}
|
|
102
91
|
/>
|
|
103
92
|
) : null}
|
|
104
93
|
|
|
105
94
|
{/* Screen reader only: dice info */}
|
|
106
95
|
<div className="sr-only" aria-live="polite">
|
|
107
|
-
{allRolled && values
|
|
108
|
-
? `Rolled ${values.join(", ")}. Total: ${sum}`
|
|
96
|
+
{renderProps.allRolled && renderProps.values
|
|
97
|
+
? `Rolled ${renderProps.values.join(", ")}. Total: ${renderProps.sum}`
|
|
109
98
|
: "Dice not rolled yet"}
|
|
110
99
|
</div>
|
|
111
100
|
</div>
|
|
@@ -152,7 +141,7 @@ function DiceRollDialog<Params extends InteractionParamsShape>({
|
|
|
152
141
|
diceCount,
|
|
153
142
|
}: {
|
|
154
143
|
action: DiceRollerRollAction<Params>;
|
|
155
|
-
values:
|
|
144
|
+
values: ReadonlyArray<number | undefined> | undefined;
|
|
156
145
|
diceCount: number;
|
|
157
146
|
}) {
|
|
158
147
|
const theme = useTheme();
|
|
@@ -409,7 +398,7 @@ function DiceRow({
|
|
|
409
398
|
size,
|
|
410
399
|
rolling,
|
|
411
400
|
}: {
|
|
412
|
-
values:
|
|
401
|
+
values: ReadonlyArray<number | undefined>;
|
|
413
402
|
size: number;
|
|
414
403
|
rolling: boolean;
|
|
415
404
|
}) {
|
|
@@ -595,7 +584,9 @@ function nextAnimatedValues(count: number): number[] {
|
|
|
595
584
|
});
|
|
596
585
|
}
|
|
597
586
|
|
|
598
|
-
function sumValues(
|
|
587
|
+
function sumValues(
|
|
588
|
+
values: ReadonlyArray<number | undefined>,
|
|
589
|
+
): number | undefined {
|
|
599
590
|
if (!values.every((value) => typeof value === "number")) return undefined;
|
|
600
591
|
return values.reduce((total, value) => total + (value ?? 0), 0);
|
|
601
592
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
test("ErrorBoundary fallback is styled without relying on consumer Tailwind output", () => {
|
|
6
|
+
const source = readFileSync(
|
|
7
|
+
join(import.meta.dir, "ErrorBoundary.tsx"),
|
|
8
|
+
"utf8",
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
expect(source).toContain("satisfies Record<string, CSSProperties>");
|
|
12
|
+
expect(source).toContain('background: "#fff"');
|
|
13
|
+
expect(source).toContain('border: "2px solid #0f172a"');
|
|
14
|
+
expect(source).toContain('boxShadow: "6px 6px 0 #111827"');
|
|
15
|
+
expect(source).toContain(
|
|
16
|
+
"<h1 style={styles.title}>Game failed to start</h1>",
|
|
17
|
+
);
|
|
18
|
+
expect(source).not.toContain("bg-gradient-to-br");
|
|
19
|
+
});
|