@trypattern/sdk 0.1.12 → 0.1.13

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.
Files changed (80) hide show
  1. package/README.md +73 -12
  2. package/dist/access.d.ts +0 -4
  3. package/dist/access.js +0 -5
  4. package/dist/api.d.ts +3 -1
  5. package/dist/api.js +5 -23
  6. package/dist/app.d.ts +20 -5
  7. package/dist/app.js +32 -2
  8. package/dist/blocks.d.ts +18 -0
  9. package/dist/blocks.js +36 -0
  10. package/dist/bookings.d.ts +400 -0
  11. package/dist/bookings.js +300 -0
  12. package/dist/bookingsMemory.d.ts +9 -0
  13. package/dist/bookingsMemory.js +79 -0
  14. package/dist/bookingsStore.d.ts +698 -0
  15. package/dist/bookingsStore.js +129 -0
  16. package/dist/cli/app.js +143 -59
  17. package/dist/cli/publishing.d.ts +3 -1
  18. package/dist/cli/publishing.js +33 -5
  19. package/dist/cli/starter.d.ts +17 -0
  20. package/dist/cli/starter.js +140 -0
  21. package/dist/components/Calendar.d.ts +20 -0
  22. package/dist/components/Calendar.js +195 -0
  23. package/dist/components/Card.d.ts +1 -1
  24. package/dist/components/Card.js +7 -29
  25. package/dist/components/Choice.js +27 -13
  26. package/dist/components/ChoiceGroup.d.ts +3 -1
  27. package/dist/components/ChoiceGroup.js +3 -4
  28. package/dist/components/Dialog.d.ts +1 -1
  29. package/dist/components/Dialog.js +3 -1
  30. package/dist/components/ListRow.js +2 -1
  31. package/dist/components/RadioGroup.d.ts +2 -1
  32. package/dist/components/RadioGroup.js +10 -2
  33. package/dist/components/Text.d.ts +1 -1
  34. package/dist/components/Text.js +43 -2
  35. package/dist/components/calendarDates.d.ts +14 -0
  36. package/dist/components/calendarDates.js +84 -0
  37. package/dist/components/controlLook.js +11 -11
  38. package/dist/components/icons.d.ts +2 -0
  39. package/dist/components/icons.js +2 -0
  40. package/dist/components/interaction.d.ts +1 -1
  41. package/dist/flows/browser.d.ts +7 -1
  42. package/dist/flows/browser.js +25 -7
  43. package/dist/flows/flowBrowser.d.ts +2 -2
  44. package/dist/flows/flowBrowser.js +2 -2
  45. package/dist/flows/flows.js +8 -2
  46. package/dist/index.d.ts +4 -1
  47. package/dist/index.js +1 -0
  48. package/dist/media.d.ts +11 -1
  49. package/dist/patternAccess.d.ts +11 -2
  50. package/dist/patternAccess.js +8 -4
  51. package/dist/publishWire.d.ts +24 -2
  52. package/dist/publishWire.js +22 -2
  53. package/dist/react.d.ts +2 -0
  54. package/dist/react.js +1 -0
  55. package/dist/rules/checkAllFiles.js +16 -2
  56. package/dist/rules/lintPlugin.js +1 -0
  57. package/dist/schema.d.ts +3 -1
  58. package/dist/schema.js +1 -0
  59. package/dist/server/bookings.d.ts +12 -0
  60. package/dist/server/bookings.js +142 -0
  61. package/dist/server/createServer.d.ts +3 -2
  62. package/dist/server/createServer.js +13 -4
  63. package/dist/server/env.d.ts +8 -0
  64. package/dist/server/index.d.ts +3 -1
  65. package/dist/server/index.js +1 -0
  66. package/dist/server/media.d.ts +16 -3
  67. package/dist/server/media.js +72 -23
  68. package/dist/server/patternViewer.js +16 -10
  69. package/dist/server/tables.d.ts +2 -0
  70. package/dist/server/tables.js +17 -1
  71. package/dist/start.js +12 -0
  72. package/dist/table.js +9 -3
  73. package/dist/theme/scales.d.ts +1 -1
  74. package/dist/theme/scales.js +1 -0
  75. package/dist/theme/tokens.stylex.d.ts +18 -0
  76. package/dist/theme/tokens.stylex.js +19 -1
  77. package/dist/vite/localWorker.d.ts +2 -0
  78. package/dist/vite/localWorker.js +48 -3
  79. package/dist/vite/vite.js +5 -1
  80. package/package.json +1 -1
@@ -0,0 +1,140 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ import { pathToFileURL } from 'node:url';
12
+ import { media } from "../media.js";
13
+ import { photoFacts } from "../server/media.js";
14
+ import { starterSql } from "../server/tables.js";
15
+ export const STARTER_ROWS = 'starter/rows.ts';
16
+ export const STARTER_PHOTOS = 'starter/photos';
17
+ const PHOTO_NAME = /^[\w][\w.-]*$/;
18
+ function record(value) {
19
+ return typeof value === 'object' && value !== null
20
+ ? value
21
+ : {};
22
+ }
23
+ function textOf(row, key) {
24
+ const value = row[key];
25
+ return typeof value === 'string' ? value.trim() : '';
26
+ }
27
+ async function readPhoto(root, raw) {
28
+ const id = textOf(raw, 'id');
29
+ const photo = textOf(raw, 'photo');
30
+ const alt = textOf(raw, 'alt');
31
+ const where = `${STARTER_ROWS}: the media row ${id === '' ? 'without an id' : id}`;
32
+ if (id === '')
33
+ throw new Error(`${where} needs an id`);
34
+ if (!PHOTO_NAME.test(photo)) {
35
+ throw new Error(`${where} names its photo by its file name in ${STARTER_PHOTOS}, like photo: 'peonies.jpg'`);
36
+ }
37
+ if (alt === '') {
38
+ throw new Error(`${where} needs alt: describe the photo for someone who can't see it`);
39
+ }
40
+ const path = `${STARTER_PHOTOS}/${photo}`;
41
+ if (!existsSync(join(root, path))) {
42
+ throw new Error(`${where} names ${path}, which isn't there`);
43
+ }
44
+ const bytes = readFileSync(join(root, path));
45
+ const facts = await photoFacts(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
46
+ if ('problem' in facts)
47
+ throw new Error(`${path}: ${facts.problem}`);
48
+ return { ...facts, id, alt, path };
49
+ }
50
+ function readRows(name, table, rows) {
51
+ if (table.owned) {
52
+ throw new Error(`${STARTER_ROWS} can't fill ${name}: its rows belong to whoever makes them, and nobody has signed in when a site first goes live`);
53
+ }
54
+ return rows.map((raw) => {
55
+ const { id, ...fields } = record(raw);
56
+ if (typeof id !== 'string' || id === '') {
57
+ throw new Error(`${STARTER_ROWS}: every ${name} row needs an id`);
58
+ }
59
+ const parsed = table.fields.safeParse(fields);
60
+ if (!parsed.success) {
61
+ const issue = parsed.error.issues[0];
62
+ throw new Error(`${STARTER_ROWS}: the ${name} row ${id} ${issue === undefined ? 'is not valid' : `has ${issue.path.join('.') || 'a field'} wrong: ${issue.message}`}`);
63
+ }
64
+ return { id, ...parsed.data };
65
+ });
66
+ }
67
+ function refuseRepeatedIds(name, rows) {
68
+ const seen = new Set();
69
+ for (const row of rows) {
70
+ if (seen.has(row['id'])) {
71
+ throw new Error(`${name} has two rows with the id ${String(row['id'])}`);
72
+ }
73
+ seen.add(row['id']);
74
+ }
75
+ }
76
+ export async function readStarter(root, tables) {
77
+ const file = join(root, STARTER_ROWS);
78
+ if (!existsSync(file))
79
+ return undefined;
80
+ const module = await import(__rewriteRelativeImportExtension(pathToFileURL(file).href));
81
+ const starter = record(module)['starter'];
82
+ if (typeof starter !== 'object' || starter === null) {
83
+ throw new Error(`${STARTER_ROWS} must export \`starter\``);
84
+ }
85
+ const photos = [];
86
+ const rows = {};
87
+ for (const [name, list] of Object.entries(starter)) {
88
+ const table = tables[name];
89
+ if (table === undefined) {
90
+ throw new Error(`${STARTER_ROWS} has rows for ${name}, which isn't in tables in schema.ts`);
91
+ }
92
+ if (!Array.isArray(list)) {
93
+ throw new Error(`${STARTER_ROWS}: ${name} must be a list of rows`);
94
+ }
95
+ if (table.name === media.name) {
96
+ for (const raw of list)
97
+ photos.push(await readPhoto(root, record(raw)));
98
+ refuseRepeatedIds(`${STARTER_ROWS} media`, photos);
99
+ continue;
100
+ }
101
+ rows[name] = readRows(name, table, list);
102
+ refuseRepeatedIds(`${STARTER_ROWS} ${name}`, rows[name]);
103
+ }
104
+ return { photos, rows: rows };
105
+ }
106
+ export function withStarter(seed, starter) {
107
+ if (starter === undefined)
108
+ return seed;
109
+ const photoRows = starter.photos.map((photo) => ({
110
+ id: photo.id,
111
+ key: photo.key,
112
+ width: photo.width,
113
+ height: photo.height,
114
+ placeholder: null,
115
+ alt: photo.alt,
116
+ type: photo.type,
117
+ bytes: photo.bytes
118
+ }));
119
+ const added = {
120
+ ...starter.rows,
121
+ ...(photoRows.length === 0 ? {} : { media: photoRows })
122
+ };
123
+ const merged = { ...seed };
124
+ for (const [name, list] of Object.entries(added)) {
125
+ const starterRows = (list ?? []);
126
+ const starterIds = new Set(starterRows.map((row) => row['id']));
127
+ const seedRows = (merged[name] ?? []);
128
+ merged[name] = [
129
+ ...seedRows.filter((row) => !starterIds.has(row['id'])),
130
+ ...starterRows
131
+ ];
132
+ }
133
+ return merged;
134
+ }
135
+ export function starterContentOf(tables, starter) {
136
+ return {
137
+ photos: starter.photos.map(({ id, path, alt }) => ({ id, path, alt })),
138
+ sql: starterSql(tables, starter.rows)
139
+ };
140
+ }
@@ -0,0 +1,20 @@
1
+ import type { Placement } from './Placement.ts';
2
+ export type CalendarDay = {
3
+ date: string;
4
+ state: 'open' | 'closed' | 'full';
5
+ };
6
+ type CalendarProps = {
7
+ label: string;
8
+ month: string;
9
+ onMonthChange: (month: string) => void;
10
+ days: readonly CalendarDay[];
11
+ value: string | null;
12
+ onPick: (date: string) => void;
13
+ firstMonth?: string;
14
+ lastMonth?: string;
15
+ today?: string;
16
+ locale?: string;
17
+ style?: Placement;
18
+ };
19
+ export declare function Calendar({ label, month, onMonthChange, days, value, onPick, firstMonth, lastMonth, today, locale, style }: CalendarProps): import("react").JSX.Element;
20
+ export {};
@@ -0,0 +1,195 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button as BaseButton } from '@base-ui/react/button';
3
+ import * as stylex from '@stylexjs/stylex';
4
+ import { useEffect, useRef, useState } from 'react';
5
+ import { borders, colors, controls, fontSizes, fontWeights, lineHeights, motion, radii, space } from "../theme/tokens.stylex.js";
6
+ import { addDays, addMonths, dayLabel, dayNumber, localToday, monthOf, monthTitle, weekdayIndex, weekdayNames, weeksOf } from "./calendarDates.js";
7
+ import { focusRing } from "./controlLook.js";
8
+ import { Icon } from "./Icon.js";
9
+ import { interact } from "./interaction.js";
10
+ export function Calendar({ label, month, onMonthChange, days, value, onPick, firstMonth, lastMonth, today = localToday(), locale, style }) {
11
+ const states = new Map(days.map((day) => [day.date, day.state]));
12
+ const [moved, setMoved] = useState(null);
13
+ const grid = useRef(null);
14
+ const shouldFocus = useRef(false);
15
+ const focused = moved !== null && monthOf(moved) === month
16
+ ? moved
17
+ : startingFocus(month, value, today, states);
18
+ const previous = addMonths(`${month}-01`, -1).slice(0, 7);
19
+ const next = addMonths(`${month}-01`, 1).slice(0, 7);
20
+ const canGoBack = firstMonth === undefined || previous >= firstMonth;
21
+ const canGoOn = lastMonth === undefined || next <= lastMonth;
22
+ useEffect(() => {
23
+ if (!shouldFocus.current)
24
+ return;
25
+ shouldFocus.current = false;
26
+ grid.current
27
+ ?.querySelector(`[data-date="${focused}"]`)
28
+ ?.focus();
29
+ });
30
+ const goTo = (target) => {
31
+ interact({
32
+ primitive: 'Calendar',
33
+ event: 'change',
34
+ label: `${label}: ${monthTitle(target, locale)}`
35
+ });
36
+ onMonthChange(target);
37
+ };
38
+ const pick = (date) => {
39
+ if ((states.get(date) ?? 'closed') !== 'open')
40
+ return;
41
+ interact({ primitive: 'Calendar', event: 'select', label });
42
+ onPick(date);
43
+ };
44
+ const move = (event, date) => {
45
+ const step = MOVES[event.key];
46
+ if (step === undefined)
47
+ return;
48
+ event.preventDefault();
49
+ const target = step(date);
50
+ const targetMonth = monthOf(target);
51
+ if (firstMonth !== undefined && targetMonth < firstMonth)
52
+ return;
53
+ if (lastMonth !== undefined && targetMonth > lastMonth)
54
+ return;
55
+ setMoved(target);
56
+ shouldFocus.current = true;
57
+ if (targetMonth !== month)
58
+ goTo(targetMonth);
59
+ };
60
+ return (_jsxs("div", { ...stylex.props(styles.calendar, style), children: [_jsxs("div", { ...stylex.props(styles.header), children: [_jsx("span", { ...stylex.props(styles.title), "aria-live": "polite", children: monthTitle(month, locale) }), _jsxs("span", { ...stylex.props(styles.nav), children: [_jsx(BaseButton, { "aria-label": monthTitle(previous, locale), disabled: !canGoBack, onClick: () => goTo(previous), className: stylex.props(styles.navButton, canGoBack ? styles.navEnabled : styles.navDisabled, focusRing.visible).className, children: _jsx(Icon, { name: "previous" }) }), _jsx(BaseButton, { "aria-label": monthTitle(next, locale), disabled: !canGoOn, onClick: () => goTo(next), className: stylex.props(styles.navButton, canGoOn ? styles.navEnabled : styles.navDisabled, focusRing.visible).className, children: _jsx(Icon, { name: "next" }) })] })] }), _jsxs("div", { ref: grid, role: "grid", "aria-label": `${label}, ${monthTitle(month, locale)}`, ...stylex.props(styles.grid), children: [_jsx("div", { role: "row", ...stylex.props(styles.row), children: weekdayNames(locale).map((weekday) => (_jsx("span", { role: "columnheader", "aria-label": weekday.long, ...stylex.props(styles.weekday), children: weekday.short }, weekday.long))) }), weeksOf(month).map((week, index) => (_jsx("div", { role: "row", ...stylex.props(styles.row), children: week.map((date, column) => {
61
+ if (date === null) {
62
+ return (_jsx("span", { role: "gridcell", ...stylex.props(styles.cell) }, `blank-${column}`));
63
+ }
64
+ const state = states.get(date) ?? 'closed';
65
+ const isOpen = state === 'open';
66
+ const isPicked = date === value;
67
+ return (_jsx("span", { role: "gridcell", "aria-selected": isPicked, ...stylex.props(styles.cell), children: _jsx("button", { type: "button", "data-date": date, tabIndex: date === focused ? 0 : -1, "aria-label": dayLabel(date, locale), "aria-disabled": !isOpen, "aria-current": date === today ? 'date' : undefined, onClick: () => pick(date), onKeyDown: (event) => move(event, date), ...stylex.props(styles.day, isOpen && styles.open, !isOpen && styles.unavailable, state === 'full' && styles.full, date === today && !isPicked && styles.today, isPicked && styles.picked, focusRing.visible), children: dayNumber(date, locale) }) }, date));
68
+ }) }, week.find((date) => date !== null) ?? index)))] })] }));
69
+ }
70
+ const MOVES = {
71
+ ArrowLeft: (date) => addDays(date, -1),
72
+ ArrowRight: (date) => addDays(date, 1),
73
+ ArrowUp: (date) => addDays(date, -7),
74
+ ArrowDown: (date) => addDays(date, 7),
75
+ Home: (date) => addDays(date, -weekdayIndex(date)),
76
+ End: (date) => addDays(date, 6 - weekdayIndex(date)),
77
+ PageUp: (date) => addMonths(date, -1),
78
+ PageDown: (date) => addMonths(date, 1)
79
+ };
80
+ function startingFocus(month, value, today, states) {
81
+ if (value !== null && monthOf(value) === month)
82
+ return value;
83
+ if (monthOf(today) === month)
84
+ return today;
85
+ const open = [...states].find(([date, state]) => monthOf(date) === month && state === 'open');
86
+ return open?.[0] ?? `${month}-01`;
87
+ }
88
+ const styles = stylex.create({
89
+ calendar: {
90
+ display: 'flex',
91
+ flexDirection: 'column',
92
+ gap: space.xxs,
93
+ width: `calc(7 * (${space.xxxl} - ${space.xs}))`
94
+ },
95
+ header: {
96
+ display: 'flex',
97
+ alignItems: 'center',
98
+ justifyContent: 'space-between',
99
+ paddingInline: space.xs,
100
+ paddingBottom: space.sm
101
+ },
102
+ title: {
103
+ fontSize: fontSizes.bodyBase,
104
+ fontWeight: fontWeights.medium,
105
+ lineHeight: lineHeights.body,
106
+ color: colors.primaryForeground
107
+ },
108
+ nav: {
109
+ display: 'flex',
110
+ gap: space.xs
111
+ },
112
+ navButton: {
113
+ display: 'flex',
114
+ flexShrink: 0,
115
+ alignItems: 'center',
116
+ justifyContent: 'center',
117
+ width: controls.heightMd,
118
+ height: controls.heightMd,
119
+ outlineOffset: 0,
120
+ borderRadius: radii.full,
121
+ transitionTimingFunction: motion.easeOut,
122
+ transitionDuration: motion.fast,
123
+ transitionProperty: 'color, background-color'
124
+ },
125
+ navEnabled: {
126
+ color: colors.primaryForeground,
127
+ backgroundColor: {
128
+ default: 'transparent',
129
+ ':hover': colors.transparentHover
130
+ }
131
+ },
132
+ navDisabled: {
133
+ color: colors.disabledForeground
134
+ },
135
+ grid: {
136
+ display: 'flex',
137
+ flexDirection: 'column',
138
+ gap: space.xxs
139
+ },
140
+ row: {
141
+ display: 'grid',
142
+ gridTemplateColumns: `repeat(7, calc(${space.xxxl} - ${space.xs}))`
143
+ },
144
+ weekday: {
145
+ display: 'flex',
146
+ alignItems: 'center',
147
+ justifyContent: 'center',
148
+ height: controls.heightMd,
149
+ fontSize: fontSizes.captionLg,
150
+ lineHeight: lineHeights.bodySm,
151
+ color: colors.secondaryForeground,
152
+ textAlign: 'center'
153
+ },
154
+ cell: {
155
+ display: 'flex',
156
+ width: `calc(${space.xxxl} - ${space.xs})`,
157
+ height: `calc(${space.xxxl} - ${space.xs})`
158
+ },
159
+ day: {
160
+ display: 'flex',
161
+ alignItems: 'center',
162
+ justifyContent: 'center',
163
+ width: '100%',
164
+ height: '100%',
165
+ fontSize: fontSizes.bodyMd,
166
+ lineHeight: lineHeights.bodySm,
167
+ outlineOffset: 0,
168
+ borderRadius: radii.full,
169
+ transitionTimingFunction: motion.easeOut,
170
+ transitionDuration: motion.fast,
171
+ transitionProperty: 'color, background-color'
172
+ },
173
+ open: {
174
+ color: colors.primaryForeground,
175
+ backgroundColor: {
176
+ default: 'transparent',
177
+ ':hover': colors.transparentHover
178
+ }
179
+ },
180
+ unavailable: {
181
+ color: colors.disabledForeground
182
+ },
183
+ full: {
184
+ textDecorationLine: 'line-through',
185
+ textDecorationThickness: borders.thin
186
+ },
187
+ today: {
188
+ boxShadow: `inset 0 0 0 ${borders.thin} ${colors.borderStrong}`
189
+ },
190
+ picked: {
191
+ fontWeight: fontWeights.medium,
192
+ color: colors.onInvertedForeground,
193
+ backgroundColor: colors.invertedSolid
194
+ }
195
+ });
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
2
2
  import type { AppPath } from '../registered.ts';
3
3
  import type { Placement } from './Placement.ts';
4
4
  type CardPadding = 'none' | 'sm' | 'md' | 'lg';
5
- type CardSurface = 'subtle' | 'strong' | 'container';
5
+ type CardSurface = 'subtle' | 'strong' | 'container' | 'none';
6
6
  type CardProps = {
7
7
  children: ReactNode;
8
8
  surface?: CardSurface;
@@ -2,12 +2,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Button as BaseButton } from '@base-ui/react/button';
3
3
  import * as stylex from '@stylexjs/stylex';
4
4
  import { Link as RouterLink } from 'react-router';
5
- import { colors, motion, radii, shadows, space } from "../theme/tokens.stylex.js";
5
+ import { colors, radii, shadows, space } from "../theme/tokens.stylex.js";
6
6
  import { focusRing } from "./controlLook.js";
7
7
  import { interact, textOf } from "./interaction.js";
8
8
  export function Card({ children, surface = 'subtle', padding = 'lg', paddingBlock, to, href, onClick, style }) {
9
9
  const isInteractive = to !== undefined || href !== undefined || onClick !== undefined;
10
- const className = stylex.props(styles.card, surfaces[surface], paddings[padding], paddingBlock !== undefined && paddingBlocks[paddingBlock], isInteractive && styles.interactive, isInteractive && hovers[surface], isInteractive && focusRing.visible, style).className;
10
+ const className = stylex.props(styles.card, surfaces[surface], paddings[padding], paddingBlock !== undefined && paddingBlocks[paddingBlock], isInteractive && styles.interactive, isInteractive && focusRing.visible, style).className;
11
11
  const report = (event) => {
12
12
  interact({
13
13
  primitive: 'Card',
@@ -39,14 +39,7 @@ const styles = stylex.create({
39
39
  color: 'inherit',
40
40
  textAlign: 'start',
41
41
  textDecoration: 'none',
42
- outlineOffset: 0,
43
- boxShadow: {
44
- default: shadows.default,
45
- ':hover': shadows.large
46
- },
47
- transitionTimingFunction: motion.easeOut,
48
- transitionDuration: motion.fast,
49
- transitionProperty: 'background-color, box-shadow'
42
+ outlineOffset: 0
50
43
  }
51
44
  });
52
45
  const surfaces = stylex.create({
@@ -58,26 +51,11 @@ const surfaces = stylex.create({
58
51
  },
59
52
  container: {
60
53
  backgroundColor: colors.container
61
- }
62
- });
63
- const hovers = stylex.create({
64
- subtle: {
65
- backgroundColor: {
66
- default: colors.componentOnContainerSubtle,
67
- ':hover': colors.componentOnContainerSubtleHover
68
- }
69
54
  },
70
- strong: {
71
- backgroundColor: {
72
- default: colors.componentOnContainerStrong,
73
- ':hover': colors.componentOnContainerStrongHover
74
- }
75
- },
76
- container: {
77
- backgroundColor: {
78
- default: colors.container,
79
- ':hover': colors.componentOnContainerStrongHover
80
- }
55
+ none: {
56
+ overflow: 'visible',
57
+ borderRadius: 0,
58
+ boxShadow: 'none'
81
59
  }
82
60
  });
83
61
  const paddings = stylex.create({
@@ -8,36 +8,37 @@ export function Choice({ value, title, label, subline, isDisabled = false, child
8
8
  const chosen = useChosen();
9
9
  const isFaded = chosen !== null && chosen !== value;
10
10
  const hasWords = title !== undefined || subline !== undefined;
11
- return (_jsxs(BaseRadio.Root, { value: value, disabled: isDisabled, "aria-label": label, className: (state) => stylex.props(styles.choice, children !== undefined && !hasWords && styles.media, !state.disabled && styles.enabled, state.checked && styles.checked, isFaded && !state.checked && !state.disabled && styles.faded, state.disabled && styles.disabled, focusRing.visible).className, children: [children === undefined ? null : (_jsx("span", { ...stylex.props(styles.content), children: children })), title === undefined ? null : (_jsx("span", { ...stylex.props(styles.title), children: title })), subline === undefined ? null : (_jsx("span", { ...stylex.props(styles.subline, isDisabled && styles.sublineDisabled), children: subline }))] }));
11
+ const isPhoto = children !== undefined && !hasWords;
12
+ return (_jsxs(BaseRadio.Root, { value: value, disabled: isDisabled, "aria-label": label, className: (state) => stylex.props(styles.choice, !state.disabled && styles.enabled, state.checked && styles.checked, isPhoto &&
13
+ isFaded &&
14
+ !state.checked &&
15
+ !state.disabled &&
16
+ styles.faded, state.disabled && styles.disabled, isPhoto && styles.photo, focusRing.visible).className, children: [children === undefined ? null : (_jsx("span", { ...stylex.props(styles.content, isPhoto && styles.photoContent), children: children })), title === undefined ? null : (_jsx("span", { ...stylex.props(styles.title), children: title })), subline === undefined ? null : (_jsx("span", { ...stylex.props(styles.subline, isDisabled && styles.sublineDisabled), children: subline }))] }));
12
17
  }
13
18
  const checkedRing = `inset 0 0 0 ${borders.thin} ${colors.borderInverted}, 0 0 0 ${borders.thin} ${colors.borderInverted}`;
14
19
  const styles = stylex.create({
15
20
  choice: {
16
21
  display: 'flex',
22
+ flexGrow: 1,
23
+ flexBasis: `calc(2 * ${space.xxxl})`,
17
24
  flexDirection: 'column',
18
- gap: space.xxs,
19
25
  alignItems: 'flex-start',
20
26
  minWidth: 0,
21
- paddingBlock: space.sm,
27
+ paddingBlock: `calc(${space.sm} + ${space.xxs})`,
22
28
  paddingInline: space.md,
23
- fontSize: fontSizes.bodySm,
29
+ fontSize: fontSizes.bodyMd,
24
30
  fontWeight: fontWeights.medium,
25
31
  lineHeight: lineHeights.bodySm,
26
32
  color: colors.primaryForeground,
27
33
  textAlign: 'start',
28
34
  outlineOffset: 0,
29
35
  backgroundColor: colors.componentOnContainerStrong,
30
- borderRadius: radii.lg,
36
+ borderRadius: radii.xl,
31
37
  boxShadow: shadows.default,
32
38
  transitionTimingFunction: motion.easeOut,
33
39
  transitionDuration: motion.fast,
34
40
  transitionProperty: 'background-color, box-shadow, opacity'
35
41
  },
36
- media: {
37
- flexShrink: 0,
38
- width: `calc(2 * ${space.xxxl})`,
39
- padding: space.xs
40
- },
41
42
  enabled: {
42
43
  backgroundColor: {
43
44
  default: colors.componentOnContainerStrong,
@@ -49,10 +50,20 @@ const styles = stylex.create({
49
50
  },
50
51
  faded: {
51
52
  opacity: {
52
- default: 0.56,
53
+ default: 0.5,
53
54
  ':hover': 1
54
55
  }
55
56
  },
57
+ photo: {
58
+ flexGrow: 1,
59
+ flexBasis: 0,
60
+ paddingBlock: 0,
61
+ paddingInline: 0,
62
+ overflow: 'hidden',
63
+ backgroundColor: 'transparent',
64
+ borderRadius: radii.lg,
65
+ boxShadow: 'none'
66
+ },
56
67
  disabled: {
57
68
  color: colors.disabledForeground,
58
69
  backgroundColor: colors.disabled,
@@ -65,13 +76,16 @@ const styles = stylex.create({
65
76
  overflow: 'hidden',
66
77
  borderRadius: radii.md
67
78
  },
79
+ photoContent: {
80
+ borderRadius: radii.lg
81
+ },
68
82
  title: {
69
83
  minWidth: 0
70
84
  },
71
85
  subline: {
72
- fontSize: fontSizes.captionLg,
86
+ fontSize: fontSizes.bodySm,
73
87
  fontWeight: fontWeights.normal,
74
- lineHeight: lineHeights.caption,
88
+ lineHeight: lineHeights.bodySm,
75
89
  color: colors.secondaryForeground
76
90
  },
77
91
  sublineDisabled: {
@@ -1,11 +1,13 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { Placement } from './Placement.ts';
3
+ import type { SpaceName } from './spacing.ts';
3
4
  type ChoiceGroupProps<Value extends string> = {
4
5
  label: string;
5
6
  value: Value | null;
6
7
  onChange: (value: Value) => void;
7
8
  children: ReactNode;
9
+ gap?: SpaceName;
8
10
  style?: Placement;
9
11
  };
10
- export declare function ChoiceGroup<Value extends string>({ label, value, onChange, children, style }: ChoiceGroupProps<Value>): import("react").JSX.Element;
12
+ export declare function ChoiceGroup<Value extends string>({ label, value, onChange, children, gap, style }: ChoiceGroupProps<Value>): import("react").JSX.Element;
11
13
  export {};
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
3
3
  import * as stylex from '@stylexjs/stylex';
4
4
  import { Children, isValidElement } from 'react';
5
- import { space } from "../theme/tokens.stylex.js";
6
5
  import { ChoiceContext } from "./choiceContext.js";
7
6
  import { interact, textOf } from "./interaction.js";
8
- export function ChoiceGroup({ label, value, onChange, children, style }) {
7
+ import { gaps } from "./spacing.js";
8
+ export function ChoiceGroup({ label, value, onChange, children, gap = 'sm', style }) {
9
9
  return (_jsx(ChoiceContext, { value: { value }, children: _jsx(BaseRadioGroup, { "aria-label": label, value: value, onValueChange: (next) => {
10
10
  if (typeof next !== 'string')
11
11
  return;
@@ -15,7 +15,7 @@ export function ChoiceGroup({ label, value, onChange, children, style }) {
15
15
  label: labelOf(children, next)
16
16
  });
17
17
  onChange(next);
18
- }, className: stylex.props(styles.group, style).className, children: children }) }));
18
+ }, className: stylex.props(styles.group, gaps[gap], style).className, children: children }) }));
19
19
  }
20
20
  function labelOf(children, value) {
21
21
  for (const child of Children.toArray(children)) {
@@ -37,7 +37,6 @@ const styles = stylex.create({
37
37
  group: {
38
38
  display: 'flex',
39
39
  flexWrap: 'wrap',
40
- gap: space.sm,
41
40
  alignItems: 'stretch',
42
41
  minWidth: 0
43
42
  }
@@ -4,7 +4,7 @@ type DialogProps = {
4
4
  isOpen: boolean;
5
5
  onOpenChange: (isOpen: boolean) => void;
6
6
  placement?: 'center' | 'top';
7
- size?: 'sm' | 'md' | 'lg';
7
+ size?: 'sm' | 'md' | 'lg' | 'xl';
8
8
  children: ReactNode;
9
9
  };
10
10
  export declare function Dialog({ label, isOpen, onOpenChange, placement, size, children }: DialogProps): import("react").JSX.Element;
@@ -45,6 +45,7 @@ const styles = stylex.create({
45
45
  frame: {
46
46
  display: 'flex',
47
47
  flexDirection: 'column',
48
+ maxHeight: 'calc(100dvh - 2rem)',
48
49
  overflow: 'hidden',
49
50
  color: colors.primaryForeground,
50
51
  backgroundColor: colors.componentOnSunken,
@@ -77,5 +78,6 @@ const hiddenPlacements = stylex.create({
77
78
  const widths = stylex.create({
78
79
  sm: { maxWidth: 'min(400px, calc(100% - 2rem))' },
79
80
  md: { maxWidth: 'min(560px, calc(100% - 2rem))' },
80
- lg: { maxWidth: 'min(720px, calc(100% - 2rem))' }
81
+ lg: { maxWidth: 'min(720px, calc(100% - 2rem))' },
82
+ xl: { maxWidth: 'min(800px, calc(100% - 2rem))' }
81
83
  });
@@ -7,7 +7,8 @@ import { focusRing } from "./controlLook.js";
7
7
  import { interact, textOf } from "./interaction.js";
8
8
  export function ListRow({ to, href, onClick, isMuted = false, isCurrent = false, size = 'md', leading, trailing, children }) {
9
9
  const content = (_jsxs(_Fragment, { children: [leading, _jsx("span", { ...stylex.props(styles.main), children: children }), trailing === undefined ? null : (_jsx("span", { ...stylex.props(styles.trailing), children: trailing }))] }));
10
- const look = stylex.props(styles.row, (to !== undefined || href !== undefined || onClick !== undefined) &&
10
+ const look = stylex.props(styles.row, size === 'sm' &&
11
+ (to !== undefined || href !== undefined || onClick !== undefined) &&
11
12
  styles.interactive, isMuted && styles.muted, isCurrent && styles.current, size === 'sm' && styles.small, size === 'lg' && styles.large, focusRing.visible, focusRing.inside);
12
13
  const report = () => {
13
14
  interact({
@@ -12,7 +12,8 @@ type RadioGroupProps<Value extends string> = {
12
12
  value: Value | null;
13
13
  options: readonly RadioOption<Value>[];
14
14
  onChange: (value: Value) => void;
15
+ size?: 'md' | 'lg';
15
16
  style?: Placement;
16
17
  };
17
- export declare function RadioGroup<Value extends string>({ label, value, options, onChange, style }: RadioGroupProps<Value>): import("react").JSX.Element;
18
+ export declare function RadioGroup<Value extends string>({ label, value, options, onChange, size, style }: RadioGroupProps<Value>): import("react").JSX.Element;
18
19
  export {};
@@ -6,7 +6,7 @@ import { borders, colors, iconSizes, motion, radii, shadows, space } from "../th
6
6
  import { focusRing } from "./controlLook.js";
7
7
  import { interact } from "./interaction.js";
8
8
  import { Text } from "./Text.js";
9
- export function RadioGroup({ label, value, options, onChange, style }) {
9
+ export function RadioGroup({ label, value, options, onChange, size = 'md', style }) {
10
10
  return (_jsx(BaseRadioGroup, { "aria-label": label, value: value, onValueChange: (next) => {
11
11
  const chosen = options.find((option) => option.value === next);
12
12
  if (chosen === undefined)
@@ -17,7 +17,8 @@ export function RadioGroup({ label, value, options, onChange, style }) {
17
17
  label: chosen.label
18
18
  });
19
19
  onChange(chosen.value);
20
- }, className: stylex.props(styles.group, style).className, children: options.map((option) => (_jsxs("label", { ...stylex.props(styles.row, option.isDisabled === true && styles.rowDisabled), children: [_jsx(BaseRadio.Root, { value: option.value, disabled: option.isDisabled === true, "aria-label": option.label, className: (state) => stylex.props(styles.dot, state.checked ? styles.dotOn : styles.dotOff, focusRing.visible).className, children: _jsx(BaseRadio.Indicator, { className: stylex.props(styles.mark).className }) }), _jsx("span", { ...stylex.props(styles.content), children: option.content ?? (_jsx(Text, { tone: option.isDisabled === true ? 'tertiary' : 'primary', isTruncated: true, children: option.label })) }), option.trailing === undefined ? null : (_jsx("span", { ...stylex.props(styles.trailing), children: option.trailing }))] }, option.value))) }));
20
+ }, className: stylex.props(styles.group, size === 'lg' && styles.groupLarge, style)
21
+ .className, children: options.map((option) => (_jsxs("label", { ...stylex.props(styles.row, size === 'lg' && styles.rowLarge, option.isDisabled === true && styles.rowDisabled), children: [_jsx(BaseRadio.Root, { value: option.value, disabled: option.isDisabled === true, "aria-label": option.label, className: (state) => stylex.props(styles.dot, state.checked ? styles.dotOn : styles.dotOff, focusRing.visible).className, children: _jsx(BaseRadio.Indicator, { className: stylex.props(styles.mark).className }) }), _jsx("span", { ...stylex.props(styles.content), children: option.content ?? (_jsx(Text, { variant: size === 'lg' ? 'lead' : 'body', tone: option.isDisabled === true ? 'tertiary' : 'primary', isTruncated: true, children: option.label })) }), option.trailing === undefined ? null : (_jsx("span", { ...stylex.props(styles.trailing), children: option.trailing }))] }, option.value))) }));
21
22
  }
22
23
  const styles = stylex.create({
23
24
  group: {
@@ -42,6 +43,13 @@ const styles = stylex.create({
42
43
  },
43
44
  borderTopWidth: borders.hairline
44
45
  },
46
+ groupLarge: {
47
+ borderRadius: radii.xxl
48
+ },
49
+ rowLarge: {
50
+ paddingBlock: space.lg,
51
+ paddingInline: `calc(${space.md} + ${space.xxs})`
52
+ },
45
53
  rowDisabled: {
46
54
  color: colors.tertiaryForeground
47
55
  },
@@ -3,7 +3,7 @@ import type { Placement } from './Placement.ts';
3
3
  import type { Tone } from './tones.ts';
4
4
  type TextProps = {
5
5
  children: ReactNode;
6
- variant?: 'heading' | 'subheading' | 'title' | 'lead' | 'label' | 'body' | 'caption' | 'code';
6
+ variant?: 'hero' | 'display' | 'section' | 'heading' | 'subheading' | 'title' | 'lead' | 'intro' | 'label' | 'body' | 'caption' | 'code';
7
7
  tone?: Tone;
8
8
  align?: 'start' | 'center' | 'end';
9
9
  as?: 'span' | 'p' | 'h1' | 'h2' | 'h3';