@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":"solitaire.js","names":["_gyral$compiled"],"sources":["../src/solitaire.ts"],"sourcesContent":["// The common solitaire frame inside a game's canvas. Games supply pure adapters; this\n// component owns only card-table controls, preferences, records and their effect drivers.\nimport {\n child,\n command,\n css,\n define,\n defineDriver,\n focus,\n html,\n intentsOf,\n nothing,\n subscription,\n type ChildValue,\n type Command,\n} from '@gyral/core';\nimport {\n isJson,\n type AnyGameSession,\n type Json,\n type Mounted,\n type PlatformApi,\n type Preferences,\n type Validator,\n} from '@joystyk/contracts';\nimport { CardTable } from './table.ts';\nimport { DEFAULT_OPTIONS, type Back, type TableLayout, type TableOptions } from './layout.ts';\nimport type { TableOut } from './controller.ts';\n\nexport type SetupField = {\n readonly name: string;\n readonly label: string;\n readonly value: string;\n readonly choices?: readonly { readonly value: string; readonly label: string }[];\n};\nexport type SolitaireData = {\n readonly title: string;\n readonly theme: 'noon' | 'dawn' | 'dusk';\n readonly back: Back;\n readonly layout: TableLayout;\n readonly status: string;\n readonly line: string;\n readonly meters: readonly { readonly label: string; readonly value: string }[];\n readonly fields: readonly SetupField[];\n readonly setupAllowed: boolean;\n readonly actions: readonly { readonly label: string; readonly command: Json; readonly disabled: boolean }[];\n readonly scorecard?: readonly {\n readonly hole: number;\n readonly strokes: number | null;\n readonly total: number | null;\n readonly par: number;\n readonly cleared: boolean;\n }[];\n readonly recordGroup: string;\n readonly moves: number;\n readonly score: number;\n readonly bestRun: number;\n readonly cleared: number;\n readonly keys: string;\n readonly history?: readonly string[];\n readonly round?: { readonly strokes: number; readonly bestHole: number; readonly par: number };\n};\nexport type SolitaireAdapter<V, E> = {\n readonly view: Validator<V>;\n readonly event: Validator<E>;\n readonly describe: (view: V, events: readonly E[], revision: number) => SolitaireData;\n readonly table: (view: V, out: TableOut) => Json | null;\n readonly shortcut: (view: V, key: string) => Json | null;\n readonly setup: (view: V, values: Readonly<Record<string, string>>) => Json | null;\n /** Optional worker validation/certification before a setup command is sent. */\n readonly prepareSetup?: (command: Json) => Promise<Json>;\n readonly dispose?: () => void;\n};\ntype Snapshot = {\n readonly data: SolitaireData;\n readonly revision: number;\n readonly canUndo: boolean;\n readonly result: string | null;\n readonly summary: string;\n readonly notice: string;\n readonly elapsedMs: number;\n readonly undosUsed: number;\n readonly assisted: boolean;\n readonly paused: boolean;\n};\nexport type RecordRow = {\n readonly played: number;\n readonly won: number;\n readonly bestScore: number | null;\n readonly fewestMoves: number | null;\n readonly bestMs: number | null;\n readonly bestRun: number;\n readonly cleared: number;\n readonly cleanWins: number;\n readonly streak: number;\n readonly longestStreak: number;\n readonly completed: number;\n readonly totalStrokes: number;\n readonly bestRound: number | null;\n readonly bestHole: number | null;\n readonly underPar: number;\n};\ntype Records = Readonly<Record<string, RecordRow>>;\nconst EMPTY_ROW: RecordRow = {\n played: 0,\n won: 0,\n bestScore: null,\n fewestMoves: null,\n bestMs: null,\n bestRun: 0,\n cleared: 0,\n cleanWins: 0,\n streak: 0,\n longestStreak: 0,\n completed: 0,\n totalStrokes: 0,\n bestRound: null,\n bestHole: null,\n underPar: 0,\n};\nconst record = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);\nconst count = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v >= 0;\nconst recordSchema: Validator<Records> = {\n '~standard': {\n version: 1,\n vendor: 'joystyk',\n validate: (v) => {\n if (!record(v)) return { issues: [{ message: 'Expected solitaire records.' }] };\n const rows: Record<string, RecordRow> = {};\n for (const [key, raw] of Object.entries(v)) {\n if (\n !record(raw) ||\n ![\n 'played',\n 'won',\n 'bestRun',\n 'cleared',\n 'cleanWins',\n 'streak',\n 'longestStreak',\n 'completed',\n 'underPar',\n ].every((field) => count(raw[field])) ||\n typeof raw['totalStrokes'] !== 'number' ||\n !Number.isFinite(raw['totalStrokes']) ||\n !['bestScore', 'fewestMoves', 'bestMs', 'bestRound', 'bestHole'].every(\n (field) => raw[field] === null || (typeof raw[field] === 'number' && Number.isFinite(raw[field])),\n )\n )\n return { issues: [{ message: 'Invalid solitaire record.' }] };\n rows[key] = raw as RecordRow;\n }\n return { value: rows };\n },\n },\n};\nexport const decode = <T>(schema: Validator<T>, raw: unknown): T | null => {\n const result = schema['~standard'].validate(raw);\n return result instanceof Promise || result.issues !== undefined ? null : result.value;\n};\ntype Meta = {\n readonly revision: number;\n readonly result: string | null;\n readonly summary: string;\n readonly notice: string;\n readonly elapsedMs: number;\n readonly undosUsed: number;\n readonly assisted: boolean;\n readonly canUndo: boolean;\n readonly paused: boolean;\n};\nconst metaSchema: Validator<Meta> = {\n '~standard': {\n version: 1,\n vendor: 'joystyk',\n validate: (raw) => {\n if (!record(raw) || !record(raw['meta']) || !count(raw['meta']['revision']))\n return { issues: [{ message: 'Invalid solitaire snapshot.' }] };\n const m = raw['meta'];\n const outcome = m['outcome'];\n const notice = m['notice'];\n if (\n outcome !== null &&\n (!record(outcome) ||\n typeof outcome['result'] !== 'string' ||\n !['won', 'lost', 'draw', 'completed', 'abandoned'].includes(outcome['result']) ||\n typeof outcome['summary'] !== 'string')\n )\n return { issues: [{ message: 'Invalid result.' }] };\n return {\n value: {\n revision: m['revision'] as number,\n result: record(outcome) ? String(outcome['result']) : null,\n summary: record(outcome) ? String(outcome['summary']) : '',\n notice: record(notice) && typeof notice['message'] === 'string' ? notice['message'] : '',\n elapsedMs: count(raw['elapsedMs'])\n ? raw['elapsedMs']\n : record(outcome) && count(outcome['elapsedMs'])\n ? outcome['elapsedMs']\n : 0,\n undosUsed: count(m['undosUsed'])\n ? m['undosUsed']\n : record(outcome) && count(outcome['undosUsed'])\n ? outcome['undosUsed']\n : 0,\n assisted: record(outcome) && Array.isArray(outcome['assists']) && outcome['assists'].length > 0,\n canUndo: record(raw['undo']) && raw['undo']['available'] === true,\n paused: raw['paused'] === true,\n },\n };\n },\n },\n};\ntype Action =\n | { readonly kind: 'command'; readonly command: Json }\n | { readonly kind: 'undo' | 'resign' }\n | { readonly kind: 'table'; readonly out: TableOut }\n | { readonly kind: 'key'; readonly key: string }\n | { readonly kind: 'setup'; readonly values: Readonly<Record<string, string>> };\ntype State = {\n readonly snapshot: Snapshot | null;\n readonly options: TableOptions;\n readonly dark: boolean;\n readonly message: string;\n readonly dialog: 'none' | 'keys' | 'confirm';\n readonly elapsedMs: number;\n readonly records: Records;\n readonly sound: boolean;\n};\ntype Msg =\n | { readonly _tag: 'Snapshot'; readonly snapshot: Snapshot }\n | { readonly _tag: 'Preferences'; readonly p: Preferences }\n | { readonly _tag: 'Records'; readonly records: Records }\n | { readonly _tag: 'Tick'; readonly ms: number }\n | { readonly _tag: 'Action'; readonly action: Action }\n | { readonly _tag: 'Table'; readonly out: TableOut }\n | { readonly _tag: 'Key'; readonly key: string; readonly ctrl: boolean }\n | { readonly _tag: 'Dialog'; readonly dialog: State['dialog'] }\n | { readonly _tag: 'Color'; readonly on: boolean }\n | { readonly _tag: 'Sound'; readonly on: boolean }\n | { readonly _tag: 'Message'; readonly message: string };\nconst feed = defineDriver<void, Snapshot, never>('solitaire-session');\nconst prefs = defineDriver<void, Preferences, never>('solitaire-preferences');\nconst timer = defineDriver<void, number, never>('solitaire-timer');\nconst sending = defineDriver<Action, string>('solitaire-send');\nconst speaking = defineDriver<string, void>('solitaire-speak');\nconst reading = defineDriver<string, Json | null>('solitaire-read');\nconst writing = defineDriver<Records, void>('solitaire-write');\nconst color = defineDriver<boolean, void>('solitaire-color');\nconst sounding = defineDriver<'card' | 'clear', void>('solitaire-sound');\nconst jsonSchema: Validator<Json> = {\n '~standard': {\n version: 1,\n vendor: 'joystyk',\n validate: (raw) => (isJson(raw) ? { value: raw } : { issues: [{ message: 'Expected JSON.' }] }),\n },\n};\nconst booleanSchema: Validator<boolean> = {\n '~standard': {\n version: 1,\n vendor: 'joystyk',\n validate: (raw) => (typeof raw === 'boolean' ? { value: raw } : { issues: [{ message: 'Expected a boolean.' }] }),\n },\n};\nconst say = (line: string) => command(speaking, line);\nconst send = (action: Action): Command<Msg> =>\n command(sending, action, {\n concurrency: 'queue',\n onSuccess: (message) => (message === '' ? undefined : { _tag: 'Message', message }),\n onFailure: () => ({ _tag: 'Message', message: 'The deal could not be prepared. Try again.' }),\n });\nconst i = intentsOf<typeof SolitaireBoard>();\nconst timeText = (ms: number) => `${Math.floor(ms / 60_000)}:${String(Math.floor(ms / 1000) % 60).padStart(2, '0')}`;\nexport const SolitaireBoard = define<State, Msg>()('jy-solitaire-board', {\n init: () => [\n {\n snapshot: null,\n options: DEFAULT_OPTIONS,\n dark: true,\n message: '',\n dialog: 'none',\n elapsedMs: 0,\n records: {},\n sound: false,\n },\n [\n command(reading, 'records', { onSuccess: (v) => ({ _tag: 'Records', records: decode(recordSchema, v) ?? {} }) }),\n command(reading, 'fourColor', {\n key: 'four-color',\n onSuccess: (v) => ({ _tag: 'Color', on: decode(booleanSchema, v) ?? false }),\n }),\n ],\n ],\n subscriptions: () => [\n command(feed, undefined, { onSuccess: (snapshot) => ({ _tag: 'Snapshot', snapshot }) }),\n command(prefs, undefined, { onSuccess: (p) => ({ _tag: 'Preferences', p }) }),\n command(timer, undefined, { onSuccess: (ms) => ({ _tag: 'Tick', ms }) }),\n ],\n intent: {\n Table: child(CardTable, (out) => ({ _tag: 'Table', out })),\n Action: ({ value }) => {\n if (value === undefined) return undefined;\n try {\n const c = decode(jsonSchema, JSON.parse(value));\n return c === null ? undefined : { _tag: 'Action', action: { kind: 'command', command: c } };\n } catch {\n return undefined;\n }\n },\n Undo: () => ({ _tag: 'Action', action: { kind: 'undo' } }),\n Hint: () => ({ _tag: 'Action', action: { kind: 'command', command: { type: 'hint' } } }),\n New: () => ({ _tag: 'Dialog', dialog: 'confirm' }),\n Keys: () => ({ _tag: 'Dialog', dialog: 'keys' }),\n Cancel: ({ key, event }) =>\n event instanceof KeyboardEvent && key !== 'Escape' ? undefined : { _tag: 'Dialog', dialog: 'none' },\n Confirm: () => ({ _tag: 'Action', action: { kind: 'resign' } }),\n Color: ({ checked }) => ({ _tag: 'Color', on: checked === true }),\n Sound: ({ checked }) => ({ _tag: 'Sound', on: checked === true }),\n Setup: ({ formData }) =>\n formData === undefined\n ? undefined\n : {\n _tag: 'Action',\n action: {\n kind: 'setup',\n values: Object.fromEntries(Array.from(formData.entries(), ([key, v]) => [key, String(v)])),\n },\n },\n Key: ({ key, event }) => {\n if (\n !(event instanceof KeyboardEvent) ||\n event.defaultPrevented ||\n event.repeat ||\n key === undefined ||\n event.altKey ||\n (event.target instanceof Element && event.target.closest('input,select,textarea,dialog') !== null)\n )\n return undefined;\n const ctrl = event.ctrlKey || event.metaKey;\n if (ctrl && key.toLowerCase() !== 'z') return undefined;\n if (['u', 'd', 'h', 'n', 't', 'w', '?'].includes(key.toLowerCase()) || ctrl) event.preventDefault();\n return { _tag: 'Key', key, ctrl };\n },\n },\n update: {\n Snapshot: (s, { snapshot: next }) => {\n const changed = s.snapshot?.revision !== next.revision;\n const commands: Command<Msg>[] = [];\n if (changed && next.data.line !== '') commands.push(say(next.data.line));\n if (changed && s.snapshot !== null && s.sound && next.data.line !== '')\n commands.push(\n command(\n sounding,\n next.result === 'won' || (next.data.round !== undefined && next.data.round.bestHole < 0) ? 'clear' : 'card',\n ),\n );\n if (s.snapshot === null) commands.push(say(next.data.status));\n if (next.notice !== '' && next.notice !== s.snapshot?.notice) commands.push(say(next.notice));\n if (\n next.data.actions.some((a) => a.label === 'Next hole' || a.label === 'Finish round') &&\n !s.snapshot?.data.actions.some((a) => a.label === 'Next hole' || a.label === 'Finish round')\n )\n commands.push(focus('.advance'));\n let records = s.records;\n if (s.snapshot !== null && s.snapshot.result === null && next.result !== null) {\n const old = records[next.data.recordGroup] ?? EMPTY_ROW;\n const won = next.result === 'won';\n const streak = won ? old.streak + 1 : 0;\n const round = next.result === 'completed' ? next.data.round : undefined;\n const row: RecordRow = {\n played: old.played + 1,\n won: old.won + Number(won),\n bestScore: Math.max(old.bestScore ?? -Infinity, next.data.score),\n fewestMoves: won ? Math.min(old.fewestMoves ?? Infinity, next.data.moves) : old.fewestMoves,\n bestMs: won ? Math.min(old.bestMs ?? Infinity, next.elapsedMs) : old.bestMs,\n bestRun: Math.max(old.bestRun, next.data.bestRun),\n cleared: old.cleared + next.data.cleared,\n cleanWins: old.cleanWins + Number(won && next.undosUsed === 0 && !next.assisted),\n streak,\n longestStreak: Math.max(old.longestStreak, streak),\n completed: old.completed + Number(round !== undefined),\n totalStrokes: old.totalStrokes + (round?.strokes ?? 0),\n bestRound: round === undefined ? old.bestRound : Math.min(old.bestRound ?? Infinity, round.strokes),\n bestHole: round === undefined ? old.bestHole : Math.min(old.bestHole ?? Infinity, round.bestHole),\n underPar: old.underPar + Number(round !== undefined && round.strokes <= round.par),\n };\n records = { ...records, [next.data.recordGroup]: row };\n commands.push(command(writing, records));\n commands.push(say(next.summary));\n }\n return [\n {\n ...s,\n snapshot: next,\n records,\n options: { ...s.options, back: next.data.back },\n message: next.notice || (changed ? next.data.line : s.message),\n dialog: next.result !== null ? 'none' : s.dialog,\n },\n commands,\n ];\n },\n Preferences: (s, { p }) => ({\n ...s,\n dark: p.colorScheme === 'dark',\n options: { ...s.options, calm: p.reducedMotion, singleKeys: p.singleKeyShortcuts },\n }),\n Records: (s, m) => ({ ...s, records: m.records }),\n Tick: (s, m) => ({ ...s, elapsedMs: m.ms }),\n Message: (s, m) => [{ ...s, message: m.message }, [say(m.message)]],\n Table: (s, { out }) =>\n out._tag === 'Announce'\n ? [s, [say(out.message)]]\n : s.snapshot?.result === null && !s.snapshot.paused\n ? [s, [send({ kind: 'table', out })]]\n : s,\n Action: (s, m) =>\n s.snapshot?.result === null && !s.snapshot.paused ? [{ ...s, dialog: 'none' }, [send(m.action)]] : s,\n Dialog: (s, m) => [\n { ...s, dialog: m.dialog },\n m.dialog === 'none' ? [focus('.actions button')] : [focus('.panel button')],\n ],\n Color: (s, m) => [{ ...s, options: { ...s.options, fourColor: m.on } }, [command(color, m.on)]],\n Sound: (s, m) => ({ ...s, sound: m.on }),\n Key: (s, m) => {\n if (m.key === '?') return s.options.singleKeys ? [{ ...s, dialog: 'keys' }, [focus('.panel button')]] : s;\n if ((m.ctrl && m.key.toLowerCase() === 'z') || (s.options.singleKeys && m.key.toLowerCase() === 'u'))\n return s.snapshot?.canUndo ? [s, [send({ kind: 'undo' })]] : s;\n return s.options.singleKeys && s.snapshot?.result === null\n ? [s, [send({ kind: 'key', key: m.key.toLowerCase() })]]\n : s;\n },\n },\n view: (s): ChildValue => {\n const snap = s.snapshot;\n if (snap === null) return html`<p>Dealing the cards…</p>`;\n const d = snap.data;\n const records = s.records[d.recordGroup] ?? EMPTY_ROW;\n return html`<section class=\"slate ${d.theme} ${s.dark ? 'dark' : 'light'}\" aria-label=${d.title} data-intent-keydown=${i.Key}>\n <div ?inert=${s.dialog !== 'none'}>\n <header><h2>${d.title}</h2><dl>${d.meters.map((meter) => html`<div><dt>${meter.label}</dt><dd>${meter.value}</dd></div>`)}<div><dt>Time</dt><dd>${timeText(snap.result === null ? Math.max(s.elapsedMs, snap.elapsedMs) : snap.elapsedMs)}</dd></div></dl><p>${snap.summary || d.status}</p></header>\n <jy-card-table .layout=${d.layout} .options=${s.options} data-intent=${i.Table}></jy-card-table>\n <p class=\"message\" aria-hidden=\"true\">${s.message}</p>\n <div class=\"actions\"><button type=\"button\" ?disabled=${snap.result !== null} data-intent=${i.New}>New game</button><button type=\"button\" ?disabled=${!snap.canUndo} data-intent=${i.Undo}>Undo</button><button type=\"button\" ?disabled=${snap.result !== null} data-intent=${i.Hint}>Hint</button>${d.actions.map((a) => html`<button class=${a.label === 'Next hole' || a.label === 'Finish round' ? 'advance' : ''} type=\"button\" ?disabled=${a.disabled || snap.result !== null} value=${JSON.stringify(a.command)} data-intent=${i.Action}>${a.label}</button>`)}<button type=\"button\" aria-haspopup=\"dialog\" data-intent=${i.Keys}>Keys</button></div>\n ${d.scorecard === undefined ? nothing : html`<div class=\"scorecard\"><table><caption>Round scorecard</caption><thead><tr><th scope=\"col\">Hole</th><th scope=\"col\">Strokes</th><th scope=\"col\">Running total</th><th scope=\"col\">Par</th></tr></thead><tbody>${d.scorecard.map((row) => html`<tr><th scope=\"row\">${row.hole}</th><td>${row.strokes ?? '–'} ${row.cleared ? '⚑ Cleared' : ''}</td><td>${row.total ?? '–'}</td><td>${row.par}</td></tr>`)}</tbody></table></div>`}\n <details><summary>Settings and deal number</summary><form data-intent=${i.Setup} aria-label=\"Settings\"><fieldset ?disabled=${!d.setupAllowed || snap.result !== null}><legend>Choose before the first move</legend>${d.fields.map((field) => html`<label>${field.label}${field.choices === undefined ? html`<input name=${field.name} type=\"number\" min=\"1\" max=\"1000000\" value=${field.value} required />` : html`<select name=${field.name}>${field.choices.map((choice) => html`<option value=${choice.value} ?selected=${field.value === choice.value}>${choice.label}</option>`)}</select>`}</label>`)}<button type=\"submit\">Deal</button></fieldset></form><label><input type=\"checkbox\" ?checked=${s.options.fourColor} data-intent=${i.Color} /> Four-color deck (blue diamonds, green clubs)</label><label><input type=\"checkbox\" ?checked=${s.sound} data-intent=${i.Sound} /> Soft card sounds</label></details>\n ${d.history === undefined ? nothing : html`<details><summary>Removed cards</summary><ul>${d.history.map((card) => html`<li>${card}</li>`)}</ul></details>`}\n <details><summary>Records on this device</summary><dl>${d.round === undefined ? nothing : html`<div><dt>Rounds completed</dt><dd>${records.completed}</dd></div><div><dt>Best round</dt><dd>${records.bestRound ?? '–'}</dd></div><div><dt>Average round</dt><dd>${records.completed === 0 ? '–' : (records.totalStrokes / records.completed).toFixed(1)}</dd></div><div><dt>Best hole</dt><dd>${records.bestHole ?? '–'}</dd></div><div><dt>Rounds at or under par</dt><dd>${records.underPar}</dd></div>`}<div><dt>Played</dt><dd>${records.played}</dd></div><div><dt>Won</dt><dd>${records.won}</dd></div><div><dt>Best score</dt><dd>${records.bestScore ?? '–'}</dd></div><div><dt>Fewest moves in a win</dt><dd>${records.fewestMoves ?? '–'}</dd></div><div><dt>Best time</dt><dd>${records.bestMs === null ? '–' : timeText(records.bestMs)}</dd></div><div><dt>Best run</dt><dd>${records.bestRun}</dd></div><div><dt>Cleared</dt><dd>${records.cleared}</dd></div><div><dt>Wins without help</dt><dd>${records.cleanWins}</dd></div><div><dt>Win streak (best)</dt><dd>${records.streak} (${records.longestStreak})</dd></div></dl></details>\n </div>\n ${s.dialog === 'none' ? nothing : html`<dialog ?modal=${true} class=\"panel\" aria-label=${s.dialog === 'keys' ? 'Keyboard controls' : 'End this deal?'} data-intent-keydown=${i.Cancel} data-intent-close=${i.Cancel}><h3>${s.dialog === 'keys' ? 'Keyboard controls' : 'End this deal?'}</h3><p>${s.dialog === 'keys' ? `${d.keys} Single-key shortcuts ${s.options.singleKeys ? 'are on' : 'are off'}.` : 'This game ends now and counts as played. Play again offers a fresh deal.'}</p>${s.dialog === 'confirm' ? html`<button type=\"button\" data-intent=${i.Confirm}>End and start again</button>` : nothing}<button type=\"button\" data-intent=${i.Cancel}>${s.dialog === 'keys' ? 'Close' : 'Keep playing'}</button></dialog>`}\n </section>`;\n },\n styles: css`@layer components {\n :host { display:block; container-type:inline-size; font: 1rem/1.5 system-ui; }\n .slate { --ink:oklch(23% 0.03 70); --paper:oklch(93% 0.04 80); color:var(--ink); background:var(--paper); padding:clamp(.25rem,1cqi,1rem); border-radius:1rem; }\n .noon { --paper:oklch(94% 0.04 80); --back-a:oklch(45% 0.1 65); --back-b:oklch(72% 0.08 80); --back-c:oklch(93% 0.03 85); }\n .dawn { --paper:oklch(94% 0.018 245); --back-a:oklch(38% 0.08 255); --back-b:oklch(68% 0.08 250); --back-c:oklch(88% 0.08 80); }\n .dusk { --paper:oklch(91% 0.03 110); --back-a:oklch(38% 0.035 170); --back-b:oklch(65% 0.05 130); --back-c:oklch(85% 0.07 80); }\n .dark { --paper:oklch(24% 0.035 170); --ink:oklch(95% 0.012 110); }\n .noon.dark { --paper:oklch(25% 0.035 65); } .dawn.dark { --paper:oklch(25% 0.04 260); }\n jy-card-table { --table-ink:var(--ink); --table-slot:color-mix(in oklch,var(--ink) 45%,transparent); --table-felt:var(--paper); --table-focus:oklch(68% 0.16 255); --table-target:oklch(55% 0.16 140); --table-hint:oklch(62% 0.15 45); }\n h2,p { margin-block:.35rem; } dl { display:flex; flex-wrap:wrap; gap:.4rem 1.25rem; margin-block:.4rem; } dt { font-size:.8rem; } dd { margin:0; font-variant-numeric:tabular-nums; font-weight:700; }\n button,input,select,summary { min-block-size:44px; } button,input,select { font:inherit; color:inherit; border:1px solid currentColor; border-radius:.4rem; background:var(--paper); padding:.35rem .6rem; } button { cursor:pointer; } button:disabled { opacity:.6; cursor:default; } :focus-visible { outline:3px solid var(--ink); outline-offset:3px; }\n .actions,fieldset { display:flex; flex-wrap:wrap; gap:.5rem; } label { display:flex; align-items:center; flex-wrap:wrap; gap:.5rem; } fieldset { border:0; margin:0; padding:.5rem 0; } legend { padding:0; } input[type=number] { inline-size:10ch; } input[type=checkbox] { min-inline-size:24px; }\n details { border-block-start:1px solid color-mix(in oklch,var(--ink) 35%,transparent); margin-block:.65rem; padding-block:.35rem; } summary { cursor:pointer; } .message { min-block-size:1.5em; } .scorecard { overflow:auto; } table { border-collapse:collapse; inline-size:100%; font-variant-numeric:tabular-nums; } th,td { padding:.35rem; border-block-end:1px solid currentColor; text-align:start; } caption { text-align:start; font-weight:700; }\n .panel { position:fixed; inset:0; margin:auto; max-inline-size:min(36rem,calc(100vi - 2rem)); border:2px solid var(--ink); border-radius:1rem; background:var(--paper); color:var(--ink); padding:1rem; box-shadow:0 0 0 100vmax oklch(0% 0 0 / .5); z-index:30; } .panel button { margin:.25rem; }\n @media (forced-colors:active) { .slate,.panel { background:Canvas; color:CanvasText; } button,input,select { border:1px solid ButtonText; } }\n }`,\n});\n\nexport const mountSolitaire = <V, E>(\n slot: HTMLElement,\n session: AnyGameSession,\n platform: PlatformApi,\n adapter: SolitaireAdapter<V, E>,\n): Mounted => {\n const board = new SolitaireBoard();\n let latest: V | null = null;\n let disposed = false;\n board.drivers = {\n 'solitaire-session': subscription<Snapshot>('solitaire-session', (emit) =>\n session.subscribe((raw: unknown) => {\n const meta = decode(metaSchema, raw);\n if (meta === null || !record(raw)) return;\n const view = decode(adapter.view, raw['view']);\n if (view === null) return;\n const events: E[] = [];\n for (const beat of Array.isArray(raw['beats']) ? raw['beats'] : []) {\n const event = record(beat) ? decode(adapter.event, beat['event']) : null;\n if (event === null) return;\n events.push(event);\n }\n latest = view;\n emit({ ...meta, data: adapter.describe(view, events, meta.revision) });\n }),\n ),\n 'solitaire-preferences': subscription<Preferences>('solitaire-preferences', (emit) => {\n emit(platform.preferences.get());\n return platform.preferences.subscribe(emit);\n }),\n 'solitaire-timer': subscription<number>('solitaire-timer', (emit) => {\n const clock = session as AnyGameSession & { elapsedMs?: () => number };\n if (clock.elapsedMs === undefined) return () => {};\n const id = setInterval(() => emit(clock.elapsedMs!()), 250);\n return () => clearInterval(id);\n }),\n 'solitaire-speak': defineDriver<string, void>({\n name: 'solitaire-speak',\n run: (line) => platform.announce(line, 'polite'),\n }),\n 'solitaire-read': defineDriver<string, Json | null>({\n name: 'solitaire-read',\n run: (key) => platform.storage.get(key),\n }),\n 'solitaire-write': defineDriver<Records, void>({\n name: 'solitaire-write',\n run: (rows) => platform.storage.set('records', rows),\n }),\n 'solitaire-sound': defineDriver<'card' | 'clear', void>({\n name: 'solitaire-sound',\n run: (kind) => {\n if (!disposed) platform.audio?.play(kind);\n },\n }),\n 'solitaire-color': defineDriver<boolean, void>({\n name: 'solitaire-color',\n run: (on) => platform.storage.set('fourColor', on),\n }),\n 'solitaire-send': defineDriver<Action, string>({\n name: 'solitaire-send',\n run: async (a) => {\n if (latest === null || disposed) return '';\n if (a.kind === 'undo' || a.kind === 'resign') {\n (session.dispatch as (input: unknown) => void)({ kind: a.kind });\n return '';\n }\n if (a.kind === 'table' && a.out._tag === 'Announce') {\n platform.announce(a.out.message, 'polite');\n return '';\n }\n const c =\n a.kind === 'command'\n ? a.command\n : a.kind === 'table'\n ? adapter.table(latest, a.out)\n : a.kind === 'key'\n ? adapter.shortcut(latest, a.key)\n : a.kind === 'setup'\n ? adapter.setup(latest, a.values)\n : null;\n if (c === null) return a.kind === 'setup' ? 'Choose valid settings and a deal number from 1 to 1,000,000.' : '';\n const command = a.kind === 'setup' && adapter.prepareSetup !== undefined ? await adapter.prepareSetup(c) : c;\n if (!disposed) (session.dispatch as (input: unknown) => void)({ kind: 'command', command });\n return '';\n },\n }),\n };\n slot.append(board);\n return {\n dispose: () => {\n if (disposed) return;\n disposed = true;\n board.remove();\n adapter.dispose?.();\n },\n };\n};\n\nexport { workerSetup } from './worker-setup.ts';\n"],"mappings":";;;;;;;AAAA,IAAA,YAAA;CAAA,MAAA;CAAA,QAAA;CAAA,SAAA,CAAA;CAAA,YAAA,CAAA,2BAAA;AAAA;AAAA,IAAA,YAAA;CAAA,MAAA;CAAA,QAAA;CAAA,SAAA;EAAA;GAAA;GAAA,CAAA,CAAA;GAAA;GAAA;IAAA;IAAA;IAAA;GAAA;EAAA;EAAA;GAAA;GAAA,CAAA,CAAA;GAAA;EAAA;EAAA;GAAA;GAAA,CAAA,CAAA;GAAA;EAAA;EAAA;GAAA;GAAA,CAAA,GAAA,CAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA,CAAA,GAAA,CAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA,CAAA,GAAA,CAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA,CAAA,CAAA,CAAA;CAAA;CAAA,YAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;GAAA,WAAA;IAAA;IAAA;IAAA;GAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,OAAA;GAAA,QAAA;GAAA,SAAA,CAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA,EAAA,KAAA,UAAA;EAAA;GAAA,KAAA;GAAA,OAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EA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"}
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const tableStyles: import("@gyral/core").StyleSource;
|
package/dist/styles.js
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import { css } from "@gyral/core";
|
|
2
|
+
//#region src/styles.ts
|
|
3
|
+
/** Grid columns, region widths and pile spans as classes (no inline styles). */
|
|
4
|
+
var counts = Array.from({ length: 12 }, (_, k) => k + 1).map((n) => `.cols-${n} { --cols: ${n}; } .n-${n} { --n: ${n}; } .w-${n} { grid-column: span ${n}; }`).join("\n");
|
|
5
|
+
var positions = Array.from({ length: 49 }, (_, n) => `.pos-c-${n} { grid-column: ${n + 1} / span 4; }`).concat(Array.from({ length: 12 }, (_, n) => `.pos-r-${n} { grid-row: ${n + 1} / span 2; }`)).join("\n");
|
|
6
|
+
var tableStyles = css`
|
|
7
|
+
@layer components {
|
|
8
|
+
${counts}
|
|
9
|
+
${positions}
|
|
10
|
+
|
|
11
|
+
:host {
|
|
12
|
+
--table-felt: oklch(93% 0.03 85);
|
|
13
|
+
--table-ink: oklch(25% 0.03 60);
|
|
14
|
+
--table-slot: color-mix(in oklch, var(--table-ink) 28%, transparent);
|
|
15
|
+
--table-focus: oklch(55% 0.2 255);
|
|
16
|
+
--table-select: oklch(62% 0.19 45);
|
|
17
|
+
--table-target: oklch(50% 0.17 150);
|
|
18
|
+
--table-hint: oklch(72% 0.16 85);
|
|
19
|
+
--back-a: oklch(42% 0.07 250);
|
|
20
|
+
--back-b: oklch(62% 0.08 250);
|
|
21
|
+
--back-c: oklch(80% 0.11 85);
|
|
22
|
+
--card-paper: oklch(98.5% 0.008 90);
|
|
23
|
+
--card-ink: oklch(24% 0.02 260);
|
|
24
|
+
--suit-black: oklch(24% 0.02 260);
|
|
25
|
+
--suit-red: oklch(52% 0.2 25);
|
|
26
|
+
--suit-blue: oklch(47% 0.17 255);
|
|
27
|
+
--suit-green: oklch(48% 0.13 150);
|
|
28
|
+
|
|
29
|
+
display: block;
|
|
30
|
+
container-type: inline-size;
|
|
31
|
+
color: var(--table-ink);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.defs {
|
|
35
|
+
position: absolute;
|
|
36
|
+
inline-size: 0;
|
|
37
|
+
block-size: 0;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.table {
|
|
42
|
+
--gap: clamp(0.2rem, 1.3cqi, 0.85rem);
|
|
43
|
+
--card-w: min(calc((100cqi - (var(--cols) - 1) * var(--gap)) / var(--cols)), var(--card-max, 7.5rem));
|
|
44
|
+
--card-h: calc(var(--card-w) * 1.4);
|
|
45
|
+
--radius: calc(var(--card-w) * 0.09);
|
|
46
|
+
--fan-down: calc(var(--card-h) * 0.1);
|
|
47
|
+
--fan-up: calc(var(--card-h) * 0.25);
|
|
48
|
+
--fan-right: calc(var(--card-w) * 0.32);
|
|
49
|
+
|
|
50
|
+
display: grid;
|
|
51
|
+
grid-template-columns: repeat(var(--cols), var(--card-w));
|
|
52
|
+
justify-content: center;
|
|
53
|
+
column-gap: var(--gap);
|
|
54
|
+
row-gap: calc(var(--gap) * 2 + 0.5rem);
|
|
55
|
+
padding-block: 0.6rem calc(var(--card-h) * 0.4);
|
|
56
|
+
user-select: none;
|
|
57
|
+
-webkit-user-select: none;
|
|
58
|
+
-webkit-tap-highlight-color: transparent;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.region {
|
|
62
|
+
display: grid;
|
|
63
|
+
grid-template-columns: subgrid;
|
|
64
|
+
align-items: start;
|
|
65
|
+
grid-row: var(--row);
|
|
66
|
+
}
|
|
67
|
+
.free-placement {
|
|
68
|
+
grid-template-columns: repeat(calc(var(--cols) * 4), minmax(0, 1fr));
|
|
69
|
+
grid-auto-rows: calc(var(--card-h) / 2);
|
|
70
|
+
column-gap: 0;
|
|
71
|
+
align-items: start;
|
|
72
|
+
}
|
|
73
|
+
.free-placement .pile {
|
|
74
|
+
inline-size: var(--card-w);
|
|
75
|
+
padding-block-end: 0;
|
|
76
|
+
}
|
|
77
|
+
.free-placement .pile.empty { pointer-events: none; }
|
|
78
|
+
.pile-badge {
|
|
79
|
+
position: absolute;
|
|
80
|
+
inset-block-end: 0;
|
|
81
|
+
inset-inline-end: 0;
|
|
82
|
+
padding: 0.15em 0.3em;
|
|
83
|
+
border: 2px solid currentColor;
|
|
84
|
+
border-radius: 0.3em;
|
|
85
|
+
background: var(--card-paper);
|
|
86
|
+
color: var(--card-ink);
|
|
87
|
+
font: 700 0.85rem/1.2 system-ui;
|
|
88
|
+
z-index: 2;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.at-top-start {
|
|
92
|
+
--row: 1;
|
|
93
|
+
grid-column: 1 / span var(--n);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.at-top-end {
|
|
97
|
+
--row: 1;
|
|
98
|
+
grid-column: span var(--n) / -1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.at-main {
|
|
102
|
+
--row: 2;
|
|
103
|
+
grid-column: 1 / -1;
|
|
104
|
+
align-items: stretch;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.at-bottom-start {
|
|
108
|
+
--row: 3;
|
|
109
|
+
grid-column: 1 / span var(--n);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.at-bottom-end {
|
|
113
|
+
--row: 3;
|
|
114
|
+
grid-column: span var(--n) / -1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* A pile: its cards in flow, each pulled up over the one before it. */
|
|
118
|
+
.pile {
|
|
119
|
+
position: relative;
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
min-block-size: var(--card-h);
|
|
123
|
+
border-radius: var(--radius);
|
|
124
|
+
outline: none;
|
|
125
|
+
cursor: default;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.at-main .pile {
|
|
129
|
+
/* The whole column is the drop target (spec: Pointer). */
|
|
130
|
+
padding-block-end: calc(var(--card-h) * 0.5);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.fan-right {
|
|
134
|
+
flex-direction: row;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.slot {
|
|
138
|
+
position: absolute;
|
|
139
|
+
inset-block-start: 0;
|
|
140
|
+
inset-inline-start: 0;
|
|
141
|
+
display: grid;
|
|
142
|
+
place-items: center;
|
|
143
|
+
inline-size: var(--card-w);
|
|
144
|
+
block-size: var(--card-h);
|
|
145
|
+
border: 2px solid var(--table-slot);
|
|
146
|
+
border-radius: var(--radius);
|
|
147
|
+
color: var(--table-slot);
|
|
148
|
+
font-size: calc(var(--card-w) * 0.42);
|
|
149
|
+
font-weight: 700;
|
|
150
|
+
line-height: 1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.card {
|
|
154
|
+
position: relative;
|
|
155
|
+
flex: none;
|
|
156
|
+
inline-size: var(--card-w);
|
|
157
|
+
block-size: var(--card-h);
|
|
158
|
+
border-radius: var(--radius);
|
|
159
|
+
box-shadow: 0 1px 2px oklch(0% 0 0 / 0.28);
|
|
160
|
+
transition:
|
|
161
|
+
translate 140ms ease-out,
|
|
162
|
+
box-shadow 140ms ease-out;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.card + .card {
|
|
166
|
+
margin-block-start: calc(var(--fan-up) - var(--card-h));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.card.down + .card {
|
|
170
|
+
margin-block-start: calc(var(--fan-down) - var(--card-h));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.fan-stack .card + .card,
|
|
174
|
+
.fan-stack .card.down + .card {
|
|
175
|
+
margin-block-start: calc(-1 * var(--card-h));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.fan-right .card + .card,
|
|
179
|
+
.fan-right .card.down + .card {
|
|
180
|
+
margin-block-start: 0;
|
|
181
|
+
margin-inline-start: calc(var(--fan-right) - var(--card-w));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Long columns overlap more tightly; the top strip of each face-up card stays visible. */
|
|
185
|
+
.dense-1 {
|
|
186
|
+
--fan-up: calc(var(--card-h) * 0.21);
|
|
187
|
+
--fan-down: calc(var(--card-h) * 0.08);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.dense-2 {
|
|
191
|
+
--fan-up: calc(var(--card-h) * 0.18);
|
|
192
|
+
--fan-down: calc(var(--card-h) * 0.06);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.dense-3 {
|
|
196
|
+
--fan-up: calc(var(--card-h) * 0.165);
|
|
197
|
+
--fan-down: calc(var(--card-h) * 0.05);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* Narrow cards, under 3.5rem wide (Spider, FreeCell and Klondike on a phone; bead
|
|
202
|
+
* gp-dx1). At that size the strip's suit symbol drew about 6 CSS pixels tall, so it
|
|
203
|
+
* grows 2.4 times, to at least 12 pixels each way on a 32-pixel card (a diamond is the
|
|
204
|
+
* narrowest shape), and the rank by 40%; the middle's pips, too small to read there,
|
|
205
|
+
* give way. A column then leaves 40% of each face-up card showing, whatever its density,
|
|
206
|
+
* so the taller strip is never covered. The step below switches at the same width as the
|
|
207
|
+
* container query: (3.5rem - card width) × 1000 is 0 or less at 3.5rem and wider, and
|
|
208
|
+
* far above the 40% cap when narrower.
|
|
209
|
+
*/
|
|
210
|
+
.card {
|
|
211
|
+
container: card / inline-size;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.fan-down .card.up + .card {
|
|
215
|
+
margin-block-start: calc(max(var(--fan-up), clamp(0px, (3.5rem - var(--card-w)) * 1000, var(--card-h) * 0.4)) - var(--card-h));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@container card (inline-size < 3.5rem) {
|
|
219
|
+
.index {
|
|
220
|
+
font-size: 30px;
|
|
221
|
+
translate: 0 4px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.suit-index {
|
|
225
|
+
transform-origin: 93px 2px;
|
|
226
|
+
scale: 2.4;
|
|
227
|
+
translate: 4px 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.middle {
|
|
231
|
+
display: none;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.up {
|
|
236
|
+
background: var(--card-paper);
|
|
237
|
+
border: 1px solid color-mix(in oklch, var(--card-ink) 22%, transparent);
|
|
238
|
+
touch-action: none;
|
|
239
|
+
cursor: grab;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.face {
|
|
243
|
+
display: block;
|
|
244
|
+
inline-size: 100%;
|
|
245
|
+
block-size: 100%;
|
|
246
|
+
fill: var(--suit);
|
|
247
|
+
font-family: var(--card-font, var(--jy-font-sans, system-ui, sans-serif));
|
|
248
|
+
font-weight: 700;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.index {
|
|
252
|
+
font-size: 21px;
|
|
253
|
+
letter-spacing: -0.5px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.corner {
|
|
257
|
+
font-size: 14px;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.letter {
|
|
261
|
+
font-size: 11px;
|
|
262
|
+
font-weight: 600;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.monogram {
|
|
266
|
+
font-family: var(--card-serif, Georgia, 'Times New Roman', serif);
|
|
267
|
+
font-size: 46px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.frame {
|
|
271
|
+
fill: color-mix(in oklch, var(--suit) 9%, transparent);
|
|
272
|
+
stroke: var(--suit);
|
|
273
|
+
stroke-width: 2.5;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.crest {
|
|
277
|
+
fill: var(--suit);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.s-S,
|
|
281
|
+
.s-C {
|
|
282
|
+
--suit: var(--suit-black);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.s-H,
|
|
286
|
+
.s-D {
|
|
287
|
+
--suit: var(--suit-red);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.four .s-D {
|
|
291
|
+
--suit: var(--suit-blue);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.four .s-C {
|
|
295
|
+
--suit: var(--suit-green);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* Backs: original patterns, colored by the game's theme. */
|
|
299
|
+
.down {
|
|
300
|
+
background-color: var(--back-a);
|
|
301
|
+
border: 1px solid color-mix(in oklch, var(--back-a) 70%, black);
|
|
302
|
+
box-shadow:
|
|
303
|
+
inset 0 0 0 calc(var(--card-w) * 0.05) var(--back-a),
|
|
304
|
+
inset 0 0 0 calc(var(--card-w) * 0.07) var(--back-c),
|
|
305
|
+
0 1px 2px oklch(0% 0 0 / 0.28);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.back-lattice .down {
|
|
309
|
+
background-image:
|
|
310
|
+
radial-gradient(circle at 50% 50%, var(--back-c) 0 9%, transparent 10%),
|
|
311
|
+
repeating-linear-gradient(45deg, var(--back-b) 0 1.5px, transparent 1.5px 8px),
|
|
312
|
+
repeating-linear-gradient(-45deg, var(--back-b) 0 1.5px, transparent 1.5px 8px);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.back-web .down {
|
|
316
|
+
background-image:
|
|
317
|
+
repeating-radial-gradient(circle at 50% 50%, transparent 0 6px, var(--back-b) 6px 7px),
|
|
318
|
+
repeating-conic-gradient(from 15deg at 50% 50%, var(--back-b) 0 1.2deg, transparent 1.2deg 30deg);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.back-cells .down {
|
|
322
|
+
background-image:
|
|
323
|
+
radial-gradient(circle at 50% 50%, var(--back-c) 0 2px, transparent 2.5px),
|
|
324
|
+
linear-gradient(var(--back-b) 1px, transparent 1px),
|
|
325
|
+
linear-gradient(90deg, var(--back-b) 1px, transparent 1px);
|
|
326
|
+
background-size:
|
|
327
|
+
9px 9px,
|
|
328
|
+
9px 9px,
|
|
329
|
+
9px 9px;
|
|
330
|
+
}
|
|
331
|
+
.back-terraces .down {
|
|
332
|
+
background-image: repeating-linear-gradient(0deg, transparent 0 8px, var(--back-b) 8px 10px), repeating-linear-gradient(90deg, transparent 0 13px, var(--back-c) 13px 14px);
|
|
333
|
+
}
|
|
334
|
+
.back-contours .down {
|
|
335
|
+
background-image: repeating-radial-gradient(ellipse at 15% 80%, transparent 0 9px, var(--back-b) 9px 10px, transparent 10px 14px);
|
|
336
|
+
}
|
|
337
|
+
.back-dunes .down {
|
|
338
|
+
background-image: repeating-radial-gradient(ellipse at 120% 120%, transparent 0 7px, var(--back-b) 7px 9px, transparent 9px 16px);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* Focus: the pile, and in a pile the card Enter would pick up. */
|
|
342
|
+
.pile:focus-visible {
|
|
343
|
+
outline: 3px solid var(--table-focus);
|
|
344
|
+
outline-offset: 3px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.pile:focus-visible .pointed {
|
|
348
|
+
outline: 3px solid var(--table-focus);
|
|
349
|
+
outline-offset: 1px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* Picked up: lifted, with a shadow and a thick outline. */
|
|
353
|
+
.held {
|
|
354
|
+
z-index: 2;
|
|
355
|
+
translate: 0 calc(var(--card-h) * -0.07);
|
|
356
|
+
box-shadow: 0 0.5rem 1rem oklch(0% 0 0 / 0.35);
|
|
357
|
+
outline: 3px solid var(--table-select);
|
|
358
|
+
outline-offset: 1px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.dragging {
|
|
362
|
+
z-index: 20;
|
|
363
|
+
pointer-events: none;
|
|
364
|
+
transition: none;
|
|
365
|
+
cursor: grabbing;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Legal destinations: a dashed outline and a drop marker, not just a tint. */
|
|
369
|
+
.target::after,
|
|
370
|
+
.hint-to::after {
|
|
371
|
+
content: '';
|
|
372
|
+
position: absolute;
|
|
373
|
+
inset: -5px;
|
|
374
|
+
border: 3px dashed var(--table-target);
|
|
375
|
+
border-radius: calc(var(--radius) + 4px);
|
|
376
|
+
pointer-events: none;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.drop {
|
|
380
|
+
display: none;
|
|
381
|
+
position: absolute;
|
|
382
|
+
z-index: 3;
|
|
383
|
+
inset-block-start: -0.95rem;
|
|
384
|
+
inset-inline-start: 50%;
|
|
385
|
+
translate: -50% 0;
|
|
386
|
+
border: 0.5rem solid transparent;
|
|
387
|
+
border-block-end-width: 0;
|
|
388
|
+
border-block-start-color: var(--table-target);
|
|
389
|
+
pointer-events: none;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.target .drop {
|
|
393
|
+
display: block;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.over::after {
|
|
397
|
+
border-style: solid;
|
|
398
|
+
background: color-mix(in oklch, var(--table-target) 16%, transparent);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.hint-to::after {
|
|
402
|
+
border-color: var(--table-hint);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.hinted {
|
|
406
|
+
outline: 4px solid var(--table-hint);
|
|
407
|
+
outline-offset: 1px;
|
|
408
|
+
animation: hint 0.9s ease-in-out 2;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
@keyframes hint {
|
|
412
|
+
50% {
|
|
413
|
+
translate: 0 calc(var(--card-h) * -0.06);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.calm .card,
|
|
418
|
+
.calm .hinted {
|
|
419
|
+
transition: none;
|
|
420
|
+
animation: none;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
@media (prefers-reduced-motion: reduce) {
|
|
424
|
+
.card,
|
|
425
|
+
.hinted {
|
|
426
|
+
transition: none;
|
|
427
|
+
animation: none;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@media (forced-colors: active) {
|
|
432
|
+
.pile:focus-visible,
|
|
433
|
+
.pile:focus-visible .pointed,
|
|
434
|
+
.held {
|
|
435
|
+
outline-color: Highlight;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.target::after,
|
|
439
|
+
.hint-to::after {
|
|
440
|
+
border-color: LinkText;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.face {
|
|
444
|
+
fill: CanvasText;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
`;
|
|
449
|
+
//#endregion
|
|
450
|
+
export { tableStyles };
|
|
451
|
+
|
|
452
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","names":[],"sources":["../src/styles.ts"],"sourcesContent":["// The card table's styles: a grid of card-sized columns that fits its container, piles that\n// fan down, right or not at all, the card faces and three original backs, and the states the\n// controller shows (spec: \"Not by color alone\"): a lifted, outlined pick-up; dashed outlines\n// and a drop marker on legal destinations; a thick ring on the focused pile and card.\n//\n// Games theme the table through inherited custom properties on <jy-card-table>:\n// --table-felt, --table-ink, --table-slot, --table-focus, --table-select, --table-target,\n// --table-hint, --back-a, --back-b, --back-c, --card-paper, --card-ink, --suit-black,\n// --suit-red, --suit-blue, --suit-green, --card-max (the widest card).\n// The site's Content Security Policy refuses style attributes, so counts become classes.\nimport { css } from '@gyral/core';\n\n/** Grid columns, region widths and pile spans as classes (no inline styles). */\nconst counts = Array.from({ length: 12 }, (_, k) => k + 1)\n .map((n) => `.cols-${n} { --cols: ${n}; } .n-${n} { --n: ${n}; } .w-${n} { grid-column: span ${n}; }`)\n .join('\\n');\nconst positions = Array.from({ length: 49 }, (_, n) => `.pos-c-${n} { grid-column: ${n + 1} / span 4; }`)\n .concat(Array.from({ length: 12 }, (_, n) => `.pos-r-${n} { grid-row: ${n + 1} / span 2; }`))\n .join('\\n');\n\nexport const tableStyles = css`\n @layer components {\n ${counts}\n ${positions}\n\n :host {\n --table-felt: oklch(93% 0.03 85);\n --table-ink: oklch(25% 0.03 60);\n --table-slot: color-mix(in oklch, var(--table-ink) 28%, transparent);\n --table-focus: oklch(55% 0.2 255);\n --table-select: oklch(62% 0.19 45);\n --table-target: oklch(50% 0.17 150);\n --table-hint: oklch(72% 0.16 85);\n --back-a: oklch(42% 0.07 250);\n --back-b: oklch(62% 0.08 250);\n --back-c: oklch(80% 0.11 85);\n --card-paper: oklch(98.5% 0.008 90);\n --card-ink: oklch(24% 0.02 260);\n --suit-black: oklch(24% 0.02 260);\n --suit-red: oklch(52% 0.2 25);\n --suit-blue: oklch(47% 0.17 255);\n --suit-green: oklch(48% 0.13 150);\n\n display: block;\n container-type: inline-size;\n color: var(--table-ink);\n }\n\n .defs {\n position: absolute;\n inline-size: 0;\n block-size: 0;\n overflow: hidden;\n }\n\n .table {\n --gap: clamp(0.2rem, 1.3cqi, 0.85rem);\n --card-w: min(calc((100cqi - (var(--cols) - 1) * var(--gap)) / var(--cols)), var(--card-max, 7.5rem));\n --card-h: calc(var(--card-w) * 1.4);\n --radius: calc(var(--card-w) * 0.09);\n --fan-down: calc(var(--card-h) * 0.1);\n --fan-up: calc(var(--card-h) * 0.25);\n --fan-right: calc(var(--card-w) * 0.32);\n\n display: grid;\n grid-template-columns: repeat(var(--cols), var(--card-w));\n justify-content: center;\n column-gap: var(--gap);\n row-gap: calc(var(--gap) * 2 + 0.5rem);\n padding-block: 0.6rem calc(var(--card-h) * 0.4);\n user-select: none;\n -webkit-user-select: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n .region {\n display: grid;\n grid-template-columns: subgrid;\n align-items: start;\n grid-row: var(--row);\n }\n .free-placement {\n grid-template-columns: repeat(calc(var(--cols) * 4), minmax(0, 1fr));\n grid-auto-rows: calc(var(--card-h) / 2);\n column-gap: 0;\n align-items: start;\n }\n .free-placement .pile {\n inline-size: var(--card-w);\n padding-block-end: 0;\n }\n .free-placement .pile.empty { pointer-events: none; }\n .pile-badge {\n position: absolute;\n inset-block-end: 0;\n inset-inline-end: 0;\n padding: 0.15em 0.3em;\n border: 2px solid currentColor;\n border-radius: 0.3em;\n background: var(--card-paper);\n color: var(--card-ink);\n font: 700 0.85rem/1.2 system-ui;\n z-index: 2;\n }\n\n .at-top-start {\n --row: 1;\n grid-column: 1 / span var(--n);\n }\n\n .at-top-end {\n --row: 1;\n grid-column: span var(--n) / -1;\n }\n\n .at-main {\n --row: 2;\n grid-column: 1 / -1;\n align-items: stretch;\n }\n\n .at-bottom-start {\n --row: 3;\n grid-column: 1 / span var(--n);\n }\n\n .at-bottom-end {\n --row: 3;\n grid-column: span var(--n) / -1;\n }\n\n /* A pile: its cards in flow, each pulled up over the one before it. */\n .pile {\n position: relative;\n display: flex;\n flex-direction: column;\n min-block-size: var(--card-h);\n border-radius: var(--radius);\n outline: none;\n cursor: default;\n }\n\n .at-main .pile {\n /* The whole column is the drop target (spec: Pointer). */\n padding-block-end: calc(var(--card-h) * 0.5);\n }\n\n .fan-right {\n flex-direction: row;\n }\n\n .slot {\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 0;\n display: grid;\n place-items: center;\n inline-size: var(--card-w);\n block-size: var(--card-h);\n border: 2px solid var(--table-slot);\n border-radius: var(--radius);\n color: var(--table-slot);\n font-size: calc(var(--card-w) * 0.42);\n font-weight: 700;\n line-height: 1;\n }\n\n .card {\n position: relative;\n flex: none;\n inline-size: var(--card-w);\n block-size: var(--card-h);\n border-radius: var(--radius);\n box-shadow: 0 1px 2px oklch(0% 0 0 / 0.28);\n transition:\n translate 140ms ease-out,\n box-shadow 140ms ease-out;\n }\n\n .card + .card {\n margin-block-start: calc(var(--fan-up) - var(--card-h));\n }\n\n .card.down + .card {\n margin-block-start: calc(var(--fan-down) - var(--card-h));\n }\n\n .fan-stack .card + .card,\n .fan-stack .card.down + .card {\n margin-block-start: calc(-1 * var(--card-h));\n }\n\n .fan-right .card + .card,\n .fan-right .card.down + .card {\n margin-block-start: 0;\n margin-inline-start: calc(var(--fan-right) - var(--card-w));\n }\n\n /* Long columns overlap more tightly; the top strip of each face-up card stays visible. */\n .dense-1 {\n --fan-up: calc(var(--card-h) * 0.21);\n --fan-down: calc(var(--card-h) * 0.08);\n }\n\n .dense-2 {\n --fan-up: calc(var(--card-h) * 0.18);\n --fan-down: calc(var(--card-h) * 0.06);\n }\n\n .dense-3 {\n --fan-up: calc(var(--card-h) * 0.165);\n --fan-down: calc(var(--card-h) * 0.05);\n }\n\n /*\n * Narrow cards, under 3.5rem wide (Spider, FreeCell and Klondike on a phone; bead\n * gp-dx1). At that size the strip's suit symbol drew about 6 CSS pixels tall, so it\n * grows 2.4 times, to at least 12 pixels each way on a 32-pixel card (a diamond is the\n * narrowest shape), and the rank by 40%; the middle's pips, too small to read there,\n * give way. A column then leaves 40% of each face-up card showing, whatever its density,\n * so the taller strip is never covered. The step below switches at the same width as the\n * container query: (3.5rem - card width) × 1000 is 0 or less at 3.5rem and wider, and\n * far above the 40% cap when narrower.\n */\n .card {\n container: card / inline-size;\n }\n\n .fan-down .card.up + .card {\n margin-block-start: calc(max(var(--fan-up), clamp(0px, (3.5rem - var(--card-w)) * 1000, var(--card-h) * 0.4)) - var(--card-h));\n }\n\n @container card (inline-size < 3.5rem) {\n .index {\n font-size: 30px;\n translate: 0 4px;\n }\n\n .suit-index {\n transform-origin: 93px 2px;\n scale: 2.4;\n translate: 4px 0;\n }\n\n .middle {\n display: none;\n }\n }\n\n .up {\n background: var(--card-paper);\n border: 1px solid color-mix(in oklch, var(--card-ink) 22%, transparent);\n touch-action: none;\n cursor: grab;\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 .index {\n font-size: 21px;\n letter-spacing: -0.5px;\n }\n\n .corner {\n font-size: 14px;\n }\n\n .letter {\n font-size: 11px;\n font-weight: 600;\n }\n\n .monogram {\n font-family: var(--card-serif, Georgia, 'Times New Roman', serif);\n font-size: 46px;\n }\n\n .frame {\n fill: color-mix(in oklch, var(--suit) 9%, transparent);\n stroke: var(--suit);\n stroke-width: 2.5;\n }\n\n .crest {\n fill: var(--suit);\n }\n\n .s-S,\n .s-C {\n --suit: var(--suit-black);\n }\n\n .s-H,\n .s-D {\n --suit: var(--suit-red);\n }\n\n .four .s-D {\n --suit: var(--suit-blue);\n }\n\n .four .s-C {\n --suit: var(--suit-green);\n }\n\n /* Backs: original patterns, colored by the game's theme. */\n .down {\n background-color: var(--back-a);\n border: 1px solid color-mix(in oklch, var(--back-a) 70%, black);\n box-shadow:\n inset 0 0 0 calc(var(--card-w) * 0.05) var(--back-a),\n inset 0 0 0 calc(var(--card-w) * 0.07) var(--back-c),\n 0 1px 2px oklch(0% 0 0 / 0.28);\n }\n\n .back-lattice .down {\n background-image:\n radial-gradient(circle at 50% 50%, var(--back-c) 0 9%, transparent 10%),\n repeating-linear-gradient(45deg, var(--back-b) 0 1.5px, transparent 1.5px 8px),\n repeating-linear-gradient(-45deg, var(--back-b) 0 1.5px, transparent 1.5px 8px);\n }\n\n .back-web .down {\n background-image:\n repeating-radial-gradient(circle at 50% 50%, transparent 0 6px, var(--back-b) 6px 7px),\n repeating-conic-gradient(from 15deg at 50% 50%, var(--back-b) 0 1.2deg, transparent 1.2deg 30deg);\n }\n\n .back-cells .down {\n background-image:\n radial-gradient(circle at 50% 50%, var(--back-c) 0 2px, transparent 2.5px),\n linear-gradient(var(--back-b) 1px, transparent 1px),\n linear-gradient(90deg, var(--back-b) 1px, transparent 1px);\n background-size:\n 9px 9px,\n 9px 9px,\n 9px 9px;\n }\n .back-terraces .down {\n background-image: repeating-linear-gradient(0deg, transparent 0 8px, var(--back-b) 8px 10px), repeating-linear-gradient(90deg, transparent 0 13px, var(--back-c) 13px 14px);\n }\n .back-contours .down {\n background-image: repeating-radial-gradient(ellipse at 15% 80%, transparent 0 9px, var(--back-b) 9px 10px, transparent 10px 14px);\n }\n .back-dunes .down {\n background-image: repeating-radial-gradient(ellipse at 120% 120%, transparent 0 7px, var(--back-b) 7px 9px, transparent 9px 16px);\n }\n\n /* Focus: the pile, and in a pile the card Enter would pick up. */\n .pile:focus-visible {\n outline: 3px solid var(--table-focus);\n outline-offset: 3px;\n }\n\n .pile:focus-visible .pointed {\n outline: 3px solid var(--table-focus);\n outline-offset: 1px;\n }\n\n /* Picked up: lifted, with a shadow and a thick outline. */\n .held {\n z-index: 2;\n translate: 0 calc(var(--card-h) * -0.07);\n box-shadow: 0 0.5rem 1rem oklch(0% 0 0 / 0.35);\n outline: 3px solid var(--table-select);\n outline-offset: 1px;\n }\n\n .dragging {\n z-index: 20;\n pointer-events: none;\n transition: none;\n cursor: grabbing;\n }\n\n /* Legal destinations: a dashed outline and a drop marker, not just a tint. */\n .target::after,\n .hint-to::after {\n content: '';\n position: absolute;\n inset: -5px;\n border: 3px dashed var(--table-target);\n border-radius: calc(var(--radius) + 4px);\n pointer-events: none;\n }\n\n .drop {\n display: none;\n position: absolute;\n z-index: 3;\n inset-block-start: -0.95rem;\n inset-inline-start: 50%;\n translate: -50% 0;\n border: 0.5rem solid transparent;\n border-block-end-width: 0;\n border-block-start-color: var(--table-target);\n pointer-events: none;\n }\n\n .target .drop {\n display: block;\n }\n\n .over::after {\n border-style: solid;\n background: color-mix(in oklch, var(--table-target) 16%, transparent);\n }\n\n .hint-to::after {\n border-color: var(--table-hint);\n }\n\n .hinted {\n outline: 4px solid var(--table-hint);\n outline-offset: 1px;\n animation: hint 0.9s ease-in-out 2;\n }\n\n @keyframes hint {\n 50% {\n translate: 0 calc(var(--card-h) * -0.06);\n }\n }\n\n .calm .card,\n .calm .hinted {\n transition: none;\n animation: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .card,\n .hinted {\n transition: none;\n animation: none;\n }\n }\n\n @media (forced-colors: active) {\n .pile:focus-visible,\n .pile:focus-visible .pointed,\n .held {\n outline-color: Highlight;\n }\n\n .target::after,\n .hint-to::after {\n border-color: LinkText;\n }\n\n .face {\n fill: CanvasText;\n }\n }\n }\n`;\n"],"mappings":";;;AAaA,IAAM,SAAS,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,IAAI,CAAC,CAAC,CACvD,KAAK,MAAM,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,uBAAuB,EAAE,IAAI,CAAC,CACrG,KAAK,IAAI;AACZ,IAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,UAAU,EAAE,kBAAkB,IAAI,EAAE,aAAa,CAAC,CACtG,OAAO,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAAM,UAAU,EAAE,eAAe,IAAI,EAAE,aAAa,CAAC,CAAC,CAC5F,KAAK,IAAI;AAEZ,IAAa,cAAc,GAAG;;MAExB,OAAO;MACP,UAAU"}
|
package/dist/table.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type PileCard, type PileSpec, type TableLayout, type TableOptions } from './layout.js';
|
|
2
|
+
import { type Ctl, type CtlMsg, type TableOut } from './controller.js';
|
|
3
|
+
export type TableProps = {
|
|
4
|
+
readonly layout: TableLayout;
|
|
5
|
+
readonly options: TableOptions;
|
|
6
|
+
};
|
|
7
|
+
export type TableState = {
|
|
8
|
+
readonly ctl: Ctl;
|
|
9
|
+
};
|
|
10
|
+
export type TableMsg = CtlMsg;
|
|
11
|
+
/** Card flight time when motion is on. */
|
|
12
|
+
export declare const FLIGHT_MS = 180;
|
|
13
|
+
type Shown = {
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly card: PileCard;
|
|
16
|
+
readonly depth: number;
|
|
17
|
+
};
|
|
18
|
+
/** The cards a pile draws: every card in a column; the top few of a stack or fan. */
|
|
19
|
+
export declare const shownCards: (pile: PileSpec) => Shown[];
|
|
20
|
+
/** How tightly a column overlaps, by length. */
|
|
21
|
+
export declare const density: (pile: PileSpec) => number;
|
|
22
|
+
export declare const CardTable: import("@gyral/core").GyralElementClass<TableState, CtlMsg, TableProps, TableOut, "Move" | "Key" | "Focus" | "Down" | "Up" | "Cancel">;
|
|
23
|
+
declare global {
|
|
24
|
+
interface HTMLElementTagNameMap {
|
|
25
|
+
'jy-card-table': InstanceType<typeof CardTable>;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export {};
|