@joystyk/cards-ui 0.0.0-stage → 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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/NOTICE +36 -0
- package/README.md +32 -2
- package/dist/controller.d.ts +110 -0
- package/dist/controller.js +449 -0
- package/dist/controller.js.map +1 -0
- package/dist/face-styles.d.ts +1 -0
- package/dist/face-styles.js +106 -0
- package/dist/face-styles.js.map +1 -0
- package/dist/faces.d.ts +11 -0
- package/dist/faces.js +403 -0
- package/dist/faces.js.map +1 -0
- package/dist/hand-styles.d.ts +1 -0
- package/dist/hand-styles.js +121 -0
- package/dist/hand-styles.js.map +1 -0
- package/dist/hand.d.ts +100 -0
- package/dist/hand.js +480 -0
- package/dist/hand.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/layout.d.ts +122 -0
- package/dist/layout.js +35 -0
- package/dist/layout.js.map +1 -0
- package/dist/solitaire.d.ts +155 -0
- package/dist/solitaire.js +1508 -0
- package/dist/solitaire.js.map +1 -0
- package/dist/styles.d.ts +1 -0
- package/dist/styles.js +452 -0
- package/dist/styles.js.map +1 -0
- package/dist/table.d.ts +28 -0
- package/dist/table.js +629 -0
- package/dist/table.js.map +1 -0
- package/dist/tiles.d.ts +3 -0
- package/dist/tiles.js +157 -0
- package/dist/tiles.js.map +1 -0
- package/dist/worker-setup.d.ts +7 -0
- package/dist/worker-setup.js +45 -0
- package/dist/worker-setup.js.map +1 -0
- package/package.json +67 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","names":[],"sources":["../src/controller.ts"],"sourcesContent":["// The pile interaction controller: a pure state machine that turns pointer and keyboard\n// input on a card table into moves (klondike.md, \"Interaction and accessibility\"). It knows\n// no rules; the layout says how many cards each pile lets go of and where they may land. The\n// engine still decides every move, so a move the controller sends may be refused.\n//\n// - Pointer: press a face-up card and move 6 CSS pixels to drag it with the cards on top of\n// it; drop anywhere over a pile. A click without dragging picks the cards up; a click on\n// another pile moves them there; a second click on the same card is a smart move (so a\n// double-click or double-tap is one); a click elsewhere puts them back.\n// - Keyboard: one Tab stop per region (a roving tabindex), Left and Right between piles, Up\n// and Down for how many cards, Enter or Space to pick up and to drop (on the cards' own\n// pile: a smart move), Escape to put back, number keys for numbered piles, and the layout's\n// single-key shortcuts, all only while `singleKeys` is on (WCAG 2.1.4).\n//\n// Everything it says goes out as `Announce` outputs; the game passes them to the frame's\n// live region.\nimport { cardName } from '@joystyk/kit/cards';\nimport {\n findPile,\n isFaceCard,\n topCards,\n type FaceCard,\n type PileSpec,\n type TableLayout,\n type TableOptions,\n} from './layout.ts';\n\n/** Pointer travel, in CSS pixels, that turns a press into a drag (spec: Pointer). */\nexport const DRAG_THRESHOLD = 6;\n\nexport type Via = 'drag' | 'click' | 'keyboard';\n\n/** Cards the player has picked up: the top `count` of pile `from`. */\nexport type Held = { readonly from: string; readonly count: number; readonly via: Via };\n\n/** A pointer pressed on a pile; `depth` counts cards from the top (0: the pile, not a card). */\nexport type Press = {\n readonly pile: string | null;\n readonly depth: number;\n readonly x: number;\n readonly y: number;\n readonly dragging: boolean;\n readonly dx: number;\n readonly dy: number;\n readonly over: string | null;\n};\n\nexport type Ctl = {\n /** The focused region. */\n readonly region: number;\n /** The remembered pile of each region: the one Tab reaches (roving tabindex). */\n readonly piles: readonly number[];\n /** Cards counted from the top of the focused pile: what Enter picks up. */\n readonly depth: number;\n readonly held: Held | null;\n readonly press: Press | null;\n /** The layout version last seen. */\n readonly version: number;\n /** A drag just dropped: the next layout lands without card flights. */\n readonly settle: boolean;\n /** The layout version that renders without card flights. */\n readonly quiet: number | null;\n};\n\n/** What the table tells the game. */\nexport type TableOut =\n | { readonly _tag: 'Move'; readonly from: string; readonly to: string; readonly count: number; readonly via: Via }\n | { readonly _tag: 'Smart'; readonly from: string; readonly count: number; readonly via: Via }\n | { readonly _tag: 'Activate'; readonly pile: string }\n | { readonly _tag: 'Key'; readonly key: string; readonly pile: string; readonly count: number }\n | { readonly _tag: 'Announce'; readonly message: string };\n\nexport type CtlMsg =\n | {\n readonly _tag: 'Key';\n readonly key: string;\n readonly ctrl: boolean;\n readonly alt: boolean;\n readonly meta: boolean;\n readonly shift: boolean;\n }\n | { readonly _tag: 'Focus'; readonly pile: string }\n | {\n readonly _tag: 'Down';\n readonly pile: string | null;\n readonly depth: number;\n readonly x: number;\n readonly y: number;\n }\n | { readonly _tag: 'Move'; readonly x: number; readonly y: number; readonly over: string | null }\n | { readonly _tag: 'Up'; readonly over: string | null }\n | { readonly _tag: 'Cancel' };\n\n/** One step: the new controller state, outputs, and a pile to focus after rendering. */\nexport type Step = { readonly ctl: Ctl; readonly outs: readonly TableOut[]; readonly focus: string | null };\n\nexport const initialCtl = (layout: TableLayout): Ctl => {\n const first = layout.regions.findIndex((r) => r.piles.length > 0);\n return {\n region: Math.max(0, first),\n piles: layout.regions.map(() => 0),\n depth: 1,\n held: null,\n press: null,\n version: layout.version,\n settle: false,\n quiet: null,\n };\n};\n\nconst clamp = (n: number, min: number, max: number): number => Math.min(max, Math.max(min, n));\n\n/**\n * Brings the controller in line with a new layout: indexes stay in range, and a new version\n * (the cards changed) drops any pick-up or press.\n */\nexport const sync = (ctl: Ctl, layout: TableLayout): Ctl => {\n const piles = layout.regions.map((r, i) => clamp(ctl.piles[i] ?? 0, 0, Math.max(0, r.piles.length - 1)));\n let region = clamp(ctl.region, 0, Math.max(0, layout.regions.length - 1));\n if ((layout.regions[region]?.piles.length ?? 0) === 0)\n region = Math.max(\n 0,\n layout.regions.findIndex((r) => r.piles.length > 0),\n );\n const moved = layout.version !== ctl.version;\n const same = piles.length === ctl.piles.length && piles.every((p, i) => p === ctl.piles[i]) && region === ctl.region;\n if (!moved && same) return ctl;\n if (!moved) return { ...ctl, piles, region };\n return {\n ...ctl,\n piles,\n region,\n depth: 1,\n held: null,\n press: null,\n version: layout.version,\n settle: false,\n quiet: ctl.settle ? layout.version : null,\n };\n};\n\n/** The focused pile, if the table has any. */\nexport const focusedPile = (ctl: Ctl, layout: TableLayout): PileSpec | null =>\n layout.regions[ctl.region]?.piles[ctl.piles[ctl.region] ?? 0] ?? null;\n\n/** A card's name for the middle of a sentence: \"queen of hearts\", \"7 of clubs\". */\nexport const spoken = (card: FaceCard): string => cardName(card).toLowerCase();\n\nconst capital = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1);\n\n/** \"9 of spades\", or \"9 of spades and 2 more cards\": the cards a pick-up of `count` takes. */\nexport const describe = (pile: PileSpec, count: number): string => {\n const [first] = topCards(pile, count);\n if (first === undefined) return 'nothing';\n const more = count - 1;\n return more === 0 ? spoken(first) : `${spoken(first)} and ${more} more ${more === 1 ? 'card' : 'cards'}`;\n};\n\nconst none = (ctl: Ctl): Step => ({ ctl, outs: [], focus: null });\n\nconst say = (message: string): TableOut => ({ _tag: 'Announce', message });\n\n/** Picks up `count` cards from `pile` and says so. */\nconst pickUp = (ctl: Ctl, pile: PileSpec, count: number, via: Via): Step => {\n const note = pile.note === undefined ? '' : ` ${pile.note}`;\n const help = via === 'keyboard' ? ' Choose a pile, then press Enter.' : '';\n return {\n ctl: { ...ctl, held: { from: pile.id, count, via } },\n outs: [say(`Picked up ${describe(pile, count)}.${note}${help}`)],\n focus: null,\n };\n};\n\n/** Drops the held cards on `to`: a move, or a smart move on their own pile. */\nconst drop = (ctl: Ctl, held: Held, to: string, via: Via): Step => {\n const cleared = { ...ctl, held: null };\n const out: TableOut =\n to === held.from\n ? { _tag: 'Smart', from: held.from, count: held.count, via }\n : { _tag: 'Move', from: held.from, to, count: held.count, via };\n return { ctl: cleared, outs: [out], focus: null };\n};\n\nconst putBack = (ctl: Ctl, layout: TableLayout): Step => {\n const held = ctl.held;\n if (held === null) return none(ctl);\n const pile = findPile(layout, held.from)?.pile;\n return {\n ctl: { ...ctl, held: null },\n outs: [say(pile === undefined ? 'Put back.' : `Put back ${describe(pile, held.count)}.`)],\n focus: null,\n };\n};\n\n/** Moves the focus to pile `index` of the focused region. */\nconst focusAt = (ctl: Ctl, layout: TableLayout, region: number, index: number): Step => {\n const pile = layout.regions[region]?.piles[index];\n if (pile === undefined) return none(ctl);\n const piles = ctl.piles.slice();\n piles[region] = index;\n const changed = region !== ctl.region || index !== ctl.piles[region];\n return { ctl: { ...ctl, region, piles, depth: changed ? 1 : ctl.depth }, outs: [], focus: pile.id };\n};\n\n/** Enter or Space on the focused pile. */\nconst activate = (ctl: Ctl, pile: PileSpec): Step => {\n if (ctl.held !== null) return drop(ctl, ctl.held, pile.id, 'keyboard');\n if (pile.action === true) return { ctl, outs: [{ _tag: 'Activate', pile: pile.id }], focus: null };\n if (pile.pickable >= 1) return pickUp(ctl, pile, clamp(ctl.depth, 1, pile.pickable), 'keyboard');\n return {\n ctl,\n outs: [say(pile.cards.length === 0 ? 'Nothing to pick up: the pile is empty.' : 'Nothing here can be picked up.')],\n focus: null,\n };\n};\n\n/** Up and Down: how many cards Enter picks up. */\nconst deepen = (ctl: Ctl, pile: PileSpec, by: 1 | -1): Step => {\n if (pile.inspect === true && ctl.held === null) {\n const depth = clamp(ctl.depth + by, 1, Math.max(1, pile.cards.length));\n const card = pile.cards[pile.cards.length - depth];\n return { ctl: { ...ctl, depth }, outs: isFaceCard(card) ? [say(capital(spoken(card)))] : [], focus: null };\n }\n if (ctl.held !== null || pile.pickable < 1) return none(ctl);\n const depth = clamp(ctl.depth, 1, pile.pickable);\n const next = depth + by;\n if (next < 1) return { ctl: { ...ctl, depth }, outs: [], focus: null };\n if (next > pile.pickable)\n return { ctl: { ...ctl, depth }, outs: pile.limit === undefined ? [] : [say(pile.limit)], focus: null };\n return { ctl: { ...ctl, depth: next }, outs: [say(capital(describe(pile, next)))], focus: null };\n};\n\nconst isDigit = (key: string): boolean => key.length === 1 && key >= '0' && key <= '9';\n\n/** Navigate a geometric layout without consulting the DOM or skipping covered cards. */\nconst geometricFocus = (ctl: Ctl, layout: TableLayout, key: string): Step | null => {\n const region = layout.regions[ctl.region];\n const here = focusedPile(ctl, layout)?.position;\n if (region?.placement !== 'free' || here === undefined) return null;\n const candidates = region.piles\n .map((pile, index) => ({ index, position: pile.position }))\n .filter((p) => p.position !== undefined);\n const row =\n key === 'ArrowUp'\n ? Math.max(-1, ...candidates.filter((p) => p.position!.row < here.row).map((p) => p.position!.row))\n : key === 'ArrowDown'\n ? Math.min(Infinity, ...candidates.filter((p) => p.position!.row > here.row).map((p) => p.position!.row))\n : here.row;\n let choices = candidates.filter((p) => p.position!.row === row);\n if (key === 'ArrowLeft') choices = choices.filter((p) => p.position!.column < here.column);\n if (key === 'ArrowRight') choices = choices.filter((p) => p.position!.column > here.column);\n choices.sort((a, b) =>\n key === 'Home'\n ? a.position!.column - b.position!.column\n : key === 'End'\n ? b.position!.column - a.position!.column\n : Math.abs(a.position!.column - here.column) - Math.abs(b.position!.column - here.column) ||\n a.position!.column - b.position!.column,\n );\n return choices[0] === undefined ? none(ctl) : focusAt(ctl, layout, ctl.region, choices[0].index);\n};\n\nconst onKey = (ctl: Ctl, layout: TableLayout, options: TableOptions, m: Extract<CtlMsg, { _tag: 'Key' }>): Step => {\n const pile = focusedPile(ctl, layout);\n if (pile === null || m.alt || m.meta || m.ctrl) return none(ctl);\n const region = layout.regions[ctl.region];\n const count = region?.piles.length ?? 0;\n const index = ctl.piles[ctl.region] ?? 0;\n if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(m.key)) {\n const spatial = geometricFocus(ctl, layout, m.key);\n if (spatial !== null) return spatial;\n }\n switch (m.key) {\n case 'ArrowLeft':\n return focusAt(ctl, layout, ctl.region, Math.max(0, index - 1));\n case 'ArrowRight':\n return focusAt(ctl, layout, ctl.region, Math.min(count - 1, index + 1));\n case 'Home':\n return focusAt(ctl, layout, ctl.region, 0);\n case 'End':\n return focusAt(ctl, layout, ctl.region, count - 1);\n case 'ArrowUp':\n return deepen(ctl, pile, 1);\n case 'ArrowDown':\n return deepen(ctl, pile, -1);\n case 'Enter':\n case ' ':\n return activate(ctl, pile);\n case 'Escape':\n return putBack(ctl, layout);\n }\n if (!options.singleKeys || m.shift) return none(ctl);\n const key = m.key.toLowerCase();\n if (isDigit(key)) {\n const target = layout.regions\n .flatMap((r, ri) => r.piles.map((p, pi) => ({ p, ri, pi })))\n .find(({ p }) => p.key === key);\n if (target === undefined) return none(ctl);\n const moved = focusAt(ctl, layout, target.ri, target.pi);\n if (ctl.held === null)\n return layout.activateNumbers === true ? { ...activate(moved.ctl, target.p), focus: moved.focus } : moved;\n const dropped = drop(moved.ctl, ctl.held, target.p.id, 'keyboard');\n return { ...dropped, focus: moved.focus };\n }\n if (layout.keys.includes(key)) {\n if (key === 'n' || key === 't' || key === 'w') {\n const piles = layout.regions.flatMap((r, ri) => r.piles.map((p, pi) => ({ p, ri, pi })));\n const available = piles\n .filter(({ p }) => p.pickable > 0)\n .sort(\n (a, b) =>\n (b.p.position?.row ?? -1) - (a.p.position?.row ?? -1) ||\n (a.p.position?.column ?? a.pi) - (b.p.position?.column ?? b.pi),\n );\n const current = available.findIndex(({ p }) => p.id === pile.id);\n const next =\n key === 'n'\n ? available[(current + 1) % Math.max(1, available.length)]\n : piles.find(({ p }) => p.id === (key === 't' ? 'turned' : 'waste'));\n if (next !== undefined) return focusAt(ctl, layout, next.ri, next.pi);\n return none(ctl);\n }\n const held = ctl.held;\n const out: TableOut =\n held === null\n ? { _tag: 'Key', key, pile: pile.id, count: clamp(ctl.depth, 1, Math.max(1, pile.pickable)) }\n : { _tag: 'Key', key, pile: held.from, count: held.count };\n return { ctl: { ...ctl, held: null }, outs: [out], focus: null };\n }\n return none(ctl);\n};\n\n/** A click (a press released without dragging) on `pileId` at `depth`. */\nconst click = (ctl: Ctl, layout: TableLayout, pileId: string | null, depth: number): Step => {\n const found = pileId === null ? null : findPile(layout, pileId);\n if (found === null) return putBack(ctl, layout);\n const { pile } = found;\n const focused = {\n ...ctl,\n region: found.region,\n piles: ctl.piles.map((p, i) => (i === found.region ? found.index : p)),\n };\n const held = ctl.held;\n if (held !== null) {\n if (pile.id !== held.from) return drop(focused, held, pile.id, 'click');\n if (depth === held.count) return drop(focused, held, pile.id, 'click');\n if (depth >= 1 && depth <= pile.pickable) return pickUp({ ...focused, depth }, pile, depth, 'click');\n return putBack(focused, layout);\n }\n if (pile.action === true) return { ctl: focused, outs: [{ _tag: 'Activate', pile: pile.id }], focus: null };\n if (depth >= 1 && depth <= pile.pickable) return pickUp({ ...focused, depth }, pile, depth, 'click');\n return none(focused);\n};\n\n/** The controller's reducer. */\nexport const step = (before: Ctl, layout: TableLayout, options: TableOptions, m: CtlMsg): Step => {\n const ctl = sync(before, layout);\n switch (m._tag) {\n case 'Key':\n return onKey(ctl, layout, options, m);\n case 'Focus': {\n const found = findPile(layout, m.pile);\n if (found === null) return none(ctl);\n const same = found.region === ctl.region && found.index === ctl.piles[found.region];\n if (same) return none(ctl);\n const piles = ctl.piles.slice();\n piles[found.region] = found.index;\n return none({ ...ctl, region: found.region, piles, depth: 1 });\n }\n case 'Down': {\n // A drag whose release never arrived (the pointer left the window) is dropped back first.\n const stale = ctl.press?.dragging === true;\n return none({\n ...ctl,\n held: stale ? null : ctl.held,\n press: { pile: m.pile, depth: m.depth, x: m.x, y: m.y, dragging: false, dx: 0, dy: 0, over: null },\n });\n }\n case 'Move': {\n const press = ctl.press;\n if (press === null || press.pile === null) return none(ctl);\n const dx = m.x - press.x;\n const dy = m.y - press.y;\n if (press.dragging) return none({ ...ctl, press: { ...press, dx, dy, over: m.over } });\n if (Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return none(ctl);\n const pile = findPile(layout, press.pile)?.pile;\n const top = pile?.cards[pile.cards.length - press.depth];\n if (pile === undefined || press.depth < 1 || press.depth > pile.pickable || !isFaceCard(top))\n return none({ ...ctl, press: null });\n return none({\n ...ctl,\n held: { from: pile.id, count: press.depth, via: 'drag' },\n press: { ...press, dragging: true, dx, dy, over: m.over },\n });\n }\n case 'Up': {\n const press = ctl.press;\n if (press === null) return none(ctl);\n const released = { ...ctl, press: null };\n if (!press.dragging) return click(released, layout, press.pile, press.depth);\n const held = ctl.held;\n const to = m.over;\n if (held === null || to === null || to === held.from) return none({ ...released, held: null });\n return {\n ctl: { ...released, held: null, settle: true },\n outs: [{ _tag: 'Move', from: held.from, to, count: held.count, via: 'drag' }],\n focus: null,\n };\n }\n case 'Cancel': {\n const dragging = ctl.press?.dragging === true;\n return none({ ...ctl, press: null, held: dragging ? null : ctl.held });\n }\n }\n};\n\n/** True when the table should render this layout without card flights. */\nexport const isQuiet = (ctl: Ctl, layout: TableLayout, options: TableOptions): boolean =>\n options.calm || ctl.quiet === layout.version;\n"],"mappings":";;;;AA4BA,IAAa,iBAAiB;AAoE9B,IAAa,cAAc,WAA6B;CACtD,MAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,EAAE,MAAM,SAAS,CAAC;CAChE,OAAO;EACL,QAAQ,KAAK,IAAI,GAAG,KAAK;EACzB,OAAO,OAAO,QAAQ,UAAU,CAAC;EACjC,OAAO;EACP,MAAM;EACN,OAAO;EACP,SAAS,OAAO;EAChB,QAAQ;EACR,OAAO;CACT;AACF;AAEA,IAAM,SAAS,GAAW,KAAa,QAAwB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;;;;;AAM7F,IAAa,QAAQ,KAAU,WAA6B;CAC1D,MAAM,QAAQ,OAAO,QAAQ,KAAK,GAAG,MAAM,MAAM,IAAI,MAAM,MAAM,GAAG,GAAG,KAAK,IAAI,GAAG,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC;CACvG,IAAI,SAAS,MAAM,IAAI,QAAQ,GAAG,KAAK,IAAI,GAAG,OAAO,QAAQ,SAAS,CAAC,CAAC;CACxE,KAAK,OAAO,QAAQ,OAAO,EAAE,MAAM,UAAU,OAAO,GAClD,SAAS,KAAK,IACZ,GACA,OAAO,QAAQ,WAAW,MAAM,EAAE,MAAM,SAAS,CAAC,CACpD;CACF,MAAM,QAAQ,OAAO,YAAY,IAAI;CACrC,MAAM,OAAO,MAAM,WAAW,IAAI,MAAM,UAAU,MAAM,OAAO,GAAG,MAAM,MAAM,IAAI,MAAM,EAAE,KAAK,WAAW,IAAI;CAC9G,IAAI,CAAC,SAAS,MAAM,OAAO;CAC3B,IAAI,CAAC,OAAO,OAAO;EAAE,GAAG;EAAK;EAAO;CAAO;CAC3C,OAAO;EACL,GAAG;EACH;EACA;EACA,OAAO;EACP,MAAM;EACN,OAAO;EACP,SAAS,OAAO;EAChB,QAAQ;EACR,OAAO,IAAI,SAAS,OAAO,UAAU;CACvC;AACF;;AAGA,IAAa,eAAe,KAAU,WACpC,OAAO,QAAQ,IAAI,OAAO,EAAE,MAAM,IAAI,MAAM,IAAI,WAAW,MAAM;;AAGnE,IAAa,UAAU,SAA2B,SAAS,IAAI,CAAC,CAAC,YAAY;AAE7E,IAAM,WAAW,MAAsB,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,MAAM,CAAC;;AAG5E,IAAa,YAAY,MAAgB,UAA0B;CACjE,MAAM,CAAC,SAAS,SAAS,MAAM,KAAK;CACpC,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,MAAM,OAAO,QAAQ;CACrB,OAAO,SAAS,IAAI,OAAO,KAAK,IAAI,GAAG,OAAO,KAAK,EAAE,OAAO,KAAK,QAAQ,SAAS,IAAI,SAAS;AACjG;AAEA,IAAM,QAAQ,SAAoB;CAAE;CAAK,MAAM,CAAC;CAAG,OAAO;AAAK;AAE/D,IAAM,OAAO,aAA+B;CAAE,MAAM;CAAY;AAAQ;;AAGxE,IAAM,UAAU,KAAU,MAAgB,OAAe,QAAmB;CAC1E,MAAM,OAAO,KAAK,SAAS,KAAA,IAAY,KAAK,IAAI,KAAK;CACrD,MAAM,OAAO,QAAQ,aAAa,sCAAsC;CACxE,OAAO;EACL,KAAK;GAAE,GAAG;GAAK,MAAM;IAAE,MAAM,KAAK;IAAI;IAAO;GAAI;EAAE;EACnD,MAAM,CAAC,IAAI,aAAa,SAAS,MAAM,KAAK,EAAE,GAAG,OAAO,MAAM,CAAC;EAC/D,OAAO;CACT;AACF;;AAGA,IAAM,QAAQ,KAAU,MAAY,IAAY,QAAmB;CAMjE,OAAO;EAAE,KAAK;GALI,GAAG;GAAK,MAAM;EAKlB;EAAS,MAAM,CAH3B,OAAO,KAAK,OACR;GAAE,MAAM;GAAS,MAAM,KAAK;GAAM,OAAO,KAAK;GAAO;EAAI,IACzD;GAAE,MAAM;GAAQ,MAAM,KAAK;GAAM;GAAI,OAAO,KAAK;GAAO;EAAI,CACjC;EAAG,OAAO;CAAK;AAClD;AAEA,IAAM,WAAW,KAAU,WAA8B;CACvD,MAAM,OAAO,IAAI;CACjB,IAAI,SAAS,MAAM,OAAO,KAAK,GAAG;CAClC,MAAM,OAAO,SAAS,QAAQ,KAAK,IAAI,CAAC,EAAE;CAC1C,OAAO;EACL,KAAK;GAAE,GAAG;GAAK,MAAM;EAAK;EAC1B,MAAM,CAAC,IAAI,SAAS,KAAA,IAAY,cAAc,YAAY,SAAS,MAAM,KAAK,KAAK,EAAE,EAAE,CAAC;EACxF,OAAO;CACT;AACF;;AAGA,IAAM,WAAW,KAAU,QAAqB,QAAgB,UAAwB;CACtF,MAAM,OAAO,OAAO,QAAQ,OAAO,EAAE,MAAM;CAC3C,IAAI,SAAS,KAAA,GAAW,OAAO,KAAK,GAAG;CACvC,MAAM,QAAQ,IAAI,MAAM,MAAM;CAC9B,MAAM,UAAU;CAChB,MAAM,UAAU,WAAW,IAAI,UAAU,UAAU,IAAI,MAAM;CAC7D,OAAO;EAAE,KAAK;GAAE,GAAG;GAAK;GAAQ;GAAO,OAAO,UAAU,IAAI,IAAI;EAAM;EAAG,MAAM,CAAC;EAAG,OAAO,KAAK;CAAG;AACpG;;AAGA,IAAM,YAAY,KAAU,SAAyB;CACnD,IAAI,IAAI,SAAS,MAAM,OAAO,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,UAAU;CACrE,IAAI,KAAK,WAAW,MAAM,OAAO;EAAE;EAAK,MAAM,CAAC;GAAE,MAAM;GAAY,MAAM,KAAK;EAAG,CAAC;EAAG,OAAO;CAAK;CACjG,IAAI,KAAK,YAAY,GAAG,OAAO,OAAO,KAAK,MAAM,MAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,GAAG,UAAU;CAC/F,OAAO;EACL;EACA,MAAM,CAAC,IAAI,KAAK,MAAM,WAAW,IAAI,2CAA2C,gCAAgC,CAAC;EACjH,OAAO;CACT;AACF;;AAGA,IAAM,UAAU,KAAU,MAAgB,OAAqB;CAC7D,IAAI,KAAK,YAAY,QAAQ,IAAI,SAAS,MAAM;EAC9C,MAAM,QAAQ,MAAM,IAAI,QAAQ,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;EACrE,MAAM,OAAO,KAAK,MAAM,KAAK,MAAM,SAAS;EAC5C,OAAO;GAAE,KAAK;IAAE,GAAG;IAAK;GAAM;GAAG,MAAM,WAAW,IAAI,IAAI,CAAC,IAAI,QAAQ,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;GAAG,OAAO;EAAK;CAC3G;CACA,IAAI,IAAI,SAAS,QAAQ,KAAK,WAAW,GAAG,OAAO,KAAK,GAAG;CAC3D,MAAM,QAAQ,MAAM,IAAI,OAAO,GAAG,KAAK,QAAQ;CAC/C,MAAM,OAAO,QAAQ;CACrB,IAAI,OAAO,GAAG,OAAO;EAAE,KAAK;GAAE,GAAG;GAAK;EAAM;EAAG,MAAM,CAAC;EAAG,OAAO;CAAK;CACrE,IAAI,OAAO,KAAK,UACd,OAAO;EAAE,KAAK;GAAE,GAAG;GAAK;EAAM;EAAG,MAAM,KAAK,UAAU,KAAA,IAAY,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC;EAAG,OAAO;CAAK;CACxG,OAAO;EAAE,KAAK;GAAE,GAAG;GAAK,OAAO;EAAK;EAAG,MAAM,CAAC,IAAI,QAAQ,SAAS,MAAM,IAAI,CAAC,CAAC,CAAC;EAAG,OAAO;CAAK;AACjG;AAEA,IAAM,WAAW,QAAyB,IAAI,WAAW,KAAK,OAAO,OAAO,OAAO;;AAGnF,IAAM,kBAAkB,KAAU,QAAqB,QAA6B;CAClF,MAAM,SAAS,OAAO,QAAQ,IAAI;CAClC,MAAM,OAAO,YAAY,KAAK,MAAM,CAAC,EAAE;CACvC,IAAI,QAAQ,cAAc,UAAU,SAAS,KAAA,GAAW,OAAO;CAC/D,MAAM,aAAa,OAAO,MACvB,KAAK,MAAM,WAAW;EAAE;EAAO,UAAU,KAAK;CAAS,EAAE,CAAC,CAC1D,QAAQ,MAAM,EAAE,aAAa,KAAA,CAAS;CACzC,MAAM,MACJ,QAAQ,YACJ,KAAK,IAAI,IAAI,GAAG,WAAW,QAAQ,MAAM,EAAE,SAAU,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,SAAU,GAAG,CAAC,IAChG,QAAQ,cACN,KAAK,IAAI,UAAU,GAAG,WAAW,QAAQ,MAAM,EAAE,SAAU,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,SAAU,GAAG,CAAC,IACtG,KAAK;CACb,IAAI,UAAU,WAAW,QAAQ,MAAM,EAAE,SAAU,QAAQ,GAAG;CAC9D,IAAI,QAAQ,aAAa,UAAU,QAAQ,QAAQ,MAAM,EAAE,SAAU,SAAS,KAAK,MAAM;CACzF,IAAI,QAAQ,cAAc,UAAU,QAAQ,QAAQ,MAAM,EAAE,SAAU,SAAS,KAAK,MAAM;CAC1F,QAAQ,MAAM,GAAG,MACf,QAAQ,SACJ,EAAE,SAAU,SAAS,EAAE,SAAU,SACjC,QAAQ,QACN,EAAE,SAAU,SAAS,EAAE,SAAU,SACjC,KAAK,IAAI,EAAE,SAAU,SAAS,KAAK,MAAM,IAAI,KAAK,IAAI,EAAE,SAAU,SAAS,KAAK,MAAM,KACtF,EAAE,SAAU,SAAS,EAAE,SAAU,MACzC;CACA,OAAO,QAAQ,OAAO,KAAA,IAAY,KAAK,GAAG,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,QAAQ,EAAE,CAAC,KAAK;AACjG;AAEA,IAAM,SAAS,KAAU,QAAqB,SAAuB,MAA8C;CACjH,MAAM,OAAO,YAAY,KAAK,MAAM;CACpC,IAAI,SAAS,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,KAAK,GAAG;CAE/D,MAAM,QADS,OAAO,QAAQ,IAAI,OACpB,EAAQ,MAAM,UAAU;CACtC,MAAM,QAAQ,IAAI,MAAM,IAAI,WAAW;CACvC,IAAI;EAAC;EAAa;EAAc;EAAW;EAAa;EAAQ;CAAK,CAAC,CAAC,SAAS,EAAE,GAAG,GAAG;EACtF,MAAM,UAAU,eAAe,KAAK,QAAQ,EAAE,GAAG;EACjD,IAAI,YAAY,MAAM,OAAO;CAC/B;CACA,QAAQ,EAAE,KAAV;EACE,KAAK,aACH,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC;EAChE,KAAK,cACH,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,QAAQ,GAAG,QAAQ,CAAC,CAAC;EACxE,KAAK,QACH,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC;EAC3C,KAAK,OACH,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,QAAQ,CAAC;EACnD,KAAK,WACH,OAAO,OAAO,KAAK,MAAM,CAAC;EAC5B,KAAK,aACH,OAAO,OAAO,KAAK,MAAM,EAAE;EAC7B,KAAK;EACL,KAAK,KACH,OAAO,SAAS,KAAK,IAAI;EAC3B,KAAK,UACH,OAAO,QAAQ,KAAK,MAAM;CAC9B;CACA,IAAI,CAAC,QAAQ,cAAc,EAAE,OAAO,OAAO,KAAK,GAAG;CACnD,MAAM,MAAM,EAAE,IAAI,YAAY;CAC9B,IAAI,QAAQ,GAAG,GAAG;EAChB,MAAM,SAAS,OAAO,QACnB,SAAS,GAAG,OAAO,EAAE,MAAM,KAAK,GAAG,QAAQ;GAAE;GAAG;GAAI;EAAG,EAAE,CAAC,CAAC,CAC3D,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG;EAChC,IAAI,WAAW,KAAA,GAAW,OAAO,KAAK,GAAG;EACzC,MAAM,QAAQ,QAAQ,KAAK,QAAQ,OAAO,IAAI,OAAO,EAAE;EACvD,IAAI,IAAI,SAAS,MACf,OAAO,OAAO,oBAAoB,OAAO;GAAE,GAAG,SAAS,MAAM,KAAK,OAAO,CAAC;GAAG,OAAO,MAAM;EAAM,IAAI;EAEtG,OAAO;GAAE,GADO,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,EAAE,IAAI,UAC3C;GAAS,OAAO,MAAM;EAAM;CAC1C;CACA,IAAI,OAAO,KAAK,SAAS,GAAG,GAAG;EAC7B,IAAI,QAAQ,OAAO,QAAQ,OAAO,QAAQ,KAAK;GAC7C,MAAM,QAAQ,OAAO,QAAQ,SAAS,GAAG,OAAO,EAAE,MAAM,KAAK,GAAG,QAAQ;IAAE;IAAG;IAAI;GAAG,EAAE,CAAC;GACvF,MAAM,YAAY,MACf,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CACjC,MACE,GAAG,OACD,EAAE,EAAE,UAAU,OAAO,OAAO,EAAE,EAAE,UAAU,OAAO,QACjD,EAAE,EAAE,UAAU,UAAU,EAAE,OAAO,EAAE,EAAE,UAAU,UAAU,EAAE,GAChE;GACF,MAAM,UAAU,UAAU,WAAW,EAAE,QAAQ,EAAE,OAAO,KAAK,EAAE;GAC/D,MAAM,OACJ,QAAQ,MACJ,WAAW,UAAU,KAAK,KAAK,IAAI,GAAG,UAAU,MAAM,KACtD,MAAM,MAAM,EAAE,QAAQ,EAAE,QAAQ,QAAQ,MAAM,WAAW,QAAQ;GACvE,IAAI,SAAS,KAAA,GAAW,OAAO,QAAQ,KAAK,QAAQ,KAAK,IAAI,KAAK,EAAE;GACpE,OAAO,KAAK,GAAG;EACjB;EACA,MAAM,OAAO,IAAI;EACjB,MAAM,MACJ,SAAS,OACL;GAAE,MAAM;GAAO;GAAK,MAAM,KAAK;GAAI,OAAO,MAAM,IAAI,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,QAAQ,CAAC;EAAE,IAC1F;GAAE,MAAM;GAAO;GAAK,MAAM,KAAK;GAAM,OAAO,KAAK;EAAM;EAC7D,OAAO;GAAE,KAAK;IAAE,GAAG;IAAK,MAAM;GAAK;GAAG,MAAM,CAAC,GAAG;GAAG,OAAO;EAAK;CACjE;CACA,OAAO,KAAK,GAAG;AACjB;;AAGA,IAAM,SAAS,KAAU,QAAqB,QAAuB,UAAwB;CAC3F,MAAM,QAAQ,WAAW,OAAO,OAAO,SAAS,QAAQ,MAAM;CAC9D,IAAI,UAAU,MAAM,OAAO,QAAQ,KAAK,MAAM;CAC9C,MAAM,EAAE,SAAS;CACjB,MAAM,UAAU;EACd,GAAG;EACH,QAAQ,MAAM;EACd,OAAO,IAAI,MAAM,KAAK,GAAG,MAAO,MAAM,MAAM,SAAS,MAAM,QAAQ,CAAE;CACvE;CACA,MAAM,OAAO,IAAI;CACjB,IAAI,SAAS,MAAM;EACjB,IAAI,KAAK,OAAO,KAAK,MAAM,OAAO,KAAK,SAAS,MAAM,KAAK,IAAI,OAAO;EACtE,IAAI,UAAU,KAAK,OAAO,OAAO,KAAK,SAAS,MAAM,KAAK,IAAI,OAAO;EACrE,IAAI,SAAS,KAAK,SAAS,KAAK,UAAU,OAAO,OAAO;GAAE,GAAG;GAAS;EAAM,GAAG,MAAM,OAAO,OAAO;EACnG,OAAO,QAAQ,SAAS,MAAM;CAChC;CACA,IAAI,KAAK,WAAW,MAAM,OAAO;EAAE,KAAK;EAAS,MAAM,CAAC;GAAE,MAAM;GAAY,MAAM,KAAK;EAAG,CAAC;EAAG,OAAO;CAAK;CAC1G,IAAI,SAAS,KAAK,SAAS,KAAK,UAAU,OAAO,OAAO;EAAE,GAAG;EAAS;CAAM,GAAG,MAAM,OAAO,OAAO;CACnG,OAAO,KAAK,OAAO;AACrB;;AAGA,IAAa,QAAQ,QAAa,QAAqB,SAAuB,MAAoB;CAChG,MAAM,MAAM,KAAK,QAAQ,MAAM;CAC/B,QAAQ,EAAE,MAAV;EACE,KAAK,OACH,OAAO,MAAM,KAAK,QAAQ,SAAS,CAAC;EACtC,KAAK,SAAS;GACZ,MAAM,QAAQ,SAAS,QAAQ,EAAE,IAAI;GACrC,IAAI,UAAU,MAAM,OAAO,KAAK,GAAG;GAEnC,IADa,MAAM,WAAW,IAAI,UAAU,MAAM,UAAU,IAAI,MAAM,MAAM,SAClE,OAAO,KAAK,GAAG;GACzB,MAAM,QAAQ,IAAI,MAAM,MAAM;GAC9B,MAAM,MAAM,UAAU,MAAM;GAC5B,OAAO,KAAK;IAAE,GAAG;IAAK,QAAQ,MAAM;IAAQ;IAAO,OAAO;GAAE,CAAC;EAC/D;EACA,KAAK,QAAQ;GAEX,MAAM,QAAQ,IAAI,OAAO,aAAa;GACtC,OAAO,KAAK;IACV,GAAG;IACH,MAAM,QAAQ,OAAO,IAAI;IACzB,OAAO;KAAE,MAAM,EAAE;KAAM,OAAO,EAAE;KAAO,GAAG,EAAE;KAAG,GAAG,EAAE;KAAG,UAAU;KAAO,IAAI;KAAG,IAAI;KAAG,MAAM;IAAK;GACnG,CAAC;EACH;EACA,KAAK,QAAQ;GACX,MAAM,QAAQ,IAAI;GAClB,IAAI,UAAU,QAAQ,MAAM,SAAS,MAAM,OAAO,KAAK,GAAG;GAC1D,MAAM,KAAK,EAAE,IAAI,MAAM;GACvB,MAAM,KAAK,EAAE,IAAI,MAAM;GACvB,IAAI,MAAM,UAAU,OAAO,KAAK;IAAE,GAAG;IAAK,OAAO;KAAE,GAAG;KAAO;KAAI;KAAI,MAAM,EAAE;IAAK;GAAE,CAAC;GACrF,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE,IAAA,GAAoB,OAAO,KAAK,GAAG;GAClE,MAAM,OAAO,SAAS,QAAQ,MAAM,IAAI,CAAC,EAAE;GAC3C,MAAM,MAAM,MAAM,MAAM,KAAK,MAAM,SAAS,MAAM;GAClD,IAAI,SAAS,KAAA,KAAa,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,YAAY,CAAC,WAAW,GAAG,GACzF,OAAO,KAAK;IAAE,GAAG;IAAK,OAAO;GAAK,CAAC;GACrC,OAAO,KAAK;IACV,GAAG;IACH,MAAM;KAAE,MAAM,KAAK;KAAI,OAAO,MAAM;KAAO,KAAK;IAAO;IACvD,OAAO;KAAE,GAAG;KAAO,UAAU;KAAM;KAAI;KAAI,MAAM,EAAE;IAAK;GAC1D,CAAC;EACH;EACA,KAAK,MAAM;GACT,MAAM,QAAQ,IAAI;GAClB,IAAI,UAAU,MAAM,OAAO,KAAK,GAAG;GACnC,MAAM,WAAW;IAAE,GAAG;IAAK,OAAO;GAAK;GACvC,IAAI,CAAC,MAAM,UAAU,OAAO,MAAM,UAAU,QAAQ,MAAM,MAAM,MAAM,KAAK;GAC3E,MAAM,OAAO,IAAI;GACjB,MAAM,KAAK,EAAE;GACb,IAAI,SAAS,QAAQ,OAAO,QAAQ,OAAO,KAAK,MAAM,OAAO,KAAK;IAAE,GAAG;IAAU,MAAM;GAAK,CAAC;GAC7F,OAAO;IACL,KAAK;KAAE,GAAG;KAAU,MAAM;KAAM,QAAQ;IAAK;IAC7C,MAAM,CAAC;KAAE,MAAM;KAAQ,MAAM,KAAK;KAAM;KAAI,OAAO,KAAK;KAAO,KAAK;IAAO,CAAC;IAC5E,OAAO;GACT;EACF;EACA,KAAK,UAAU;GACb,MAAM,WAAW,IAAI,OAAO,aAAa;GACzC,OAAO,KAAK;IAAE,GAAG;IAAK,OAAO;IAAM,MAAM,WAAW,OAAO,IAAI;GAAK,CAAC;EACvE;CACF;AACF;;AAGA,IAAa,WAAW,KAAU,QAAqB,YACrD,QAAQ,QAAQ,IAAI,UAAU,OAAO"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const faceStyles: import("@gyral/core").StyleSource;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { css } from "@gyral/core";
|
|
2
|
+
//#region src/face-styles.ts
|
|
3
|
+
var faceStyles = css`
|
|
4
|
+
@layer components {
|
|
5
|
+
.defs {
|
|
6
|
+
position: absolute;
|
|
7
|
+
inline-size: 0;
|
|
8
|
+
block-size: 0;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.cface,
|
|
13
|
+
.cback {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
aspect-ratio: 5 / 7;
|
|
16
|
+
border-radius: 9%/6.5%;
|
|
17
|
+
box-shadow: 0 1px 2px oklch(0% 0 0 / 0.28);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.cface {
|
|
21
|
+
background: var(--card-paper, oklch(98.5% 0.008 90));
|
|
22
|
+
border: 1px solid color-mix(in oklch, var(--card-ink, oklch(24% 0.02 260)) 22%, transparent);
|
|
23
|
+
color: var(--card-ink, oklch(24% 0.02 260));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.cback {
|
|
27
|
+
background-color: var(--back-a, oklch(42% 0.07 250));
|
|
28
|
+
background-image:
|
|
29
|
+
radial-gradient(circle at 50% 50%, var(--back-c, oklch(80% 0.11 85)) 0 9%, transparent 10%),
|
|
30
|
+
repeating-linear-gradient(45deg, var(--back-b, oklch(62% 0.08 250)) 0 1.5px, transparent 1.5px 8px),
|
|
31
|
+
repeating-linear-gradient(-45deg, var(--back-b, oklch(62% 0.08 250)) 0 1.5px, transparent 1.5px 8px);
|
|
32
|
+
border: 1px solid color-mix(in oklch, var(--back-a, oklch(42% 0.07 250)) 70%, black);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.face {
|
|
36
|
+
display: block;
|
|
37
|
+
inline-size: 100%;
|
|
38
|
+
block-size: 100%;
|
|
39
|
+
fill: var(--suit);
|
|
40
|
+
font-family: var(--card-font, var(--jy-font-sans, system-ui, sans-serif));
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.face .index {
|
|
45
|
+
font-size: 21px;
|
|
46
|
+
letter-spacing: -0.5px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.face .corner {
|
|
50
|
+
font-size: 14px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.face .letter {
|
|
54
|
+
font-size: 11px;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.face .monogram {
|
|
59
|
+
font-family: var(--card-serif, Georgia, 'Times New Roman', serif);
|
|
60
|
+
font-size: 46px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.face .frame {
|
|
64
|
+
fill: color-mix(in oklch, var(--suit) 9%, transparent);
|
|
65
|
+
stroke: var(--suit);
|
|
66
|
+
stroke-width: 2.5;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.face .crest {
|
|
70
|
+
fill: var(--suit);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.s-S,
|
|
74
|
+
.s-C {
|
|
75
|
+
--suit: var(--suit-black, oklch(24% 0.02 260));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.s-H,
|
|
79
|
+
.s-D {
|
|
80
|
+
--suit: var(--suit-red, oklch(52% 0.2 25));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.four .s-D {
|
|
84
|
+
--suit: var(--suit-blue, oklch(47% 0.17 255));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.four .s-C {
|
|
88
|
+
--suit: var(--suit-green, oklch(48% 0.13 150));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@media (forced-colors: active) {
|
|
92
|
+
.face {
|
|
93
|
+
fill: CanvasText;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.cface,
|
|
97
|
+
.cback {
|
|
98
|
+
border-color: CanvasText;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { faceStyles };
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=face-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-styles.js","names":[],"sources":["../src/face-styles.ts"],"sourcesContent":["// The card art's styles on their own, for components that draw single cards rather than a\n// whole table: the hand widget (<jy-card-hand>) and a game's trick or discard area. Put\n// `suitDefs()` once in the same shadow root, then a card is\n// `<div class=\"cface s-${suit}\">${cardFace(rank, suit)}</div>` and a back is `<div class=\"cback\"></div>`.\n//\n// Every color has a fallback instead of a default on `:host`, so a game themes these cards by\n// setting the custom properties on its own element; they inherit into the hand's shadow root:\n// --card-paper, --card-ink, --suit-black, --suit-red, --suit-blue, --suit-green,\n// --back-a, --back-b, --back-c, --card-font, --card-serif.\n// A `.four` ancestor turns on the four-color deck (blue diamonds, green clubs); the suit's\n// shape and letter always carry the suit, so color is only a second cue.\nimport { css } from '@gyral/core';\n\nexport const faceStyles = css`\n @layer components {\n .defs {\n position: absolute;\n inline-size: 0;\n block-size: 0;\n overflow: hidden;\n }\n\n .cface,\n .cback {\n box-sizing: border-box;\n aspect-ratio: 5 / 7;\n border-radius: 9%/6.5%;\n box-shadow: 0 1px 2px oklch(0% 0 0 / 0.28);\n }\n\n .cface {\n background: var(--card-paper, oklch(98.5% 0.008 90));\n border: 1px solid color-mix(in oklch, var(--card-ink, oklch(24% 0.02 260)) 22%, transparent);\n color: var(--card-ink, oklch(24% 0.02 260));\n }\n\n .cback {\n background-color: var(--back-a, oklch(42% 0.07 250));\n background-image:\n radial-gradient(circle at 50% 50%, var(--back-c, oklch(80% 0.11 85)) 0 9%, transparent 10%),\n repeating-linear-gradient(45deg, var(--back-b, oklch(62% 0.08 250)) 0 1.5px, transparent 1.5px 8px),\n repeating-linear-gradient(-45deg, var(--back-b, oklch(62% 0.08 250)) 0 1.5px, transparent 1.5px 8px);\n border: 1px solid color-mix(in oklch, var(--back-a, oklch(42% 0.07 250)) 70%, black);\n }\n\n .face {\n display: block;\n inline-size: 100%;\n block-size: 100%;\n fill: var(--suit);\n font-family: var(--card-font, var(--jy-font-sans, system-ui, sans-serif));\n font-weight: 700;\n }\n\n .face .index {\n font-size: 21px;\n letter-spacing: -0.5px;\n }\n\n .face .corner {\n font-size: 14px;\n }\n\n .face .letter {\n font-size: 11px;\n font-weight: 600;\n }\n\n .face .monogram {\n font-family: var(--card-serif, Georgia, 'Times New Roman', serif);\n font-size: 46px;\n }\n\n .face .frame {\n fill: color-mix(in oklch, var(--suit) 9%, transparent);\n stroke: var(--suit);\n stroke-width: 2.5;\n }\n\n .face .crest {\n fill: var(--suit);\n }\n\n .s-S,\n .s-C {\n --suit: var(--suit-black, oklch(24% 0.02 260));\n }\n\n .s-H,\n .s-D {\n --suit: var(--suit-red, oklch(52% 0.2 25));\n }\n\n .four .s-D {\n --suit: var(--suit-blue, oklch(47% 0.17 255));\n }\n\n .four .s-C {\n --suit: var(--suit-green, oklch(48% 0.13 150));\n }\n\n @media (forced-colors: active) {\n .face {\n fill: CanvasText;\n }\n\n .cface,\n .cback {\n border-color: CanvasText;\n }\n }\n }\n`;\n"],"mappings":";;AAaA,IAAa,aAAa,GAAG"}
|
package/dist/faces.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Rank, Suit } from '@joystyk/kit/cards';
|
|
2
|
+
/** Suit shapes in a 100 × 100 box, each its own silhouette. */
|
|
3
|
+
export declare const SUIT_PATHS: Readonly<Record<Suit, string>>;
|
|
4
|
+
/** The rank as the card prints it: "A", "7", "10", "Q". */
|
|
5
|
+
export declare const rankLabel: (rank: Rank) => string;
|
|
6
|
+
/** The suit symbols, once per table; faces refer to them with `<use>`. */
|
|
7
|
+
export declare const suitDefs: () => import("@gyral/core").TemplateResult;
|
|
8
|
+
/** Pip centers for 2–10, in the area below the top strip. Lower pips print upside down. */
|
|
9
|
+
export declare const PIPS: Readonly<Record<number, readonly (readonly [number, number])[]>>;
|
|
10
|
+
/** A face-up card's art. Decoration only: the pile's accessible name says what the card is. */
|
|
11
|
+
export declare const cardFace: (rank: Rank, suit: Suit) => import("@gyral/core").TemplateResult;
|
package/dist/faces.js
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import "@gyral/core";
|
|
2
|
+
import { compiled, compiledSvg } from "@gyral/core/compiled";
|
|
3
|
+
//#region src/faces.ts
|
|
4
|
+
var _gyral$t0 = {
|
|
5
|
+
"id": "22xok3v97av",
|
|
6
|
+
"html": "<svg class=\"defs\" width=\"0\" height=\"0\" aria-hidden=\"true\" focusable=\"false\"><defs><symbol id=\"suit-S\" viewBox=\"0 0 100 100\"><path/></symbol><symbol id=\"suit-H\" viewBox=\"0 0 100 100\"><path/></symbol><symbol id=\"suit-D\" viewBox=\"0 0 100 100\"><path/></symbol><symbol id=\"suit-C\" viewBox=\"0 0 100 100\"><path/></symbol></defs></svg>",
|
|
7
|
+
"parts": [
|
|
8
|
+
[
|
|
9
|
+
3,
|
|
10
|
+
[
|
|
11
|
+
0,
|
|
12
|
+
0,
|
|
13
|
+
0,
|
|
14
|
+
0
|
|
15
|
+
],
|
|
16
|
+
"d"
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
3,
|
|
20
|
+
[
|
|
21
|
+
0,
|
|
22
|
+
0,
|
|
23
|
+
1,
|
|
24
|
+
0
|
|
25
|
+
],
|
|
26
|
+
"d"
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
3,
|
|
30
|
+
[
|
|
31
|
+
0,
|
|
32
|
+
0,
|
|
33
|
+
2,
|
|
34
|
+
0
|
|
35
|
+
],
|
|
36
|
+
"d"
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
3,
|
|
40
|
+
[
|
|
41
|
+
0,
|
|
42
|
+
0,
|
|
43
|
+
3,
|
|
44
|
+
0
|
|
45
|
+
],
|
|
46
|
+
"d"
|
|
47
|
+
]
|
|
48
|
+
],
|
|
49
|
+
"segments": [
|
|
50
|
+
"<svg class=\"defs\" width=\"0\" height=\"0\" aria-hidden=\"true\" focusable=\"false\"><defs><symbol id=\"suit-S\" viewBox=\"0 0 100 100\"><path",
|
|
51
|
+
{
|
|
52
|
+
"k": "attr",
|
|
53
|
+
"name": "d"
|
|
54
|
+
},
|
|
55
|
+
"/></symbol><symbol id=\"suit-H\" viewBox=\"0 0 100 100\"><path",
|
|
56
|
+
{
|
|
57
|
+
"k": "attr",
|
|
58
|
+
"name": "d"
|
|
59
|
+
},
|
|
60
|
+
"/></symbol><symbol id=\"suit-D\" viewBox=\"0 0 100 100\"><path",
|
|
61
|
+
{
|
|
62
|
+
"k": "attr",
|
|
63
|
+
"name": "d"
|
|
64
|
+
},
|
|
65
|
+
"/></symbol><symbol id=\"suit-C\" viewBox=\"0 0 100 100\"><path",
|
|
66
|
+
{
|
|
67
|
+
"k": "attr",
|
|
68
|
+
"name": "d"
|
|
69
|
+
},
|
|
70
|
+
"/></symbol></defs></svg>"
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
var _gyral$t1 = {
|
|
74
|
+
"id": "j1b5og2pav",
|
|
75
|
+
"html": "<use/>",
|
|
76
|
+
"parts": [
|
|
77
|
+
[
|
|
78
|
+
3,
|
|
79
|
+
[0],
|
|
80
|
+
"class"
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
3,
|
|
84
|
+
[0],
|
|
85
|
+
"href"
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
3,
|
|
89
|
+
[0],
|
|
90
|
+
"x"
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
3,
|
|
94
|
+
[0],
|
|
95
|
+
"y"
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
3,
|
|
99
|
+
[0],
|
|
100
|
+
"width"
|
|
101
|
+
],
|
|
102
|
+
[
|
|
103
|
+
3,
|
|
104
|
+
[0],
|
|
105
|
+
"height"
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
3,
|
|
109
|
+
[0],
|
|
110
|
+
"transform"
|
|
111
|
+
]
|
|
112
|
+
],
|
|
113
|
+
"svg": true,
|
|
114
|
+
"segments": [
|
|
115
|
+
"<use",
|
|
116
|
+
{
|
|
117
|
+
"k": "attr",
|
|
118
|
+
"name": "class"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"k": "attr",
|
|
122
|
+
"name": "href"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"k": "attr",
|
|
126
|
+
"name": "x"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"k": "attr",
|
|
130
|
+
"name": "y"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"k": "attr",
|
|
134
|
+
"name": "width"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"k": "attr",
|
|
138
|
+
"name": "height"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"k": "attr",
|
|
142
|
+
"name": "transform"
|
|
143
|
+
},
|
|
144
|
+
"/>"
|
|
145
|
+
]
|
|
146
|
+
};
|
|
147
|
+
var _gyral$t2 = {
|
|
148
|
+
"id": "1fjawzfzu4a",
|
|
149
|
+
"html": "<path class=\"crest\" d=\"M30 62L34 44L42 54L50 38L58 54L66 44L70 62Z\"/>",
|
|
150
|
+
"parts": [],
|
|
151
|
+
"svg": true,
|
|
152
|
+
"segments": ["<path class=\"crest\" d=\"M30 62L34 44L42 54L50 38L58 54L66 44L70 62Z\"/>"]
|
|
153
|
+
};
|
|
154
|
+
var _gyral$t3 = {
|
|
155
|
+
"id": "1k3rr19hgb6",
|
|
156
|
+
"html": "<g class=\"crest\"><path d=\"M30 60Q50 40 70 60Z\"/><circle cx=\"30\" cy=\"54\" r=\"4\"/><circle cx=\"50\" cy=\"42\" r=\"4.5\"/><circle cx=\"70\" cy=\"54\" r=\"4\"/></g>",
|
|
157
|
+
"parts": [],
|
|
158
|
+
"svg": true,
|
|
159
|
+
"segments": ["<g class=\"crest\"><path d=\"M30 60Q50 40 70 60Z\"/><circle cx=\"30\" cy=\"54\" r=\"4\"/><circle cx=\"50\" cy=\"42\" r=\"4.5\"/><circle cx=\"70\" cy=\"54\" r=\"4\"/></g>"]
|
|
160
|
+
};
|
|
161
|
+
var _gyral$t4 = {
|
|
162
|
+
"id": "7d8qbh9elk",
|
|
163
|
+
"html": "<path class=\"crest\" d=\"M30 48L50 60L70 48L70 56L50 68L30 56Z\"/>",
|
|
164
|
+
"parts": [],
|
|
165
|
+
"svg": true,
|
|
166
|
+
"segments": ["<path class=\"crest\" d=\"M30 48L50 60L70 48L70 56L50 68L30 56Z\"/>"]
|
|
167
|
+
};
|
|
168
|
+
var _gyral$t5 = {
|
|
169
|
+
"id": "1sphrc3jsqw",
|
|
170
|
+
"html": "<rect class=\"frame\" x=\"17\" y=\"32\" width=\"66\" height=\"96\" rx=\"9\"/><!----> <text class=\"monogram\" x=\"50\" y=\"108\" text-anchor=\"middle\"></text> <!---->",
|
|
171
|
+
"parts": [
|
|
172
|
+
[
|
|
173
|
+
2,
|
|
174
|
+
[],
|
|
175
|
+
1
|
|
176
|
+
],
|
|
177
|
+
[1, [3]],
|
|
178
|
+
[
|
|
179
|
+
2,
|
|
180
|
+
[],
|
|
181
|
+
5
|
|
182
|
+
],
|
|
183
|
+
[0, []]
|
|
184
|
+
],
|
|
185
|
+
"svg": true,
|
|
186
|
+
"segments": [
|
|
187
|
+
"<rect class=\"frame\" x=\"17\" y=\"32\" width=\"66\" height=\"96\" rx=\"9\"/>",
|
|
188
|
+
{ "k": "child" },
|
|
189
|
+
"<!----> <text class=\"monogram\" x=\"50\" y=\"108\" text-anchor=\"middle\">",
|
|
190
|
+
{
|
|
191
|
+
"k": "child",
|
|
192
|
+
"in": "text",
|
|
193
|
+
"svg": true
|
|
194
|
+
},
|
|
195
|
+
"</text> ",
|
|
196
|
+
{ "k": "child" },
|
|
197
|
+
"<!---->",
|
|
198
|
+
{ "k": "child" }
|
|
199
|
+
]
|
|
200
|
+
};
|
|
201
|
+
var _gyral$t6 = {
|
|
202
|
+
"id": "1b8fkoq43rf",
|
|
203
|
+
"html": "<svg class=\"face\" viewBox=\"0 0 100 140\" aria-hidden=\"true\" focusable=\"false\"><text class=\"index\" x=\"7\" y=\"22\"></text> <g class=\"middle\"></g><g transform=\"rotate(180 50 70)\"><text class=\"corner\" x=\"7\" y=\"16\"></text> <text class=\"letter\" x=\"7\" y=\"29\"></text></g></svg>",
|
|
204
|
+
"parts": [
|
|
205
|
+
[1, [0, 0]],
|
|
206
|
+
[
|
|
207
|
+
2,
|
|
208
|
+
[0],
|
|
209
|
+
2
|
|
210
|
+
],
|
|
211
|
+
[1, [0, 2]],
|
|
212
|
+
[1, [
|
|
213
|
+
0,
|
|
214
|
+
3,
|
|
215
|
+
0
|
|
216
|
+
]],
|
|
217
|
+
[1, [
|
|
218
|
+
0,
|
|
219
|
+
3,
|
|
220
|
+
2
|
|
221
|
+
]]
|
|
222
|
+
],
|
|
223
|
+
"segments": [
|
|
224
|
+
"<svg class=\"face\" viewBox=\"0 0 100 140\" aria-hidden=\"true\" focusable=\"false\"><text class=\"index\" x=\"7\" y=\"22\">",
|
|
225
|
+
{
|
|
226
|
+
"k": "child",
|
|
227
|
+
"in": "text",
|
|
228
|
+
"svg": true
|
|
229
|
+
},
|
|
230
|
+
"</text> ",
|
|
231
|
+
{
|
|
232
|
+
"k": "child",
|
|
233
|
+
"in": "svg",
|
|
234
|
+
"svg": true
|
|
235
|
+
},
|
|
236
|
+
"<g class=\"middle\">",
|
|
237
|
+
{
|
|
238
|
+
"k": "child",
|
|
239
|
+
"in": "g",
|
|
240
|
+
"svg": true
|
|
241
|
+
},
|
|
242
|
+
"</g><g transform=\"rotate(180 50 70)\"><text class=\"corner\" x=\"7\" y=\"16\">",
|
|
243
|
+
{
|
|
244
|
+
"k": "child",
|
|
245
|
+
"in": "text",
|
|
246
|
+
"svg": true
|
|
247
|
+
},
|
|
248
|
+
"</text> <text class=\"letter\" x=\"7\" y=\"29\">",
|
|
249
|
+
{
|
|
250
|
+
"k": "child",
|
|
251
|
+
"in": "text",
|
|
252
|
+
"svg": true
|
|
253
|
+
},
|
|
254
|
+
"</text></g></svg>"
|
|
255
|
+
]
|
|
256
|
+
};
|
|
257
|
+
/** Suit shapes in a 100 × 100 box, each its own silhouette. */
|
|
258
|
+
var SUIT_PATHS = {
|
|
259
|
+
S: "M50 4C64 26 94 40 94 62C94 76 84 85 72 85C63 85 56 80 53 73C54 83 59 91 67 96H33C41 91 46 83 47 73C44 80 37 85 28 85C16 85 6 76 6 62C6 40 36 26 50 4Z",
|
|
260
|
+
H: "M50 92C22 70 4 52 4 30C4 14 16 4 29 4C39 4 46 10 50 19C54 10 61 4 71 4C84 4 96 14 96 30C96 52 78 70 50 92Z",
|
|
261
|
+
D: "M50 2Q64 28 88 50Q64 72 50 98Q36 72 12 50Q36 28 50 2Z",
|
|
262
|
+
C: "M50 8A19 19 0 1 1 49.9 8ZM27 38A19 19 0 1 1 26.9 38ZM73 38A19 19 0 1 1 72.9 38ZM46 58Q46 84 33 96H67Q54 84 54 58Z"
|
|
263
|
+
};
|
|
264
|
+
var RANK_LABELS = {
|
|
265
|
+
1: "A",
|
|
266
|
+
2: "2",
|
|
267
|
+
3: "3",
|
|
268
|
+
4: "4",
|
|
269
|
+
5: "5",
|
|
270
|
+
6: "6",
|
|
271
|
+
7: "7",
|
|
272
|
+
8: "8",
|
|
273
|
+
9: "9",
|
|
274
|
+
10: "10",
|
|
275
|
+
11: "J",
|
|
276
|
+
12: "Q",
|
|
277
|
+
13: "K"
|
|
278
|
+
};
|
|
279
|
+
/** The rank as the card prints it: "A", "7", "10", "Q". */
|
|
280
|
+
var rankLabel = (rank) => RANK_LABELS[rank];
|
|
281
|
+
/** The suit symbols, once per table; faces refer to them with `<use>`. */
|
|
282
|
+
var suitDefs = () => compiled(_gyral$t0, [
|
|
283
|
+
SUIT_PATHS.S,
|
|
284
|
+
SUIT_PATHS.H,
|
|
285
|
+
SUIT_PATHS.D,
|
|
286
|
+
SUIT_PATHS.C
|
|
287
|
+
]);
|
|
288
|
+
/** One suit shape, `size` wide, centered on (x, y); `flip` turns it upside down. */
|
|
289
|
+
var pip = (suit, x, y, size, flip = false, cls = null) => compiledSvg(_gyral$t1, [
|
|
290
|
+
cls,
|
|
291
|
+
`#suit-${suit}`,
|
|
292
|
+
x - size / 2,
|
|
293
|
+
y - size / 2,
|
|
294
|
+
size,
|
|
295
|
+
size,
|
|
296
|
+
flip ? `rotate(180 ${x} ${y})` : null
|
|
297
|
+
]);
|
|
298
|
+
var L = 31;
|
|
299
|
+
var C = 50;
|
|
300
|
+
var R = 69;
|
|
301
|
+
/** Pip centers for 2–10, in the area below the top strip. Lower pips print upside down. */
|
|
302
|
+
var PIPS = {
|
|
303
|
+
2: [[C, 44], [C, 118]],
|
|
304
|
+
3: [
|
|
305
|
+
[C, 44],
|
|
306
|
+
[C, 81],
|
|
307
|
+
[C, 118]
|
|
308
|
+
],
|
|
309
|
+
4: [
|
|
310
|
+
[L, 44],
|
|
311
|
+
[R, 44],
|
|
312
|
+
[L, 118],
|
|
313
|
+
[R, 118]
|
|
314
|
+
],
|
|
315
|
+
5: [
|
|
316
|
+
[L, 44],
|
|
317
|
+
[R, 44],
|
|
318
|
+
[C, 81],
|
|
319
|
+
[L, 118],
|
|
320
|
+
[R, 118]
|
|
321
|
+
],
|
|
322
|
+
6: [
|
|
323
|
+
[L, 44],
|
|
324
|
+
[R, 44],
|
|
325
|
+
[L, 81],
|
|
326
|
+
[R, 81],
|
|
327
|
+
[L, 118],
|
|
328
|
+
[R, 118]
|
|
329
|
+
],
|
|
330
|
+
7: [
|
|
331
|
+
[L, 44],
|
|
332
|
+
[R, 44],
|
|
333
|
+
[C, 62],
|
|
334
|
+
[L, 81],
|
|
335
|
+
[R, 81],
|
|
336
|
+
[L, 118],
|
|
337
|
+
[R, 118]
|
|
338
|
+
],
|
|
339
|
+
8: [
|
|
340
|
+
[L, 44],
|
|
341
|
+
[R, 44],
|
|
342
|
+
[C, 62],
|
|
343
|
+
[L, 81],
|
|
344
|
+
[R, 81],
|
|
345
|
+
[C, 100],
|
|
346
|
+
[L, 118],
|
|
347
|
+
[R, 118]
|
|
348
|
+
],
|
|
349
|
+
9: [
|
|
350
|
+
[L, 44],
|
|
351
|
+
[R, 44],
|
|
352
|
+
[L, 69],
|
|
353
|
+
[R, 69],
|
|
354
|
+
[C, 81],
|
|
355
|
+
[L, 93],
|
|
356
|
+
[R, 93],
|
|
357
|
+
[L, 118],
|
|
358
|
+
[R, 118]
|
|
359
|
+
],
|
|
360
|
+
10: [
|
|
361
|
+
[L, 44],
|
|
362
|
+
[R, 44],
|
|
363
|
+
[C, 56],
|
|
364
|
+
[L, 69],
|
|
365
|
+
[R, 69],
|
|
366
|
+
[L, 93],
|
|
367
|
+
[R, 93],
|
|
368
|
+
[C, 106],
|
|
369
|
+
[L, 118],
|
|
370
|
+
[R, 118]
|
|
371
|
+
]
|
|
372
|
+
};
|
|
373
|
+
/** A geometric crest above a court card's letter: a crown's points, a band of beads, a chevron. */
|
|
374
|
+
var crest = (rank) => {
|
|
375
|
+
switch (rank) {
|
|
376
|
+
case 13: return compiledSvg(_gyral$t2, []);
|
|
377
|
+
case 12: return compiledSvg(_gyral$t3, []);
|
|
378
|
+
case 11: return compiledSvg(_gyral$t4, []);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
/** The middle of a face: pips, the ace's single large shape, or a court card's monogram. */
|
|
382
|
+
var middle = (rank, suit) => {
|
|
383
|
+
if (rank === 1) return pip(suit, 50, 82, 46);
|
|
384
|
+
if (rank >= 11) return compiledSvg(_gyral$t5, [
|
|
385
|
+
crest(rank),
|
|
386
|
+
RANK_LABELS[rank],
|
|
387
|
+
pip(suit, 27, 120, 12),
|
|
388
|
+
pip(suit, 73, 120, 12)
|
|
389
|
+
]);
|
|
390
|
+
return (PIPS[rank] ?? []).map(([x, y]) => pip(suit, x, y, 17, y > 82));
|
|
391
|
+
};
|
|
392
|
+
/** A face-up card's art. Decoration only: the pile's accessible name says what the card is. */
|
|
393
|
+
var cardFace = (rank, suit) => compiled(_gyral$t6, [
|
|
394
|
+
RANK_LABELS[rank],
|
|
395
|
+
pip(suit, 82, 13, 22, false, "suit-index"),
|
|
396
|
+
middle(rank, suit),
|
|
397
|
+
RANK_LABELS[rank],
|
|
398
|
+
suit
|
|
399
|
+
]);
|
|
400
|
+
//#endregion
|
|
401
|
+
export { PIPS, SUIT_PATHS, cardFace, rankLabel, suitDefs };
|
|
402
|
+
|
|
403
|
+
//# sourceMappingURL=faces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faces.js","names":["_gyral$compiled","_gyral$compiledSvg1","court"],"sources":["../src/faces.ts"],"sourcesContent":["// The card art, drawn for Joystyk (klondike.md, \"Theme\"): four suit shapes with distinct\n// silhouettes, number cards with pip layouts, and monogram court cards (a framed letter\n// under a simple geometric crest), never traditional court figures. Every face shows its\n// rank and suit shape in a top strip, the part a fanned column leaves visible, and the rank\n// with the suit's letter in the opposite corner, so a suit never depends on color or on one\n// glyph alone. Faces are SVG in a 100 × 140 box; the card's shape and back are CSS. The\n// strip's rank (`.index`) and suit (`.suit-index`) and the middle (`.middle`) carry classes,\n// so the card table can enlarge the strip on narrow cards (styles.ts).\nimport { html, svg } from '@gyral/core';\nimport type { Rank, Suit } from '@joystyk/kit/cards';\n\n/** Suit shapes in a 100 × 100 box, each its own silhouette. */\nexport const SUIT_PATHS: Readonly<Record<Suit, string>> = {\n // A spade: a pointed leaf over a flared stem.\n S: 'M50 4C64 26 94 40 94 62C94 76 84 85 72 85C63 85 56 80 53 73C54 83 59 91 67 96H33C41 91 46 83 47 73C44 80 37 85 28 85C16 85 6 76 6 62C6 40 36 26 50 4Z',\n // A heart: two round lobes meeting in a point.\n H: 'M50 92C22 70 4 52 4 30C4 14 16 4 29 4C39 4 46 10 50 19C54 10 61 4 71 4C84 4 96 14 96 30C96 52 78 70 50 92Z',\n // A diamond with gently curved sides.\n D: 'M50 2Q64 28 88 50Q64 72 50 98Q36 72 12 50Q36 28 50 2Z',\n // A club: three round leaves over a flared stem.\n C: 'M50 8A19 19 0 1 1 49.9 8ZM27 38A19 19 0 1 1 26.9 38ZM73 38A19 19 0 1 1 72.9 38ZM46 58Q46 84 33 96H67Q54 84 54 58Z',\n};\n\nconst RANK_LABELS: Readonly<Record<Rank, string>> = {\n 1: 'A',\n 2: '2',\n 3: '3',\n 4: '4',\n 5: '5',\n 6: '6',\n 7: '7',\n 8: '8',\n 9: '9',\n 10: '10',\n 11: 'J',\n 12: 'Q',\n 13: 'K',\n};\n\n/** The rank as the card prints it: \"A\", \"7\", \"10\", \"Q\". */\nexport const rankLabel = (rank: Rank): string => RANK_LABELS[rank];\n\n/** The suit symbols, once per table; faces refer to them with `<use>`. */\nexport const suitDefs = () =>\n html`<svg class=\"defs\" width=\"0\" height=\"0\" aria-hidden=\"true\" focusable=\"false\">\n <defs>\n <symbol id=\"suit-S\" viewBox=\"0 0 100 100\"><path d=${SUIT_PATHS.S} /></symbol>\n <symbol id=\"suit-H\" viewBox=\"0 0 100 100\"><path d=${SUIT_PATHS.H} /></symbol>\n <symbol id=\"suit-D\" viewBox=\"0 0 100 100\"><path d=${SUIT_PATHS.D} /></symbol>\n <symbol id=\"suit-C\" viewBox=\"0 0 100 100\"><path d=${SUIT_PATHS.C} /></symbol>\n </defs>\n </svg>`;\n\n/** One suit shape, `size` wide, centered on (x, y); `flip` turns it upside down. */\nconst pip = (suit: Suit, x: number, y: number, size: number, flip = false, cls: string | null = null) =>\n svg`<use class=${cls} href=${`#suit-${suit}`} x=${x - size / 2} y=${y - size / 2} width=${size} height=${size} transform=${flip ? `rotate(180 ${x} ${y})` : null} />`;\n\nconst L = 31;\nconst C = 50;\nconst R = 69;\n\n/** Pip centers for 2–10, in the area below the top strip. Lower pips print upside down. */\nexport const PIPS: Readonly<Record<number, readonly (readonly [number, number])[]>> = {\n 2: [\n [C, 44],\n [C, 118],\n ],\n 3: [\n [C, 44],\n [C, 81],\n [C, 118],\n ],\n 4: [\n [L, 44],\n [R, 44],\n [L, 118],\n [R, 118],\n ],\n 5: [\n [L, 44],\n [R, 44],\n [C, 81],\n [L, 118],\n [R, 118],\n ],\n 6: [\n [L, 44],\n [R, 44],\n [L, 81],\n [R, 81],\n [L, 118],\n [R, 118],\n ],\n 7: [\n [L, 44],\n [R, 44],\n [C, 62],\n [L, 81],\n [R, 81],\n [L, 118],\n [R, 118],\n ],\n 8: [\n [L, 44],\n [R, 44],\n [C, 62],\n [L, 81],\n [R, 81],\n [C, 100],\n [L, 118],\n [R, 118],\n ],\n 9: [\n [L, 44],\n [R, 44],\n [L, 69],\n [R, 69],\n [C, 81],\n [L, 93],\n [R, 93],\n [L, 118],\n [R, 118],\n ],\n 10: [\n [L, 44],\n [R, 44],\n [C, 56],\n [L, 69],\n [R, 69],\n [L, 93],\n [R, 93],\n [C, 106],\n [L, 118],\n [R, 118],\n ],\n};\n\n/** A geometric crest above a court card's letter: a crown's points, a band of beads, a chevron. */\nconst crest = (rank: 11 | 12 | 13) => {\n switch (rank) {\n case 13:\n return svg`<path class=\"crest\" d=\"M30 62L34 44L42 54L50 38L58 54L66 44L70 62Z\" />`;\n case 12:\n return svg`<g class=\"crest\">\n <path d=\"M30 60Q50 40 70 60Z\" />\n <circle cx=\"30\" cy=\"54\" r=\"4\" /><circle cx=\"50\" cy=\"42\" r=\"4.5\" /><circle cx=\"70\" cy=\"54\" r=\"4\" />\n </g>`;\n case 11:\n return svg`<path class=\"crest\" d=\"M30 48L50 60L70 48L70 56L50 68L30 56Z\" />`;\n }\n};\n\n/** The middle of a face: pips, the ace's single large shape, or a court card's monogram. */\nconst middle = (rank: Rank, suit: Suit) => {\n if (rank === 1) return pip(suit, 50, 82, 46);\n if (rank >= 11) {\n const court = rank as 11 | 12 | 13;\n return svg`<rect class=\"frame\" x=\"17\" y=\"32\" width=\"66\" height=\"96\" rx=\"9\" />\n ${crest(court)}\n <text class=\"monogram\" x=\"50\" y=\"108\" text-anchor=\"middle\">${RANK_LABELS[rank]}</text>\n ${pip(suit, 27, 120, 12)}${pip(suit, 73, 120, 12)}`;\n }\n return (PIPS[rank] ?? []).map(([x, y]) => pip(suit, x, y, 17, y > 82));\n};\n\n/** A face-up card's art. Decoration only: the pile's accessible name says what the card is. */\nexport const cardFace = (rank: Rank, suit: Suit) =>\n html`<svg class=\"face\" viewBox=\"0 0 100 140\" aria-hidden=\"true\" focusable=\"false\">\n <text class=\"index\" x=\"7\" y=\"22\">${RANK_LABELS[rank]}</text>\n ${pip(suit, 82, 13, 22, false, 'suit-index')}\n <g class=\"middle\">${middle(rank, suit)}</g>\n <g transform=\"rotate(180 50 70)\">\n <text class=\"corner\" x=\"7\" y=\"16\">${RANK_LABELS[rank]}</text>\n <text class=\"letter\" x=\"7\" y=\"29\">${suit}</text>\n </g>\n </svg>`;\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const handStyles: import("@gyral/core").StyleSource;
|