@monimesl/dot-renderer-react 1.0.0-beta.1 → 1.0.0-beta.11

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 (58) hide show
  1. package/README.md +3 -3
  2. package/dist/components/amount-input.js +1 -1
  3. package/dist/components/avatar.js +1 -1
  4. package/dist/components/box.js +9 -3
  5. package/dist/components/button.js +64 -14
  6. package/dist/components/carousel.js +65 -50
  7. package/dist/components/chip.js +4 -5
  8. package/dist/components/code-input.js +1 -1
  9. package/dist/components/detail-row.js +5 -5
  10. package/dist/components/disclosure.js +11 -6
  11. package/dist/components/empty-state.js +7 -6
  12. package/dist/components/feedback.js +7 -7
  13. package/dist/components/field-box.js +1 -1
  14. package/dist/components/field.js +5 -4
  15. package/dist/components/footer.js +6 -11
  16. package/dist/components/grid.js +25 -15
  17. package/dist/components/hidden-view.js +5 -0
  18. package/dist/components/icon-button.js +1 -1
  19. package/dist/components/icon.js +32 -5
  20. package/dist/components/index.d.ts +2 -1
  21. package/dist/components/index.js +3 -1
  22. package/dist/components/input.js +1 -1
  23. package/dist/components/link.js +11 -3
  24. package/dist/components/list.js +62 -33
  25. package/dist/components/option.js +6 -5
  26. package/dist/components/padding.js +9 -10
  27. package/dist/components/pinned-bar.js +29 -0
  28. package/dist/components/pinned.d.ts +6 -0
  29. package/dist/components/pinned.js +26 -0
  30. package/dist/components/pressable.js +3 -1
  31. package/dist/components/prose.js +23 -20
  32. package/dist/components/rich-text.js +30 -18
  33. package/dist/components/scroll.js +33 -10
  34. package/dist/components/select.js +1 -1
  35. package/dist/components/sheet.js +55 -46
  36. package/dist/components/side-room.js +5 -0
  37. package/dist/components/skeleton.js +1 -1
  38. package/dist/components/slider.js +3 -3
  39. package/dist/components/spinner.js +1 -1
  40. package/dist/components/stack.js +12 -8
  41. package/dist/components/stepper.js +4 -5
  42. package/dist/components/steps.js +17 -14
  43. package/dist/components/tabs.js +74 -27
  44. package/dist/components/text.js +12 -14
  45. package/dist/components/toast.js +3 -3
  46. package/dist/components/toggles.js +14 -11
  47. package/dist/components/tokens.d.ts +16 -0
  48. package/dist/components/tokens.js +26 -1
  49. package/dist/components/view.js +40 -18
  50. package/dist/index.d.ts +2 -1
  51. package/dist/index.js +2 -1
  52. package/dist/page-root.js +9 -3
  53. package/dist/pull-to-refresh.js +1 -1
  54. package/dist/root.js +4 -3
  55. package/dist/styles.css +1 -1
  56. package/dist/theme.d.ts +12 -1
  57. package/dist/theme.js +85 -15
  58. package/package.json +4 -4
package/README.md CHANGED
@@ -20,9 +20,9 @@ source at build time, from `@phosphor-icons/core`.
20
20
 
21
21
  ## The theme
22
22
 
23
- A dot sets `scheme`, `colors` and `shape` in `defineDot`. The colors are `primary`, `background`,
24
- `surface`, `text`, `muted`, `border` and `subtle`, each for light and dark, and `custom` for the
25
- dot's own named colors. A page can set its own colors over the theme's, with `configs.colors` or
23
+ A dot sets its `theme` in `defineDot`, with `scheme`, `colors` and `shape`. The colors are
24
+ `primary`, `background`, `surface`, `text`, `muted`, `border` and `subtle`, each for light and dark,
25
+ and `custom` for the dot's own named colors. A page can set its own colors over the theme's, with `configs.colors` or
26
26
  `setColors`. The renderer fills in the rest of the palette from Dash's own look. On a background
27
27
  that isn't Dash's, it works out surfaces and quieter text that sit well on it. `useTheme()` gives a
28
28
  component the resolved theme.
@@ -1,5 +1,5 @@
1
- import { useTheme } from "../theme.js";
2
1
  import { SPACE, WEIGHTS } from "./tokens.js";
2
+ import { useTheme } from "../theme.js";
3
3
  import { Field } from "./field.js";
4
4
  import { BARE_INPUT_STYLE, useFieldBox, useFieldNote } from "./field-box.js";
5
5
  import { useTypedText } from "./typed-text.js";
@@ -1,5 +1,5 @@
1
- import { useTheme } from "../theme.js";
2
1
  import { WEIGHTS } from "./tokens.js";
2
+ import { useTheme } from "../theme.js";
3
3
  import { USER_ARTWORK } from "./icon-artwork.js";
4
4
  import { IconSvg } from "./icon-svg.js";
5
5
  import { useState } from "react";
@@ -1,5 +1,5 @@
1
1
  import { CARD_RADIUS_SCALE } from "./tokens.js";
2
- import { isTone, toneBackground } from "./tones.js";
2
+ import { isTone, toneBackground, toneColor } from "./tones.js";
3
3
  import { BACKGROUNDS } from "@monimesl/dot-core";
4
4
  //#region src/components/box.ts
5
5
  const RADIUS_SCALE = {
@@ -38,6 +38,7 @@ function boxStyle(props, theme, look = {}) {
38
38
  function backgroundColor(background, theme) {
39
39
  if (isTone(background)) return toneBackground(theme.colors, background);
40
40
  if (BACKGROUND_WORDS.has(background)) return theme.colors[background];
41
+ if (Object.hasOwn(theme.gradients, background)) return theme.gradients[background];
41
42
  return customColor(background, theme);
42
43
  }
43
44
  const unknownColors = /* @__PURE__ */ new Set();
@@ -45,12 +46,17 @@ function customColor(name, theme) {
45
46
  const color = theme.customColors[name];
46
47
  if (color === void 0 && !unknownColors.has(name)) {
47
48
  unknownColors.add(name);
48
- console.warn(`The theme has no colour named "${name}". Add it to colors.custom in dot.ts.`);
49
+ console.warn(`The theme has no colour named "${name}". Add it to theme.colors.custom in dot.ts.`);
49
50
  }
50
51
  return color;
51
52
  }
52
53
  function cssLength(value) {
53
54
  return value === "fill" ? "100%" : value;
54
55
  }
56
+ function textToneColor(tone, theme) {
57
+ if (isTone(tone)) return toneColor(theme.colors, tone);
58
+ if (tone === "onPrimary") return theme.colors.onPrimary;
59
+ return customColor(tone, theme);
60
+ }
55
61
  //#endregion
56
- export { boxStyle, customColor };
62
+ export { backgroundColor, boxStyle, customColor, textToneColor };
@@ -1,15 +1,31 @@
1
+ import { DISABLED_OPACITY, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { DISABLED_OPACITY, FONT_SIZE, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
3
3
  import { LoadingIcon } from "./spinner.js";
4
- import { createEvent } from "@monimesl/dot-core";
4
+ import { toneBackground } from "./tones.js";
5
+ import { DEFAULT_VARIANTS, createEvent, textOn } from "@monimesl/dot-core";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  //#region src/components/button.tsx
7
- const Button$1 = ({ variant = "primary", disabled, loading, onClick, children }) => {
8
- const { colors, radius } = useTheme();
9
- const filled = variant === "primary" && !disabled;
10
- let color = colors.text;
11
- if (filled) color = colors.onPrimary;
12
- else if (disabled) color = colors.subtle;
8
+ const SIZES = {
9
+ small: {
10
+ minHeight: 36,
11
+ padding: "8px 14px",
12
+ text: "caption"
13
+ },
14
+ medium: {
15
+ minHeight: 48,
16
+ padding: "12px 20px",
17
+ text: "body"
18
+ },
19
+ large: {
20
+ minHeight: 56,
21
+ padding: "16px 24px",
22
+ text: "lead"
23
+ }
24
+ };
25
+ const Button$1 = ({ variant = DEFAULT_VARIANTS.Button, tone = "default", size = "medium", disabled, loading, onClick, children }) => {
26
+ const { colors, radius, fontSize } = useTheme();
27
+ const look = buttonLook(colors, variant, tone, Boolean(disabled));
28
+ const sizing = SIZES[size] ?? SIZES.medium;
13
29
  return /* @__PURE__ */ jsxs("button", {
14
30
  type: "button",
15
31
  className: "dot-button",
@@ -23,13 +39,13 @@ const Button$1 = ({ variant = "primary", disabled, loading, onClick, children })
23
39
  alignItems: "center",
24
40
  justifyContent: "center",
25
41
  gap: SPACE.medium,
26
- minHeight: 48,
27
- padding: "12px 20px",
28
- border: "none",
42
+ minHeight: sizing.minHeight,
43
+ padding: sizing.padding,
44
+ border: look.border,
29
45
  borderRadius: radius,
30
- background: filled ? colors.primary : colors.muted,
31
- color,
32
- fontSize: FONT_SIZE.body,
46
+ background: look.background,
47
+ color: look.color,
48
+ fontSize: fontSize[sizing.text],
33
49
  fontWeight: WEIGHTS.bold,
34
50
  lineHeight: "normal",
35
51
  opacity: disabled ? DISABLED_OPACITY : 1,
@@ -38,5 +54,39 @@ const Button$1 = ({ variant = "primary", disabled, loading, onClick, children })
38
54
  children: [loading ? /* @__PURE__ */ jsx(LoadingIcon, { size: ICON_SIZE.small }) : null, children]
39
55
  });
40
56
  };
57
+ function buttonLook(colors, variant, tone, disabled) {
58
+ const toneColor = tone === "default" ? void 0 : colors[tone];
59
+ if (variant === "outline" || variant === "ghost") {
60
+ let color = toneColor ?? colors.primaryText;
61
+ if (disabled) color = colors.subtle;
62
+ let border = "none";
63
+ if (variant === "outline") border = `1px solid ${disabled ? colors.border : color}`;
64
+ return {
65
+ background: "transparent",
66
+ color,
67
+ border
68
+ };
69
+ }
70
+ if (disabled) return {
71
+ background: colors.muted,
72
+ color: colors.subtle,
73
+ border: "none"
74
+ };
75
+ if (variant === "secondary") return {
76
+ background: toneBackground(colors, tone),
77
+ color: toneColor ?? colors.text,
78
+ border: "none"
79
+ };
80
+ if (toneColor) return {
81
+ background: toneColor,
82
+ color: textOn(toneColor),
83
+ border: "none"
84
+ };
85
+ return {
86
+ background: colors.primary,
87
+ color: colors.onPrimary,
88
+ border: "none"
89
+ };
90
+ }
41
91
  //#endregion
42
92
  export { Button$1 as Button };
@@ -1,9 +1,10 @@
1
+ import { SHADOW_REACH, SPACE } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { SPACE } from "./tokens.js";
3
3
  import { clamp } from "./numbers.js";
4
4
  import { paddingStyle } from "./padding.js";
5
+ import { SideRoomContext } from "./side-room.js";
5
6
  import { createEvent } from "@monimesl/dot-core";
6
- import { Children, Fragment, isValidElement, useEffect, useEffectEvent, useRef, useState } from "react";
7
+ import { Children, Fragment, isValidElement, useContext, useEffect, useEffectEvent, useRef, useState } from "react";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  //#region src/components/carousel.tsx
9
10
  const SLIDE_DURATION = 350;
@@ -13,7 +14,8 @@ const SWIPE_SPEED = .4;
13
14
  const EDGE_RESISTANCE = .3;
14
15
  const TAP_SLOP = 6;
15
16
  const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator = "dots", gap = "none", effect = "slide", index, onChange, children, ...padding }) => {
16
- const { colors } = useTheme();
17
+ const { colors, space, elevation } = useTheme();
18
+ const mayUseSides = useContext(SideRoomContext);
17
19
  const slides = slidesOf(children);
18
20
  const count = slides.length;
19
21
  const looping = loop && count > 1;
@@ -23,20 +25,20 @@ const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator
23
25
  const [animated, setAnimated] = useState(true);
24
26
  const [dragOffset, setDragOffset] = useState(0);
25
27
  const [dragging, setDragging] = useState(false);
26
- const [viewportWidth, setViewportWidth] = useState(0);
28
+ const [slideWidth, setSlideWidth] = useState(0);
27
29
  const [previousIndex, setPreviousIndex] = useState(index);
28
- const viewportRef = useRef(null);
30
+ const swipeAreaRef = useRef(null);
29
31
  const gestureRef = useRef(null);
30
32
  const ignoreNextClick = useRef(false);
33
+ const shownPosition = clamp(position, 0, lastPosition);
34
+ const currentIndex = count === 0 ? 0 : wrapIndex(shownPosition - firstPosition, count);
31
35
  if (index !== previousIndex) {
32
36
  setPreviousIndex(index);
33
- if (index !== void 0) {
37
+ if (index !== void 0 && slideIndex(index, count) !== currentIndex) {
34
38
  setAnimated(true);
35
39
  setPosition(firstPosition + slideIndex(index, count));
36
40
  }
37
41
  }
38
- const shownPosition = clamp(position, 0, lastPosition);
39
- const currentIndex = count === 0 ? 0 : wrapIndex(shownPosition - firstPosition, count);
40
42
  const moveTo = (targetPosition) => {
41
43
  const nextPosition = clamp(targetPosition, 0, lastPosition);
42
44
  setAnimated(true);
@@ -98,7 +100,7 @@ const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator
98
100
  };
99
101
  ignoreNextClick.current = false;
100
102
  setDragging(true);
101
- setViewportWidth(viewportRef.current?.clientWidth ?? 0);
103
+ setSlideWidth(swipeAreaRef.current?.clientWidth ?? 0);
102
104
  };
103
105
  const onPointerMove = (event) => {
104
106
  const swipe = gestureOf(event);
@@ -128,13 +130,14 @@ const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator
128
130
  if (!swipe.swiping) return;
129
131
  const movedX = event.clientX - swipe.startX;
130
132
  const speed = Math.abs(movedX) / Math.max(1, event.timeStamp - swipe.startTime);
131
- if (Math.abs(movedX) > viewportWidth * SWIPE_DISTANCE || speed > SWIPE_SPEED) stepBy(movedX < 0 ? 1 : -1);
133
+ if (Math.abs(movedX) > slideWidth * SWIPE_DISTANCE || speed > SWIPE_SPEED) stepBy(movedX < 0 ? 1 : -1);
132
134
  else moveTo(shownPosition);
133
135
  };
134
- const gapWidth = SPACE[gap];
136
+ const gapWidth = mayUseSides && elevation === "raised" ? Math.max(space[gap], SHADOW_REACH.sides) : space[gap];
137
+ const sideRoom = mayUseSides ? gapWidth : 0;
135
138
  const transition = animated ? `transform ${SLIDE_DURATION}ms ${SLIDE_EASING}` : "none";
136
139
  let draggedSlides = 0;
137
- if (viewportWidth > 0) draggedSlides = dragOffset / (viewportWidth + gapWidth);
140
+ if (slideWidth > 0) draggedSlides = dragOffset / (slideWidth + gapWidth);
138
141
  let track = slides;
139
142
  if (looping) track = [
140
143
  {
@@ -156,52 +159,64 @@ const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator
156
159
  flexDirection: "column",
157
160
  gap: SPACE.medium,
158
161
  minWidth: 0,
159
- ...paddingStyle(padding)
162
+ ...paddingStyle(padding, space)
160
163
  },
161
164
  children: [/* @__PURE__ */ jsx("div", {
162
- ref: viewportRef,
163
- onPointerDown,
164
- onPointerMove,
165
- onPointerUp,
166
- onPointerCancel: endGesture,
167
- onDragStart: (event) => event.preventDefault(),
168
- onClickCapture: (event) => {
169
- if (ignoreNextClick.current) {
170
- ignoreNextClick.current = false;
171
- event.stopPropagation();
172
- }
173
- },
174
165
  style: {
175
166
  overflow: "hidden",
176
- touchAction: "pan-y",
177
- userSelect: dragging ? "none" : void 0
167
+ paddingTop: SHADOW_REACH.top,
168
+ paddingBottom: SHADOW_REACH.bottom,
169
+ paddingInline: sideRoom,
170
+ marginTop: -SHADOW_REACH.top,
171
+ marginBottom: -SHADOW_REACH.bottom,
172
+ marginInline: -sideRoom,
173
+ pointerEvents: "none"
178
174
  },
179
175
  children: /* @__PURE__ */ jsx("div", {
176
+ ref: swipeAreaRef,
177
+ onPointerDown,
178
+ onPointerMove,
179
+ onPointerUp,
180
+ onPointerCancel: endGesture,
181
+ onDragStart: (event) => event.preventDefault(),
182
+ onClickCapture: (event) => {
183
+ if (ignoreNextClick.current) {
184
+ ignoreNextClick.current = false;
185
+ event.stopPropagation();
186
+ }
187
+ },
180
188
  style: {
181
- display: "flex",
182
- gap: gapWidth,
183
- transform: `translateX(calc(${-shownPosition} * (100% + ${gapWidth}px) + ${dragOffset}px))`,
184
- transition
189
+ pointerEvents: "auto",
190
+ touchAction: "pan-y",
191
+ userSelect: dragging ? "none" : void 0
185
192
  },
186
- children: track.map((slide, trackPosition) => {
187
- const visible = trackPosition === shownPosition;
188
- const distance = Math.abs(trackPosition - shownPosition + draggedSlides);
189
- const scale = 1 - Math.min(distance, 1) * .15000000000000002;
190
- return /* @__PURE__ */ jsx("div", {
191
- role: "group",
192
- "aria-roledescription": "slide",
193
- "aria-label": `${wrapIndex(trackPosition - firstPosition, count) + 1} of ${count}`,
194
- "aria-hidden": !visible,
195
- inert: !visible,
196
- style: {
197
- display: "grid",
198
- flex: "0 0 100%",
199
- minWidth: 0,
200
- transform: effect === "scale" ? `scale(${scale})` : void 0,
201
- transition: effect === "scale" ? transition : void 0
202
- },
203
- children: slide.node
204
- }, slide.key);
193
+ children: /* @__PURE__ */ jsx("div", {
194
+ style: {
195
+ display: "flex",
196
+ gap: gapWidth,
197
+ transform: `translateX(calc(${-shownPosition} * (100% + ${gapWidth}px) + ${dragOffset}px))`,
198
+ transition
199
+ },
200
+ children: track.map((slide, trackPosition) => {
201
+ const visible = trackPosition === shownPosition;
202
+ const distance = Math.abs(trackPosition - shownPosition + draggedSlides);
203
+ const scale = 1 - Math.min(distance, 1) * .15000000000000002;
204
+ return /* @__PURE__ */ jsx("div", {
205
+ role: "group",
206
+ "aria-roledescription": "slide",
207
+ "aria-label": `${wrapIndex(trackPosition - firstPosition, count) + 1} of ${count}`,
208
+ "aria-hidden": !visible,
209
+ inert: !visible,
210
+ style: {
211
+ display: "grid",
212
+ flex: "0 0 100%",
213
+ minWidth: 0,
214
+ transform: effect === "scale" ? `scale(${scale})` : void 0,
215
+ transition: effect === "scale" ? transition : void 0
216
+ },
217
+ children: slide.node
218
+ }, slide.key);
219
+ })
205
220
  })
206
221
  })
207
222
  }), indicator === "dots" && count > 1 ? /* @__PURE__ */ jsx("div", {
@@ -1,11 +1,11 @@
1
+ import { ICON_SIZE, WEIGHTS, pressLook } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { DISABLED_OPACITY, FONT_SIZE, ICON_SIZE, WEIGHTS } from "./tokens.js";
3
3
  import { IconGraphic } from "./icon.js";
4
4
  import { createEvent } from "@monimesl/dot-core";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/chip.tsx
7
7
  const Chip$1 = ({ label, icon, selected = false, disabled, onTap }) => {
8
- const { colors } = useTheme();
8
+ const { colors, fontSize } = useTheme();
9
9
  return /* @__PURE__ */ jsxs("button", {
10
10
  type: "button",
11
11
  className: "dot-chip",
@@ -23,12 +23,11 @@ const Chip$1 = ({ label, icon, selected = false, disabled, onTap }) => {
23
23
  border: `1px solid ${selected ? colors.primary : colors.border}`,
24
24
  background: selected ? colors.primary : colors.surface,
25
25
  color: selected ? colors.onPrimary : colors.text,
26
- fontSize: FONT_SIZE.caption,
26
+ fontSize: fontSize.caption,
27
27
  fontWeight: WEIGHTS.medium,
28
28
  lineHeight: "normal",
29
29
  whiteSpace: "nowrap",
30
- opacity: disabled ? DISABLED_OPACITY : 1,
31
- cursor: disabled ? "default" : "pointer"
30
+ ...pressLook(disabled)
32
31
  },
33
32
  children: [icon ? /* @__PURE__ */ jsx(IconGraphic, {
34
33
  name: icon,
@@ -1,5 +1,5 @@
1
- import { useTheme } from "../theme.js";
2
1
  import { CENTERED, DISABLED_OPACITY, SPACE, WEIGHTS } from "./tokens.js";
2
+ import { useTheme } from "../theme.js";
3
3
  import { Field } from "./field.js";
4
4
  import { fieldBorderColor, useFieldNote } from "./field-box.js";
5
5
  import { useTypedText } from "./typed-text.js";
@@ -1,11 +1,11 @@
1
+ import { WEIGHTS } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { FONT_SIZE, WEIGHTS } from "./tokens.js";
3
- import { insets, paddingStyle } from "./padding.js";
4
3
  import { toneColor } from "./tones.js";
4
+ import { insets, paddingStyle } from "./padding.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/detail-row.tsx
7
7
  const DetailRow = ({ label, value, tone, weight = "regular", ...padding }) => {
8
- const { colors } = useTheme();
8
+ const { colors, space, fontSize } = useTheme();
9
9
  return /* @__PURE__ */ jsxs("div", {
10
10
  className: "dot-detail-row",
11
11
  style: {
@@ -13,8 +13,8 @@ const DetailRow = ({ label, value, tone, weight = "regular", ...padding }) => {
13
13
  alignItems: "baseline",
14
14
  justifyContent: "space-between",
15
15
  gap: 16,
16
- ...paddingStyle(padding, insets(6, 0)),
17
- fontSize: FONT_SIZE.body,
16
+ ...paddingStyle(padding, space, insets(6, 0)),
17
+ fontSize: fontSize.body,
18
18
  lineHeight: "normal"
19
19
  },
20
20
  children: [/* @__PURE__ */ jsx("span", {
@@ -1,13 +1,13 @@
1
+ import { ICON_SIZE, SPACE, WEIGHTS, pressLook } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { FONT_SIZE, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
3
3
  import { CHEVRON_DOWN_ARTWORK } from "./icon-artwork.js";
4
4
  import { IconSvg } from "./icon-svg.js";
5
5
  import { createEvent } from "@monimesl/dot-core";
6
6
  import { useState } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/components/disclosure.tsx
9
- const Disclosure$1 = ({ title, subtitle, open: startsOpen = false, children, onToggle }) => {
10
- const { colors } = useTheme();
9
+ const Disclosure$1 = ({ title, subtitle, open: startsOpen = false, disabled, children, onToggle }) => {
10
+ const { colors, fontSize } = useTheme();
11
11
  const [open, setOpen] = useState(startsOpen);
12
12
  return /* @__PURE__ */ jsxs("details", {
13
13
  className: "dot-disclosure",
@@ -21,12 +21,17 @@ const Disclosure$1 = ({ title, subtitle, open: startsOpen = false, children, onT
21
21
  },
22
22
  style: { color: colors.text },
23
23
  children: [/* @__PURE__ */ jsxs("summary", {
24
+ "aria-disabled": disabled || void 0,
25
+ tabIndex: disabled ? -1 : void 0,
26
+ onClick: (event) => {
27
+ if (disabled) event.preventDefault();
28
+ },
24
29
  style: {
25
30
  display: "flex",
26
31
  alignItems: "center",
27
32
  gap: SPACE.medium,
28
33
  padding: "12px 0",
29
- cursor: "pointer"
34
+ ...pressLook(disabled)
30
35
  },
31
36
  children: [/* @__PURE__ */ jsxs("span", {
32
37
  style: {
@@ -38,13 +43,13 @@ const Disclosure$1 = ({ title, subtitle, open: startsOpen = false, children, onT
38
43
  },
39
44
  children: [/* @__PURE__ */ jsx("span", {
40
45
  style: {
41
- fontSize: FONT_SIZE.body,
46
+ fontSize: fontSize.body,
42
47
  fontWeight: WEIGHTS.medium
43
48
  },
44
49
  children: title
45
50
  }), subtitle ? /* @__PURE__ */ jsx("span", {
46
51
  style: {
47
- fontSize: FONT_SIZE.caption,
52
+ fontSize: fontSize.caption,
48
53
  color: colors.subtle
49
54
  },
50
55
  children: subtitle
@@ -1,11 +1,11 @@
1
+ import { CENTERED, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { CENTERED, FONT_SIZE, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
3
3
  import { insets, paddingStyle } from "./padding.js";
4
4
  import { IconGraphic } from "./icon.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/empty-state.tsx
7
7
  const EmptyState = ({ icon, title, text, children, ...padding }) => {
8
- const { colors } = useTheme();
8
+ const { colors, space, fontSize, headingFontFamily, headingWeight } = useTheme();
9
9
  return /* @__PURE__ */ jsxs("div", {
10
10
  className: "dot-empty-state",
11
11
  style: {
@@ -13,7 +13,7 @@ const EmptyState = ({ icon, title, text, children, ...padding }) => {
13
13
  flexDirection: "column",
14
14
  alignItems: "center",
15
15
  gap: SPACE.medium,
16
- ...paddingStyle(padding, insets(32, 16)),
16
+ ...paddingStyle(padding, space, insets(32, 16)),
17
17
  textAlign: "center",
18
18
  color: colors.text
19
19
  },
@@ -34,8 +34,9 @@ const EmptyState = ({ icon, title, text, children, ...padding }) => {
34
34
  }) : null,
35
35
  title ? /* @__PURE__ */ jsx("span", {
36
36
  style: {
37
- fontSize: 17,
38
- fontWeight: WEIGHTS.bold,
37
+ fontSize: fontSize.subtitle,
38
+ fontFamily: headingFontFamily,
39
+ fontWeight: WEIGHTS[headingWeight ?? "bold"],
39
40
  lineHeight: "normal"
40
41
  },
41
42
  children: title
@@ -43,7 +44,7 @@ const EmptyState = ({ icon, title, text, children, ...padding }) => {
43
44
  text ? /* @__PURE__ */ jsx("span", {
44
45
  style: {
45
46
  maxWidth: 300,
46
- fontSize: FONT_SIZE.body,
47
+ fontSize: fontSize.body,
47
48
  lineHeight: "normal",
48
49
  color: colors.subtle
49
50
  },
@@ -1,10 +1,10 @@
1
+ import { ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { FONT_SIZE, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
3
3
  import { CLOSE_ARTWORK } from "./icon-artwork.js";
4
4
  import { IconSvg } from "./icon-svg.js";
5
+ import { STATUS_ARTWORK, toneBackground, toneColor } from "./tones.js";
5
6
  import { clamp } from "./numbers.js";
6
7
  import { insets, paddingStyle } from "./padding.js";
7
- import { STATUS_ARTWORK, toneBackground, toneColor } from "./tones.js";
8
8
  import { IconButton } from "./icon-button.js";
9
9
  import { createEvent } from "@monimesl/dot-core";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -34,18 +34,18 @@ const ProgressBar$1 = ({ value = 0 }) => {
34
34
  });
35
35
  };
36
36
  const Notice$1 = ({ tone = "default", title, children, onClose, ...padding }) => {
37
- const { colors, radius } = useTheme();
37
+ const { colors, radius, space, fontSize } = useTheme();
38
38
  return /* @__PURE__ */ jsxs("div", {
39
39
  className: "dot-notice",
40
40
  role: tone === "danger" ? "alert" : "status",
41
41
  style: {
42
42
  display: "flex",
43
43
  gap: SPACE.medium,
44
- ...paddingStyle(padding, insets(12, 14)),
44
+ ...paddingStyle(padding, space, insets(12, 14)),
45
45
  borderRadius: radius,
46
46
  background: toneBackground(colors, tone),
47
47
  color: colors.text,
48
- fontSize: FONT_SIZE.body,
48
+ fontSize: fontSize.body,
49
49
  lineHeight: "normal"
50
50
  },
51
51
  children: [
@@ -79,7 +79,7 @@ const Notice$1 = ({ tone = "default", title, children, onClose, ...padding }) =>
79
79
  });
80
80
  };
81
81
  const Badge$1 = ({ tone = "default", children }) => {
82
- const { colors } = useTheme();
82
+ const { colors, fontSize } = useTheme();
83
83
  return /* @__PURE__ */ jsx("span", {
84
84
  className: "dot-badge",
85
85
  style: {
@@ -89,7 +89,7 @@ const Badge$1 = ({ tone = "default", children }) => {
89
89
  borderRadius: 999,
90
90
  background: toneBackground(colors, tone),
91
91
  color: toneColor(colors, tone),
92
- fontSize: FONT_SIZE.caption,
92
+ fontSize: fontSize.caption,
93
93
  fontWeight: WEIGHTS.bold,
94
94
  lineHeight: "normal",
95
95
  whiteSpace: "nowrap"
@@ -1,5 +1,5 @@
1
- import { useTheme } from "../theme.js";
2
1
  import { DISABLED_OPACITY } from "./tokens.js";
2
+ import { useTheme } from "../theme.js";
3
3
  import { useId, useState } from "react";
4
4
  //#region src/components/field-box.ts
5
5
  const BARE_INPUT_STYLE = {
@@ -1,9 +1,9 @@
1
+ import { SPACE, WEIGHTS } from "./tokens.js";
1
2
  import { useTheme } from "../theme.js";
2
- import { FONT_SIZE, SPACE, WEIGHTS } from "./tokens.js";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  //#region src/components/field.tsx
5
5
  function Field({ label, hint, error, labelId, noteId, children }) {
6
- const { colors } = useTheme();
6
+ const { colors, fontSize } = useTheme();
7
7
  const note = error || hint;
8
8
  return /* @__PURE__ */ jsxs("label", {
9
9
  className: "dot-field",
@@ -21,7 +21,7 @@ function Field({ label, hint, error, labelId, noteId, children }) {
21
21
  note ? /* @__PURE__ */ jsx("span", {
22
22
  id: noteId,
23
23
  style: {
24
- fontSize: FONT_SIZE.caption,
24
+ fontSize: fontSize.caption,
25
25
  color: error ? colors.danger : colors.subtle
26
26
  },
27
27
  children: note
@@ -30,10 +30,11 @@ function Field({ label, hint, error, labelId, noteId, children }) {
30
30
  });
31
31
  }
32
32
  function FieldLabel({ id, children }) {
33
+ const { fontSize } = useTheme();
33
34
  return /* @__PURE__ */ jsx("span", {
34
35
  id,
35
36
  style: {
36
- fontSize: FONT_SIZE.caption,
37
+ fontSize: fontSize.caption,
37
38
  fontWeight: WEIGHTS.medium
38
39
  },
39
40
  children
@@ -1,21 +1,16 @@
1
- import { useTheme } from "../theme.js";
2
1
  import { SPACE } from "./tokens.js";
3
- import { HOME_INDICATOR, insets, pinnedPaddingStyle } from "./padding.js";
2
+ import { useTheme } from "../theme.js";
3
+ import { insets } from "./padding.js";
4
+ import { pinnedBarStyle } from "./pinned-bar.js";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
  //#region src/components/footer.tsx
6
7
  const Footer = ({ children, ...padding }) => {
7
- const { colors } = useTheme();
8
+ const { colors, space } = useTheme();
8
9
  return /* @__PURE__ */ jsx("footer", {
9
- className: "dot-footer",
10
+ className: "dot-footer dot-bar-bottom",
10
11
  style: {
11
- position: "sticky",
12
- bottom: 0,
13
- marginTop: "auto",
14
- display: "flex",
15
- flexDirection: "column",
12
+ ...pinnedBarStyle("bottom", padding, space, insets(SPACE.medium, 0)),
16
13
  gap: SPACE.medium,
17
- ...pinnedPaddingStyle(padding, insets(SPACE.medium, 0)),
18
- marginBottom: `calc(-1 * ${HOME_INDICATOR})`,
19
14
  background: colors.background
20
15
  },
21
16
  children