@joystyk/cards-ui 0.0.0 → 0.2.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.
package/dist/hand.d.ts ADDED
@@ -0,0 +1,100 @@
1
+ import { type Rank, type Suit } from '@joystyk/kit/cards';
2
+ import { type TileFace } from '@joystyk/kit/tiles';
3
+ /** One card in the hand, with what the player may do with it now. */
4
+ export type HandCard = ({
5
+ readonly tile?: never;
6
+ readonly id: string;
7
+ readonly rank: Rank;
8
+ readonly suit: Suit;
9
+ } | {
10
+ readonly tile: TileFace;
11
+ readonly rank?: never;
12
+ readonly suit?: never;
13
+ }) & {
14
+ readonly id: string;
15
+ /** It may be played (or chosen) now. A card that may not is aria-disabled. */
16
+ readonly playable: boolean;
17
+ /** Chosen for a multi-card choice (aria-pressed), drawn lifted. */
18
+ readonly chosen?: boolean;
19
+ /** Added to the card's name and marked on its face: "just received". */
20
+ readonly note?: string;
21
+ };
22
+ export type HandMode = 'play' | 'choose';
23
+ export type HandLayout = {
24
+ /** The hand's name: "Your hand, 13 cards". */
25
+ readonly label: string;
26
+ /** In display order. */
27
+ readonly cards: readonly HandCard[];
28
+ readonly mode: HandMode;
29
+ };
30
+ export type HandOptions = {
31
+ /** Reduced motion: chosen cards lift without a transition. */
32
+ readonly calm: boolean;
33
+ /** Blue diamonds and green clubs. */
34
+ readonly fourColor: boolean;
35
+ readonly numbers?: boolean;
36
+ readonly draggable?: boolean;
37
+ };
38
+ /** What the hand tells the game. */
39
+ export type HandOut = {
40
+ readonly _tag: 'Play';
41
+ readonly id: string;
42
+ } | {
43
+ readonly _tag: 'Refused';
44
+ readonly id: string;
45
+ } | {
46
+ readonly _tag: 'Toggle';
47
+ readonly id: string;
48
+ } | {
49
+ readonly _tag: 'Commit';
50
+ };
51
+ export type HandState = {
52
+ /** The index of the card the Tab stop is on. */
53
+ readonly focus: number;
54
+ /** The ids last rendered, to keep the focus on the same card when the hand changes. */
55
+ readonly ids: readonly string[];
56
+ /** Focus is inside the hand, so a card that leaves it hands the focus on. */
57
+ readonly inside: boolean;
58
+ };
59
+ export type HandMsg = {
60
+ readonly _tag: 'Key';
61
+ readonly key: string;
62
+ } | {
63
+ readonly _tag: 'Press';
64
+ readonly id: string;
65
+ } | {
66
+ readonly _tag: 'Enter';
67
+ readonly id: string;
68
+ } | {
69
+ readonly _tag: 'Leave';
70
+ };
71
+ export declare const EMPTY_HAND: HandLayout;
72
+ export declare const DEFAULT_HAND_OPTIONS: HandOptions;
73
+ export declare const isHandLayout: (value: unknown) => value is HandLayout;
74
+ export declare const isHandOptions: (value: unknown) => value is HandOptions;
75
+ /** A card's accessible name: "Queen of spades", "9 of hearts, just received". */
76
+ export declare const handCardLabel: (card: HandCard) => string;
77
+ export declare const initialHand: (layout: HandLayout) => HandState;
78
+ /** One step of the hand's controller: the new state, outputs, and a card to focus after rendering. */
79
+ export type HandStep = {
80
+ readonly state: HandState;
81
+ readonly outs: readonly HandOut[];
82
+ readonly focus: string | null;
83
+ };
84
+ /**
85
+ * Brings the state in line with a new hand. The focus stays on the same card if it is still
86
+ * there, otherwise on the card now at its place; if focus was inside the hand and its card
87
+ * left, the new card takes the focus.
88
+ */
89
+ export declare const syncHand: (s: HandState, layout: HandLayout) => HandStep;
90
+ /** The hand's reducer, pure, so its behavior is tested without a DOM. */
91
+ export declare const handStep: (before: HandState, layout: HandLayout, m: HandMsg) => HandStep;
92
+ export declare const CardHand: import("@gyral/core").GyralElementClass<HandState, HandMsg, {
93
+ readonly layout: HandLayout;
94
+ readonly options: HandOptions;
95
+ }, HandOut, "Key" | "Enter" | "Press" | "Leave">;
96
+ declare global {
97
+ interface HTMLElementTagNameMap {
98
+ 'jy-card-hand': InstanceType<typeof CardHand>;
99
+ }
100
+ }
package/dist/hand.js ADDED
@@ -0,0 +1,480 @@
1
+ import { faceStyles } from "./face-styles.js";
2
+ import { cardFace, suitDefs } from "./faces.js";
3
+ import { handStyles } from "./hand-styles.js";
4
+ import { tileFace } from "./tiles.js";
5
+ import { cardName } from "@joystyk/kit/cards";
6
+ import { define, each, focus, intentsOf, outputs, prop } from "@gyral/core";
7
+ import { compiled } from "@gyral/core/compiled";
8
+ import { tileName } from "@joystyk/kit/tiles";
9
+ //#region src/hand.ts
10
+ var _gyral$t0 = {
11
+ "id": "ak2gvt1npv",
12
+ "html": "<button type=\"button\"><span></span></button>",
13
+ "parts": [
14
+ [
15
+ 4,
16
+ [0],
17
+ "class",
18
+ [
19
+ "slot ",
20
+ " ",
21
+ " ",
22
+ ""
23
+ ]
24
+ ],
25
+ [
26
+ 3,
27
+ [0],
28
+ "data-card"
29
+ ],
30
+ [
31
+ 3,
32
+ [0],
33
+ "draggable"
34
+ ],
35
+ [
36
+ 3,
37
+ [0],
38
+ "tabindex"
39
+ ],
40
+ [
41
+ 3,
42
+ [0],
43
+ "aria-label"
44
+ ],
45
+ [
46
+ 3,
47
+ [0],
48
+ "aria-disabled"
49
+ ],
50
+ [
51
+ 3,
52
+ [0],
53
+ "aria-pressed"
54
+ ],
55
+ [
56
+ 3,
57
+ [0],
58
+ "data-intent"
59
+ ],
60
+ [
61
+ 4,
62
+ [0, 0],
63
+ "class",
64
+ ["cface s-", ""]
65
+ ],
66
+ [1, [0, 0]]
67
+ ],
68
+ "segments": [
69
+ "<button type=\"button\"",
70
+ {
71
+ "k": "attr",
72
+ "name": "class",
73
+ "strings": [
74
+ "slot ",
75
+ " ",
76
+ " ",
77
+ ""
78
+ ]
79
+ },
80
+ {
81
+ "k": "attr",
82
+ "name": "data-card"
83
+ },
84
+ {
85
+ "k": "attr",
86
+ "name": "draggable"
87
+ },
88
+ {
89
+ "k": "attr",
90
+ "name": "tabindex"
91
+ },
92
+ {
93
+ "k": "attr",
94
+ "name": "aria-label"
95
+ },
96
+ {
97
+ "k": "attr",
98
+ "name": "aria-disabled"
99
+ },
100
+ {
101
+ "k": "attr",
102
+ "name": "aria-pressed"
103
+ },
104
+ {
105
+ "k": "attr",
106
+ "name": "data-intent"
107
+ },
108
+ "><span",
109
+ {
110
+ "k": "attr",
111
+ "name": "class",
112
+ "strings": ["cface s-", ""]
113
+ },
114
+ ">",
115
+ {
116
+ "k": "child",
117
+ "in": "span"
118
+ },
119
+ "</span></button>"
120
+ ]
121
+ };
122
+ var _gyral$t1 = {
123
+ "id": "wiy3aoavqm",
124
+ "html": "<div class=\"h-enter\" data-intent-on=\"focusin\"><div class=\"h-leave\" data-intent-on=\"focusout\"><div role=\"toolbar\" data-intent-on=\"keydown\"></div></div></div>",
125
+ "parts": [
126
+ [
127
+ 2,
128
+ [],
129
+ 0
130
+ ],
131
+ [
132
+ 3,
133
+ [0],
134
+ "data-intent"
135
+ ],
136
+ [
137
+ 3,
138
+ [0, 0],
139
+ "data-intent"
140
+ ],
141
+ [
142
+ 4,
143
+ [
144
+ 0,
145
+ 0,
146
+ 0
147
+ ],
148
+ "class",
149
+ [
150
+ "hand ",
151
+ " n-",
152
+ " ",
153
+ " ",
154
+ " mode-",
155
+ ""
156
+ ]
157
+ ],
158
+ [
159
+ 3,
160
+ [
161
+ 0,
162
+ 0,
163
+ 0
164
+ ],
165
+ "aria-label"
166
+ ],
167
+ [
168
+ 3,
169
+ [
170
+ 0,
171
+ 0,
172
+ 0
173
+ ],
174
+ "data-intent"
175
+ ],
176
+ [1, [
177
+ 0,
178
+ 0,
179
+ 0
180
+ ]]
181
+ ],
182
+ "segments": [
183
+ { "k": "child" },
184
+ "<div class=\"h-enter\" data-intent-on=\"focusin\"",
185
+ {
186
+ "k": "attr",
187
+ "name": "data-intent"
188
+ },
189
+ "><div class=\"h-leave\" data-intent-on=\"focusout\"",
190
+ {
191
+ "k": "attr",
192
+ "name": "data-intent"
193
+ },
194
+ "><div role=\"toolbar\" data-intent-on=\"keydown\"",
195
+ {
196
+ "k": "attr",
197
+ "name": "class",
198
+ "strings": [
199
+ "hand ",
200
+ " n-",
201
+ " ",
202
+ " ",
203
+ " mode-",
204
+ ""
205
+ ]
206
+ },
207
+ {
208
+ "k": "attr",
209
+ "name": "aria-label"
210
+ },
211
+ {
212
+ "k": "attr",
213
+ "name": "data-intent"
214
+ },
215
+ ">",
216
+ {
217
+ "k": "child",
218
+ "in": "div"
219
+ },
220
+ "</div></div></div>"
221
+ ]
222
+ };
223
+ var EMPTY_HAND = {
224
+ label: "Your hand",
225
+ cards: [],
226
+ mode: "play"
227
+ };
228
+ var DEFAULT_HAND_OPTIONS = {
229
+ calm: false,
230
+ fourColor: false
231
+ };
232
+ var isRecord = (value) => typeof value === "object" && value !== null;
233
+ var isHandLayout = (value) => isRecord(value) && typeof value["label"] === "string" && Array.isArray(value["cards"]) && (value["mode"] === "play" || value["mode"] === "choose");
234
+ var isHandOptions = (value) => isRecord(value) && typeof value["calm"] === "boolean" && typeof value["fourColor"] === "boolean";
235
+ /** A card's accessible name: "Queen of spades", "9 of hearts, just received". */
236
+ var handCardLabel = (card) => card.note === void 0 ? card.tile === void 0 ? cardName(card) : tileName(card.tile) : `${card.tile === void 0 ? cardName(card) : tileName(card.tile)}, ${card.note}`;
237
+ var clamp = (n, max) => Math.max(0, Math.min(max, n));
238
+ var initialHand = (layout) => ({
239
+ focus: 0,
240
+ ids: layout.cards.map((c) => c.id),
241
+ inside: false
242
+ });
243
+ /**
244
+ * Brings the state in line with a new hand. The focus stays on the same card if it is still
245
+ * there, otherwise on the card now at its place; if focus was inside the hand and its card
246
+ * left, the new card takes the focus.
247
+ */
248
+ var syncHand = (s, layout) => {
249
+ const ids = layout.cards.map((c) => c.id);
250
+ if (ids.length === s.ids.length && ids.every((id, k) => id === s.ids[k])) return {
251
+ state: s,
252
+ outs: [],
253
+ focus: null
254
+ };
255
+ const was = s.ids[s.focus];
256
+ const kept = was === void 0 ? -1 : ids.indexOf(was);
257
+ const index = kept >= 0 ? kept : clamp(s.focus, ids.length - 1);
258
+ const target = ids[index] ?? null;
259
+ const refocus = s.inside && kept < 0 && target !== null;
260
+ return {
261
+ state: {
262
+ focus: index,
263
+ ids,
264
+ inside: s.inside && target !== null
265
+ },
266
+ outs: [],
267
+ focus: refocus ? target : null
268
+ };
269
+ };
270
+ var activate = (layout, card, via) => {
271
+ if (layout.mode === "choose") {
272
+ if (via === "enter") return [{ _tag: "Commit" }];
273
+ return card.playable ? [{
274
+ _tag: "Toggle",
275
+ id: card.id
276
+ }] : [{
277
+ _tag: "Refused",
278
+ id: card.id
279
+ }];
280
+ }
281
+ return card.playable ? [{
282
+ _tag: "Play",
283
+ id: card.id
284
+ }] : [{
285
+ _tag: "Refused",
286
+ id: card.id
287
+ }];
288
+ };
289
+ /** The hand's reducer, pure, so its behavior is tested without a DOM. */
290
+ var handStep = (before, layout, m) => {
291
+ const s = syncHand(before, layout).state;
292
+ const cards = layout.cards;
293
+ const last = cards.length - 1;
294
+ switch (m._tag) {
295
+ case "Key": {
296
+ const card = cards[s.focus];
297
+ if (card === void 0) return {
298
+ state: s,
299
+ outs: [],
300
+ focus: null
301
+ };
302
+ const moveTo = (index) => {
303
+ const next = clamp(index, last);
304
+ return {
305
+ state: {
306
+ ...s,
307
+ focus: next,
308
+ inside: true
309
+ },
310
+ outs: [],
311
+ focus: cards[next].id
312
+ };
313
+ };
314
+ switch (m.key) {
315
+ case "ArrowLeft": return moveTo(s.focus - 1);
316
+ case "ArrowRight": return moveTo(s.focus + 1);
317
+ case "Home": return moveTo(0);
318
+ case "End": return moveTo(last);
319
+ case "Enter": return {
320
+ state: s,
321
+ outs: activate(layout, card, "enter"),
322
+ focus: null
323
+ };
324
+ case " ": return {
325
+ state: s,
326
+ outs: activate(layout, card, "space"),
327
+ focus: null
328
+ };
329
+ default: return {
330
+ state: s,
331
+ outs: [],
332
+ focus: null
333
+ };
334
+ }
335
+ }
336
+ case "Press": {
337
+ const index = cards.findIndex((c) => c.id === m.id);
338
+ const card = cards[index];
339
+ if (card === void 0) return {
340
+ state: s,
341
+ outs: [],
342
+ focus: null
343
+ };
344
+ return {
345
+ state: {
346
+ ...s,
347
+ focus: index,
348
+ inside: true
349
+ },
350
+ outs: activate(layout, card, "pointer"),
351
+ focus: null
352
+ };
353
+ }
354
+ case "Enter": {
355
+ const index = cards.findIndex((c) => c.id === m.id);
356
+ return {
357
+ state: {
358
+ ...s,
359
+ focus: index >= 0 ? index : s.focus,
360
+ inside: true
361
+ },
362
+ outs: [],
363
+ focus: null
364
+ };
365
+ }
366
+ case "Leave": return {
367
+ state: {
368
+ ...s,
369
+ inside: false
370
+ },
371
+ outs: [],
372
+ focus: null
373
+ };
374
+ }
375
+ };
376
+ /** Keys the hand handles; their default actions (scrolling, a button's click) are suppressed. */
377
+ var HAND_KEYS = /* @__PURE__ */ new Set([
378
+ "ArrowLeft",
379
+ "ArrowRight",
380
+ "Home",
381
+ "End",
382
+ "Enter",
383
+ " "
384
+ ]);
385
+ var emit = outputs();
386
+ var i = intentsOf();
387
+ var CardButton = (card, v) => compiled(_gyral$t0, [
388
+ card.chosen === true ? "chosen" : "",
389
+ card.playable ? "" : "off",
390
+ card.note === void 0 ? "" : "noted",
391
+ card.id,
392
+ v.draggable ? "true" : "false",
393
+ v.focused ? 0 : -1,
394
+ handCardLabel(card),
395
+ card.playable ? null : "true",
396
+ v.mode === "choose" ? String(card.chosen === true) : null,
397
+ i.Press,
398
+ card.suit ?? "tile",
399
+ card.tile === void 0 ? cardFace(card.rank, card.suit) : tileFace(card.tile, v.numbers)
400
+ ]);
401
+ var run = (s, layout, m) => {
402
+ const step = handStep(s, layout, m);
403
+ const commands = step.outs.map((out) => emit(out));
404
+ if (step.focus !== null) commands.push(focus(`[data-card="${step.focus}"]`));
405
+ return commands.length === 0 ? step.state : [step.state, commands];
406
+ };
407
+ var CardHand = define()("jy-card-hand", {
408
+ props: {
409
+ layout: prop.value(isHandLayout, { default: EMPTY_HAND }),
410
+ options: prop.value(isHandOptions, { default: DEFAULT_HAND_OPTIONS })
411
+ },
412
+ init: (p) => initialHand(p.layout),
413
+ intent: {
414
+ Key: ({ key, event }) => {
415
+ if (key === void 0 || !(event instanceof KeyboardEvent) || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return void 0;
416
+ if (!HAND_KEYS.has(key)) return void 0;
417
+ event.preventDefault();
418
+ return {
419
+ _tag: "Key",
420
+ key
421
+ };
422
+ },
423
+ Press: ({ event }) => {
424
+ const id = event.target instanceof Element ? event.target.closest("[data-card]")?.getAttribute("data-card") : null;
425
+ return typeof id === "string" ? {
426
+ _tag: "Press",
427
+ id
428
+ } : void 0;
429
+ },
430
+ Enter: ({ event }) => {
431
+ const id = event.target instanceof Element ? event.target.closest("[data-card]")?.getAttribute("data-card") : null;
432
+ return typeof id === "string" ? {
433
+ _tag: "Enter",
434
+ id
435
+ } : void 0;
436
+ },
437
+ Leave: ({ event, target }) => {
438
+ const next = event instanceof FocusEvent ? event.relatedTarget : null;
439
+ return next instanceof Node && target instanceof Node && target.contains(next) ? void 0 : { _tag: "Leave" };
440
+ }
441
+ },
442
+ update: {
443
+ Key: (s, m, { props: p }) => run(s, p.layout, m),
444
+ Press: (s, m, { props: p }) => run(s, p.layout, m),
445
+ Enter: (s, m, { props: p }) => run(s, p.layout, m),
446
+ Leave: (s, m, { props: p }) => run(s, p.layout, m),
447
+ PropsChanged: (s, { props: p }) => {
448
+ const step = syncHand(s, p.layout);
449
+ return step.focus === null ? step.state : [step.state, [focus(`[data-card="${step.focus}"]`)]];
450
+ }
451
+ },
452
+ view: (s, _i, { props: p }) => {
453
+ const { layout, options } = p;
454
+ const focusIndex = clamp(s.focus, layout.cards.length - 1);
455
+ return compiled(_gyral$t1, [
456
+ suitDefs(),
457
+ i.Enter,
458
+ i.Leave,
459
+ layout.cards.some((c) => c.tile !== void 0) ? "tiles" : "",
460
+ Math.min(layout.cards.length, 32),
461
+ options.fourColor ? "four" : "",
462
+ options.calm ? "calm" : "",
463
+ layout.mode,
464
+ layout.label,
465
+ i.Key,
466
+ each(layout.cards, (card) => card.id, CardButton, (card) => ({
467
+ focused: layout.cards[focusIndex]?.id === card.id,
468
+ mode: layout.mode,
469
+ calm: options.calm,
470
+ numbers: options.numbers === true,
471
+ draggable: options.draggable === true
472
+ }))
473
+ ]);
474
+ },
475
+ styles: [faceStyles, handStyles]
476
+ });
477
+ //#endregion
478
+ export { CardHand, DEFAULT_HAND_OPTIONS, EMPTY_HAND, handCardLabel, handStep, initialHand, isHandLayout, isHandOptions, syncHand };
479
+
480
+ //# sourceMappingURL=hand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hand.js","names":["_gyral$compiled"],"sources":["../src/hand.ts"],"sourcesContent":["import type { ChildValue } from '@gyral/core';\n// <jy-card-hand>: the player's own hand in a multiplayer card game (hearts.md, \"Shared needs\"\n// and \"Keyboard and pointer controls\"). The hand is one Tab stop, a toolbar of card buttons\n// with a roving focus: Left and Right move along it, Home and End jump to its ends. Each card\n// is a real <button> named in full (\"Queen of spades\"), never by glyph.\n//\n// Two modes, chosen by the game for the current decision:\n// - `play`: Enter, Space or a tap plays the card. A card that can't be played now is\n// `aria-disabled`; activating it reports `Refused`, so the game can say why.\n// - `choose`: a multi-card choice such as Hearts' pass. Space or a tap toggles a card\n// (`aria-pressed`), and Enter reports `Commit`.\n//\n// The hand knows no rules: the game passes the cards with what each may do, and the engine\n// still decides. Other keys bubble to the game, which adds its own (H, T, S…). Cards are\n// fanned and overlap; each keeps at least a 44 px strip, and the fan wraps onto a second row\n// on narrow screens rather than shrink below it. Focus follows the hand when a played card\n// leaves it.\nimport { define, each, focus, html, intentsOf, outputs, prop, type Command } from '@gyral/core';\nimport { cardName, type Rank, type Suit } from '@joystyk/kit/cards';\nimport { cardFace, suitDefs } from './faces.ts';\nimport { tileName, type TileFace } from '@joystyk/kit/tiles';\nimport { tileFace } from './tiles.ts';\nimport { handStyles } from './hand-styles.ts';\nimport { faceStyles } from './face-styles.ts';\n\n/** One card in the hand, with what the player may do with it now. */\nexport type HandCard = (\n | {\n readonly tile?: never;\n readonly id: string;\n readonly rank: Rank;\n readonly suit: Suit;\n }\n | { readonly tile: TileFace; readonly rank?: never; readonly suit?: never }\n) & {\n readonly id: string;\n /** It may be played (or chosen) now. A card that may not is aria-disabled. */\n readonly playable: boolean;\n /** Chosen for a multi-card choice (aria-pressed), drawn lifted. */\n readonly chosen?: boolean;\n /** Added to the card's name and marked on its face: \"just received\". */\n readonly note?: string;\n};\n\nexport type HandMode = 'play' | 'choose';\n\nexport type HandLayout = {\n /** The hand's name: \"Your hand, 13 cards\". */\n readonly label: string;\n /** In display order. */\n readonly cards: readonly HandCard[];\n readonly mode: HandMode;\n};\n\nexport type HandOptions = {\n /** Reduced motion: chosen cards lift without a transition. */\n readonly calm: boolean;\n /** Blue diamonds and green clubs. */\n readonly fourColor: boolean;\n readonly numbers?: boolean;\n readonly draggable?: boolean;\n};\n\n/** What the hand tells the game. */\nexport type HandOut =\n | { readonly _tag: 'Play'; readonly id: string }\n | { readonly _tag: 'Refused'; readonly id: string }\n | { readonly _tag: 'Toggle'; readonly id: string }\n | { readonly _tag: 'Commit' };\n\nexport type HandState = {\n /** The index of the card the Tab stop is on. */\n readonly focus: number;\n /** The ids last rendered, to keep the focus on the same card when the hand changes. */\n readonly ids: readonly string[];\n /** Focus is inside the hand, so a card that leaves it hands the focus on. */\n readonly inside: boolean;\n};\n\nexport type HandMsg =\n | { readonly _tag: 'Key'; readonly key: string }\n | { readonly _tag: 'Press'; readonly id: string }\n | { readonly _tag: 'Enter'; readonly id: string }\n | { readonly _tag: 'Leave' };\n\nexport const EMPTY_HAND: HandLayout = { label: 'Your hand', cards: [], mode: 'play' };\nexport const DEFAULT_HAND_OPTIONS: HandOptions = { calm: false, fourColor: false };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\nexport const isHandLayout = (value: unknown): value is HandLayout =>\n isRecord(value) &&\n typeof value['label'] === 'string' &&\n Array.isArray(value['cards']) &&\n (value['mode'] === 'play' || value['mode'] === 'choose');\n\nexport const isHandOptions = (value: unknown): value is HandOptions =>\n isRecord(value) && typeof value['calm'] === 'boolean' && typeof value['fourColor'] === 'boolean';\n\n/** A card's accessible name: \"Queen of spades\", \"9 of hearts, just received\". */\nexport const handCardLabel = (card: HandCard): string =>\n card.note === undefined\n ? card.tile === undefined\n ? cardName(card)\n : tileName(card.tile)\n : `${card.tile === undefined ? cardName(card) : tileName(card.tile)}, ${card.note}`;\n\nconst clamp = (n: number, max: number): number => Math.max(0, Math.min(max, n));\n\nexport const initialHand = (layout: HandLayout): HandState => ({\n focus: 0,\n ids: layout.cards.map((c) => c.id),\n inside: false,\n});\n\n/** One step of the hand's controller: the new state, outputs, and a card to focus after rendering. */\nexport type HandStep = { readonly state: HandState; readonly outs: readonly HandOut[]; readonly focus: string | null };\n\n/**\n * Brings the state in line with a new hand. The focus stays on the same card if it is still\n * there, otherwise on the card now at its place; if focus was inside the hand and its card\n * left, the new card takes the focus.\n */\nexport const syncHand = (s: HandState, layout: HandLayout): HandStep => {\n const ids = layout.cards.map((c) => c.id);\n const same = ids.length === s.ids.length && ids.every((id, k) => id === s.ids[k]);\n if (same) return { state: s, outs: [], focus: null };\n const was = s.ids[s.focus];\n const kept = was === undefined ? -1 : ids.indexOf(was);\n const index = kept >= 0 ? kept : clamp(s.focus, ids.length - 1);\n const target = ids[index] ?? null;\n const refocus = s.inside && kept < 0 && target !== null;\n return {\n state: { focus: index, ids, inside: s.inside && target !== null },\n outs: [],\n focus: refocus ? target : null,\n };\n};\n\nconst activate = (layout: HandLayout, card: HandCard, via: 'enter' | 'space' | 'pointer'): HandOut[] => {\n if (layout.mode === 'choose') {\n if (via === 'enter') return [{ _tag: 'Commit' }];\n return card.playable ? [{ _tag: 'Toggle', id: card.id }] : [{ _tag: 'Refused', id: card.id }];\n }\n return card.playable ? [{ _tag: 'Play', id: card.id }] : [{ _tag: 'Refused', id: card.id }];\n};\n\n/** The hand's reducer, pure, so its behavior is tested without a DOM. */\nexport const handStep = (before: HandState, layout: HandLayout, m: HandMsg): HandStep => {\n const s = syncHand(before, layout).state;\n const cards = layout.cards;\n const last = cards.length - 1;\n switch (m._tag) {\n case 'Key': {\n const card = cards[s.focus];\n if (card === undefined) return { state: s, outs: [], focus: null };\n const moveTo = (index: number): HandStep => {\n const next = clamp(index, last);\n return { state: { ...s, focus: next, inside: true }, outs: [], focus: cards[next]!.id };\n };\n switch (m.key) {\n case 'ArrowLeft':\n return moveTo(s.focus - 1);\n case 'ArrowRight':\n return moveTo(s.focus + 1);\n case 'Home':\n return moveTo(0);\n case 'End':\n return moveTo(last);\n case 'Enter':\n return { state: s, outs: activate(layout, card, 'enter'), focus: null };\n case ' ':\n return { state: s, outs: activate(layout, card, 'space'), focus: null };\n default:\n return { state: s, outs: [], focus: null };\n }\n }\n case 'Press': {\n const index = cards.findIndex((c) => c.id === m.id);\n const card = cards[index];\n if (card === undefined) return { state: s, outs: [], focus: null };\n return { state: { ...s, focus: index, inside: true }, outs: activate(layout, card, 'pointer'), focus: null };\n }\n case 'Enter': {\n const index = cards.findIndex((c) => c.id === m.id);\n return { state: { ...s, focus: index >= 0 ? index : s.focus, inside: true }, outs: [], focus: null };\n }\n case 'Leave':\n return { state: { ...s, inside: false }, outs: [], focus: null };\n }\n};\n\n/** Keys the hand handles; their default actions (scrolling, a button's click) are suppressed. */\nconst HAND_KEYS = new Set(['ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' ']);\n\nconst emit = outputs<HandOut>();\nconst i = intentsOf<typeof CardHand>();\n\ntype CardView = {\n readonly focused: boolean;\n readonly mode: HandMode;\n readonly calm: boolean;\n readonly numbers: boolean;\n readonly draggable: boolean;\n};\n\nconst CardButton = (card: HandCard, v: CardView) =>\n html`<button\n type=\"button\"\n class=\"slot ${card.chosen === true ? 'chosen' : ''} ${card.playable ? '' : 'off'} ${card.note === undefined ? '' : 'noted'}\"\n data-card=${card.id}\n draggable=${v.draggable ? 'true' : 'false'}\n tabindex=${v.focused ? 0 : -1}\n aria-label=${handCardLabel(card)}\n aria-disabled=${card.playable ? null : 'true'}\n aria-pressed=${v.mode === 'choose' ? String(card.chosen === true) : null}\n data-intent=${i.Press}\n >\n <span class=\"cface s-${card.suit ?? 'tile'}\">${card.tile === undefined ? cardFace(card.rank, card.suit) : tileFace(card.tile, v.numbers)}</span>\n </button>`;\n\nconst run = (s: HandState, layout: HandLayout, m: HandMsg): HandState | readonly [HandState, Command<HandMsg>[]] => {\n const step = handStep(s, layout, m);\n const commands: Command<HandMsg>[] = step.outs.map((out) => emit(out));\n if (step.focus !== null) commands.push(focus(`[data-card=\"${step.focus}\"]`));\n return commands.length === 0 ? step.state : [step.state, commands];\n};\n\nexport const CardHand = define<\n HandState,\n HandMsg,\n { readonly layout: HandLayout; readonly options: HandOptions },\n HandOut\n>()('jy-card-hand', {\n props: {\n layout: prop.value(isHandLayout, { default: EMPTY_HAND }),\n options: prop.value(isHandOptions, { default: DEFAULT_HAND_OPTIONS }),\n },\n init: (p) => initialHand(p.layout),\n intent: {\n Key: ({ key, event }) => {\n if (\n key === undefined ||\n !(event instanceof KeyboardEvent) ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey\n )\n return undefined;\n if (!HAND_KEYS.has(key)) return undefined;\n event.preventDefault();\n return { _tag: 'Key', key };\n },\n Press: ({ event }) => {\n const id =\n event.target instanceof Element ? event.target.closest('[data-card]')?.getAttribute('data-card') : null;\n return typeof id === 'string' ? { _tag: 'Press', id } : undefined;\n },\n Enter: ({ event }) => {\n const id =\n event.target instanceof Element ? event.target.closest('[data-card]')?.getAttribute('data-card') : null;\n return typeof id === 'string' ? { _tag: 'Enter', id } : undefined;\n },\n Leave: ({ event, target }) => {\n const next = event instanceof FocusEvent ? event.relatedTarget : null;\n return next instanceof Node && target instanceof Node && target.contains(next) ? undefined : { _tag: 'Leave' };\n },\n },\n update: {\n Key: (s, m, { props: p }) => run(s, p.layout, m),\n Press: (s, m, { props: p }) => run(s, p.layout, m),\n Enter: (s, m, { props: p }) => run(s, p.layout, m),\n Leave: (s, m, { props: p }) => run(s, p.layout, m),\n PropsChanged: (s, { props: p }) => {\n const step = syncHand(s, p.layout);\n return step.focus === null ? step.state : [step.state, [focus(`[data-card=\"${step.focus}\"]`)]];\n },\n },\n view: (s, _i, { props: p }): ChildValue => {\n const { layout, options } = p;\n const focusIndex = clamp(s.focus, layout.cards.length - 1);\n return html`${suitDefs()}\n <div class=\"h-enter\" data-intent=${i.Enter} data-intent-on=\"focusin\">\n <div class=\"h-leave\" data-intent=${i.Leave} data-intent-on=\"focusout\">\n <div\n class=\"hand ${layout.cards.some((c) => c.tile !== undefined) ? 'tiles' : ''} n-${Math.min(layout.cards.length, 32)} ${options.fourColor ? 'four' : ''} ${options.calm ? 'calm' : ''} mode-${layout.mode}\"\n role=\"toolbar\"\n aria-label=${layout.label}\n data-intent=${i.Key}\n data-intent-on=\"keydown\"\n >\n ${each(\n layout.cards,\n (card) => card.id,\n CardButton,\n (card): CardView => ({\n focused: layout.cards[focusIndex]?.id === card.id,\n mode: layout.mode,\n calm: options.calm,\n numbers: options.numbers === true,\n draggable: options.draggable === true,\n }),\n )}\n </div>\n </div>\n </div>`;\n },\n styles: [faceStyles, handStyles],\n});\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'jy-card-hand': InstanceType<typeof CardHand>;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { CardTable, FLIGHT_MS, density, shownCards, type TableMsg, type TableProps, type TableState } from './table.js';
2
+ export { DRAG_THRESHOLD, describe, focusedPile, initialCtl, spoken, step, sync, type Ctl, type CtlMsg, type Held, type Press, type Step, type TableOut, type Via, } from './controller.js';
3
+ export { DEFAULT_OPTIONS, EMPTY_LAYOUT, allPiles, findPile, isFaceCard, isTableLayout, isTableOptions, isTarget, topCards, type Back, type DownCard, type FaceCard, type Fan, type HintSpec, type PileCard, type PileSpec, type Place, type RegionSpec, type TableLayout, type TableOptions, } from './layout.js';
4
+ export { PIPS, SUIT_PATHS, cardFace, rankLabel, suitDefs } from './faces.js';
5
+ export { tableStyles } from './styles.js';
6
+ export { faceStyles } from './face-styles.js';
7
+ export { CardHand, DEFAULT_HAND_OPTIONS, EMPTY_HAND, handCardLabel, handStep, initialHand, isHandLayout, isHandOptions, syncHand, type HandCard, type HandLayout, type HandMode, type HandMsg, type HandOptions, type HandOut, type HandState, type HandStep, } from './hand.js';
8
+ export { tileFace } from './tiles.js';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { DEFAULT_OPTIONS, EMPTY_LAYOUT, allPiles, findPile, isFaceCard, isTableLayout, isTableOptions, isTarget, topCards } from "./layout.js";
2
+ import { DRAG_THRESHOLD, describe, focusedPile, initialCtl, spoken, step, sync } from "./controller.js";
3
+ import { faceStyles } from "./face-styles.js";
4
+ import { PIPS, SUIT_PATHS, cardFace, rankLabel, suitDefs } from "./faces.js";
5
+ import { tileFace } from "./tiles.js";
6
+ import { CardHand, DEFAULT_HAND_OPTIONS, EMPTY_HAND, handCardLabel, handStep, initialHand, isHandLayout, isHandOptions, syncHand } from "./hand.js";
7
+ import { tableStyles } from "./styles.js";
8
+ import { CardTable, FLIGHT_MS, density, shownCards } from "./table.js";
9
+ export { CardHand, CardTable, DEFAULT_HAND_OPTIONS, DEFAULT_OPTIONS, DRAG_THRESHOLD, EMPTY_HAND, EMPTY_LAYOUT, FLIGHT_MS, PIPS, SUIT_PATHS, allPiles, cardFace, density, describe, faceStyles, findPile, focusedPile, handCardLabel, handStep, initialCtl, initialHand, isFaceCard, isHandLayout, isHandOptions, isTableLayout, isTableOptions, isTarget, rankLabel, shownCards, spoken, step, suitDefs, sync, syncHand, tableStyles, tileFace, topCards };