@trypattern/sdk 0.1.12 → 0.1.14
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/README.md +73 -12
- package/dist/access.d.ts +0 -4
- package/dist/access.js +0 -5
- package/dist/api.d.ts +3 -1
- package/dist/api.js +5 -23
- package/dist/app.d.ts +20 -5
- package/dist/app.js +32 -2
- package/dist/blocks.d.ts +18 -0
- package/dist/blocks.js +36 -0
- package/dist/bookings.d.ts +400 -0
- package/dist/bookings.js +300 -0
- package/dist/bookingsMemory.d.ts +9 -0
- package/dist/bookingsMemory.js +79 -0
- package/dist/bookingsStore.d.ts +698 -0
- package/dist/bookingsStore.js +129 -0
- package/dist/cli/app.js +144 -59
- package/dist/cli/publishing.d.ts +3 -1
- package/dist/cli/publishing.js +45 -10
- package/dist/cli/starter.d.ts +17 -0
- package/dist/cli/starter.js +140 -0
- package/dist/components/Calendar.d.ts +20 -0
- package/dist/components/Calendar.js +195 -0
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +7 -29
- package/dist/components/Choice.js +27 -13
- package/dist/components/ChoiceGroup.d.ts +3 -1
- package/dist/components/ChoiceGroup.js +3 -4
- package/dist/components/Dialog.d.ts +1 -1
- package/dist/components/Dialog.js +3 -1
- package/dist/components/ListRow.js +2 -1
- package/dist/components/RadioDescribed.d.ts +4 -0
- package/dist/components/RadioDescribed.js +20 -0
- package/dist/components/RadioGroup.d.ts +4 -1
- package/dist/components/RadioGroup.js +28 -3
- package/dist/components/Text.d.ts +1 -1
- package/dist/components/Text.js +43 -2
- package/dist/components/calendarDates.d.ts +14 -0
- package/dist/components/calendarDates.js +84 -0
- package/dist/components/controlLook.js +11 -11
- package/dist/components/icons.d.ts +2 -0
- package/dist/components/icons.js +2 -0
- package/dist/components/interaction.d.ts +1 -1
- package/dist/flows/browser.d.ts +7 -1
- package/dist/flows/browser.js +25 -7
- package/dist/flows/flowBrowser.d.ts +2 -2
- package/dist/flows/flowBrowser.js +2 -2
- package/dist/flows/flows.js +8 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/media.d.ts +11 -1
- package/dist/patternAccess.d.ts +11 -2
- package/dist/patternAccess.js +8 -4
- package/dist/publishWire.d.ts +35 -2
- package/dist/publishWire.js +26 -3
- package/dist/react.d.ts +2 -0
- package/dist/react.js +1 -0
- package/dist/rules/checkAllFiles.js +16 -2
- package/dist/rules/lintPlugin.js +1 -0
- package/dist/schema.d.ts +3 -1
- package/dist/schema.js +1 -0
- package/dist/server/bookings.d.ts +12 -0
- package/dist/server/bookings.js +142 -0
- package/dist/server/createServer.d.ts +3 -2
- package/dist/server/createServer.js +13 -4
- package/dist/server/env.d.ts +8 -0
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.js +1 -0
- package/dist/server/media.d.ts +16 -3
- package/dist/server/media.js +72 -23
- package/dist/server/patternViewer.js +16 -10
- package/dist/server/tables.d.ts +2 -0
- package/dist/server/tables.js +17 -1
- package/dist/start.js +12 -0
- package/dist/table.js +9 -3
- package/dist/theme/scales.d.ts +1 -1
- package/dist/theme/scales.js +1 -0
- package/dist/theme/tokens.stylex.d.ts +18 -0
- package/dist/theme/tokens.stylex.js +19 -1
- package/dist/vite/localWorker.d.ts +2 -0
- package/dist/vite/localWorker.js +48 -3
- package/dist/vite/projectFile.d.ts +5 -0
- package/dist/vite/projectFile.js +19 -1
- package/dist/vite/vite.js +5 -1
- 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;
|
package/dist/components/Card.js
CHANGED
|
@@ -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,
|
|
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 &&
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
86
|
+
fontSize: fontSizes.bodySm,
|
|
73
87
|
fontWeight: fontWeights.normal,
|
|
74
|
-
lineHeight: lineHeights.
|
|
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
|
-
|
|
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,
|
|
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({
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as stylex from '@stylexjs/stylex';
|
|
3
|
+
import { space } from "../theme/tokens.stylex.js";
|
|
4
|
+
import { Text } from "./Text.js";
|
|
5
|
+
export function RadioDescribed({ option }) {
|
|
6
|
+
const tone = option.isDisabled === true ? 'tertiary' : undefined;
|
|
7
|
+
return (_jsxs("span", { ...stylex.props(styles.described), children: [_jsxs("span", { ...stylex.props(styles.labelLine), children: [_jsx(Text, { variant: "label", tone: tone ?? 'primary', children: option.label }), option.badge] }), _jsx(Text, { variant: "body", tone: tone ?? 'secondary', children: option.description })] }));
|
|
8
|
+
}
|
|
9
|
+
const styles = stylex.create({
|
|
10
|
+
described: {
|
|
11
|
+
display: 'flex',
|
|
12
|
+
flexDirection: 'column',
|
|
13
|
+
gap: space.xxs
|
|
14
|
+
},
|
|
15
|
+
labelLine: {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
gap: space.sm,
|
|
18
|
+
alignItems: 'center'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -3,6 +3,8 @@ import type { Placement } from './Placement.ts';
|
|
|
3
3
|
export type RadioOption<Value extends string> = {
|
|
4
4
|
value: Value;
|
|
5
5
|
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
badge?: ReactNode;
|
|
6
8
|
content?: ReactNode;
|
|
7
9
|
trailing?: ReactNode;
|
|
8
10
|
isDisabled?: boolean;
|
|
@@ -12,7 +14,8 @@ type RadioGroupProps<Value extends string> = {
|
|
|
12
14
|
value: Value | null;
|
|
13
15
|
options: readonly RadioOption<Value>[];
|
|
14
16
|
onChange: (value: Value) => void;
|
|
17
|
+
size?: 'md' | 'lg';
|
|
15
18
|
style?: Placement;
|
|
16
19
|
};
|
|
17
|
-
export declare function RadioGroup<Value extends string>({ label, value, options, onChange, style }: RadioGroupProps<Value>): import("react").JSX.Element;
|
|
20
|
+
export declare function RadioGroup<Value extends string>({ label, value, options, onChange, size, style }: RadioGroupProps<Value>): import("react").JSX.Element;
|
|
18
21
|
export {};
|