@monimesl/dot-renderer-react 1.0.0-beta.7 → 1.0.0-beta.9

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 (55) hide show
  1. package/dist/components/amount-input.js +1 -1
  2. package/dist/components/avatar.js +1 -1
  3. package/dist/components/box.js +8 -2
  4. package/dist/components/button.js +64 -14
  5. package/dist/components/carousel.js +65 -50
  6. package/dist/components/chip.js +3 -3
  7. package/dist/components/code-input.js +1 -1
  8. package/dist/components/detail-row.js +5 -5
  9. package/dist/components/disclosure.js +4 -4
  10. package/dist/components/empty-state.js +7 -6
  11. package/dist/components/feedback.js +7 -7
  12. package/dist/components/field-box.js +1 -1
  13. package/dist/components/field.js +5 -4
  14. package/dist/components/footer.js +6 -11
  15. package/dist/components/grid.js +20 -13
  16. package/dist/components/icon-button.js +1 -1
  17. package/dist/components/icon.js +32 -5
  18. package/dist/components/index.d.ts +2 -1
  19. package/dist/components/index.js +3 -1
  20. package/dist/components/input.js +1 -1
  21. package/dist/components/link.js +1 -1
  22. package/dist/components/list.js +11 -9
  23. package/dist/components/option.js +3 -3
  24. package/dist/components/padding.js +9 -10
  25. package/dist/components/pinned-bar.js +29 -0
  26. package/dist/components/pinned.d.ts +6 -0
  27. package/dist/components/pinned.js +26 -0
  28. package/dist/components/prose.js +23 -20
  29. package/dist/components/rich-text.js +30 -18
  30. package/dist/components/scroll.js +25 -8
  31. package/dist/components/select.js +1 -1
  32. package/dist/components/sheet.js +51 -45
  33. package/dist/components/side-room.js +5 -0
  34. package/dist/components/skeleton.js +1 -1
  35. package/dist/components/slider.js +3 -3
  36. package/dist/components/spinner.js +1 -1
  37. package/dist/components/stack.js +12 -8
  38. package/dist/components/stepper.js +3 -3
  39. package/dist/components/steps.js +17 -14
  40. package/dist/components/tabs.js +72 -26
  41. package/dist/components/text.js +12 -14
  42. package/dist/components/toast.js +3 -3
  43. package/dist/components/toggles.js +13 -9
  44. package/dist/components/tokens.d.ts +16 -0
  45. package/dist/components/tokens.js +20 -1
  46. package/dist/components/view.js +29 -15
  47. package/dist/index.d.ts +2 -1
  48. package/dist/index.js +2 -1
  49. package/dist/page-root.js +9 -3
  50. package/dist/pull-to-refresh.js +1 -1
  51. package/dist/root.js +4 -3
  52. package/dist/styles.css +1 -1
  53. package/dist/theme.d.ts +12 -1
  54. package/dist/theme.js +85 -15
  55. package/package.json +4 -4
package/dist/page-root.js CHANGED
@@ -1,18 +1,24 @@
1
- import { toList, toText } from "@monimesl/dot-core";
1
+ import { useTheme } from "./theme.js";
2
+ import { toList, toText, withComponentDefaults } from "@monimesl/dot-core";
2
3
  import { Fragment, createElement, useMemo, useSyncExternalStore } from "react";
3
4
  //#region src/page-root.tsx
4
5
  function PageRoot({ page, components }) {
5
6
  const state = useSyncExternalStore(page.subscribe, page.getState);
7
+ const themeComponents = useTheme().components;
6
8
  const context = useMemo(() => ({
7
9
  h(type, props, ...children) {
8
10
  const component = components[type];
9
11
  if (!component) throw new Error(`The renderer has no component for <${type}>. Check the tag's name, or pass a component for it to the renderer.`);
10
- return createElement(component, props, ...children);
12
+ return createElement(component, withComponentDefaults(type, props, themeComponents), ...children);
11
13
  },
12
14
  text: toText,
13
15
  on: (handler, data) => page.on(handler, data),
14
16
  each: (items, render, key) => createElement(Fragment, null, toList(items).map((item, index) => createElement(Fragment, { key: key ? String(key(item, index)) : index }, ...render(item, index))))
15
- }), [page, components]);
17
+ }), [
18
+ page,
19
+ components,
20
+ themeComponents
21
+ ]);
16
22
  if (!state) return null;
17
23
  return page.viewRenderer(state, context);
18
24
  }
@@ -1,5 +1,5 @@
1
- import { useTheme } from "./theme.js";
2
1
  import { CENTERED } from "./components/tokens.js";
2
+ import { useTheme } from "./theme.js";
3
3
  import { LoadingIcon } from "./components/spinner.js";
4
4
  import { useEffect, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
package/dist/root.js CHANGED
@@ -1,4 +1,4 @@
1
- import { FONT_FAMILY, ThemeContext, resolveTheme, useTheme } from "./theme.js";
1
+ import { ThemeContext, resolveTheme, useTheme } from "./theme.js";
2
2
  import { HOME_INDICATOR } from "./components/padding.js";
3
3
  import { PortalContainerContext } from "./portal.js";
4
4
  import { PageRoot } from "./page-root.js";
@@ -27,6 +27,7 @@ function Root({ page, dotContext, components, wrapper: Wrapper }) {
27
27
  ref: setRootElement,
28
28
  className: "dot-theme-root",
29
29
  "data-dot-scheme": resolvedTheme.scheme,
30
+ style: { overflow: "clip" },
30
31
  children: /* @__PURE__ */ jsx(PortalContainerContext.Provider, {
31
32
  value: rootElement,
32
33
  children: /* @__PURE__ */ jsx(PullToRefresh, {
@@ -51,7 +52,7 @@ function withPageColors(theme, scheme, pageColors) {
51
52
  };
52
53
  }
53
54
  function DefaultSurface({ children }) {
54
- const { colors } = useTheme();
55
+ const { colors, fontFamily } = useTheme();
55
56
  return /* @__PURE__ */ jsx("div", {
56
57
  className: "dot-surface",
57
58
  style: {
@@ -62,7 +63,7 @@ function DefaultSurface({ children }) {
62
63
  flexDirection: "column",
63
64
  background: colors.background,
64
65
  color: colors.text,
65
- fontFamily: FONT_FAMILY
66
+ fontFamily
66
67
  },
67
68
  children
68
69
  });
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- @layer base{.dot-theme-root,.dot-theme-root *,.dot-theme-root :before,.dot-theme-root :after{box-sizing:border-box}.dot-theme-root :is(button,input,select,textarea){font:inherit}[data-dot-scheme=light]{color-scheme:light}[data-dot-scheme=dark]{color-scheme:dark}.dot-theme-root{-webkit-user-select:none;user-select:none}.dot-theme-root :is(input,textarea){-webkit-user-select:text;user-select:text}}@keyframes dot-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}.dot-image{max-width:100%;display:block}.dot-list>.dot-list-item:not(:last-child){border-bottom:1px solid var(--dot-list-divider)}.dot-scroll{overscroll-behavior-x:contain;scrollbar-width:none;flex-direction:row;display:flex;position:relative;overflow:auto hidden}.dot-scroll::-webkit-scrollbar{display:none}.dot-scroll>*{scroll-snap-align:var(--dot-scroll-snap);flex:none;width:min-content}.dot-scroll .dot-image{max-width:none}.dot-disclosure>summary{list-style:none}.dot-disclosure>summary::-webkit-details-marker{display:none}.dot-steps>.dot-step:last-child .dot-step-line{display:none}.dot-sheet-backdrop{transition:opacity .2s}.dot-sheet{transition:transform .2s ease-out}.dot-sheet-backdrop:is([data-starting-style],[data-ending-style]){opacity:0}.dot-sheet:is([data-starting-style],[data-ending-style]){transform:translateY(100%)}.dot-prose>.dot-heading{margin-block-start:.5em}.dot-prose>.dot-heading:first-child{margin-block-start:0}
1
+ @layer base{.dot-theme-root,.dot-theme-root *,.dot-theme-root :before,.dot-theme-root :after{box-sizing:border-box}.dot-theme-root :is(button,input,select,textarea){font:inherit}[data-dot-scheme=light]{color-scheme:light}[data-dot-scheme=dark]{color-scheme:dark}.dot-theme-root{-webkit-user-select:none;user-select:none}.dot-theme-root :is(input,textarea){-webkit-user-select:text;user-select:text}}@keyframes dot-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}.dot-image{max-width:100%;display:block}.dot-list>.dot-list-item:not(:last-child){border-bottom:1px solid var(--dot-list-divider)}.dot-scroll::-webkit-scrollbar{display:none}.dot-scroll>*{scroll-snap-align:var(--dot-scroll-snap);flex:none;width:min-content}.dot-scroll .dot-image{max-width:none}.dot-bar-top:not(:not(.dot-toast)~*){margin-top:calc(-1 * var(--dot-view-top,0px))}.dot-bar-bottom:not(:has(~:not(.dot-toast))){margin-bottom:calc(-1 * var(--dot-view-bottom,0px) - env(safe-area-inset-bottom,0px))}.dot-disclosure>summary{list-style:none}.dot-disclosure>summary::-webkit-details-marker{display:none}.dot-steps>.dot-step:last-child .dot-step-line{display:none}.dot-sheet-backdrop{transition:opacity .2s}.dot-sheet{transition:transform .2s ease-out}.dot-sheet-backdrop:is([data-starting-style],[data-ending-style]){opacity:0}.dot-sheet:is([data-starting-style],[data-ending-style]){transform:translateY(100%)}.dot-prose>.dot-heading{margin-block-start:.5em}.dot-prose>.dot-heading:first-child{margin-block-start:0}
package/dist/theme.d.ts CHANGED
@@ -1,8 +1,10 @@
1
- import { ColorScheme, PageColors, Theme, ThemeShape } from "@monimesl/dot-core";
1
+ import { FONT_SIZE, SPACE } from "./components/tokens.js";
2
+ import { ColorScheme, PageColors, Theme, ThemeComponents, ThemeElevation, ThemeShape, ThemeWeight } from "@monimesl/dot-core";
2
3
  //#region src/theme.d.ts
3
4
  export interface ResolvedColors {
4
5
  primary: string;
5
6
  onPrimary: string;
7
+ primaryText: string;
6
8
  background: string;
7
9
  surface: string;
8
10
  text: string;
@@ -20,6 +22,15 @@ export interface ResolvedTheme {
20
22
  customColors: Readonly<Record<string, string>>;
21
23
  shape: ThemeShape;
22
24
  radius: number;
25
+ fontFamily: string;
26
+ headingFontFamily: string;
27
+ headingWeight: ThemeWeight | undefined;
28
+ fontSize: Readonly<Record<keyof typeof FONT_SIZE, number>>;
29
+ components: ThemeComponents;
30
+ icons: Readonly<Record<string, string>>;
31
+ gradients: Readonly<Record<string, string>>;
32
+ space: Readonly<Record<keyof typeof SPACE, number>>;
33
+ elevation: ThemeElevation;
23
34
  }
24
35
  export declare function resolveTheme(theme: Theme, systemScheme: ColorScheme, pageColors?: PageColors): ResolvedTheme;
25
36
  export declare function useTheme(): ResolvedTheme;
package/dist/theme.js CHANGED
@@ -1,8 +1,29 @@
1
+ import { FONT_SIZE, SPACE } from "./components/tokens.js";
1
2
  import { darken, mix } from "./color.js";
2
3
  import { DEFAULT_BACKGROUND, READABLE_CONTRAST, contrast, isDark, pageBackground, textOn, themeScheme } from "@monimesl/dot-core";
3
4
  import { createContext, useContext } from "react";
4
5
  //#region src/theme.ts
5
- const FONT_FAMILY = "system-ui, sans-serif";
6
+ const DENSITY_SCALE = {
7
+ compact: .75,
8
+ regular: 1,
9
+ comfortable: 1.25
10
+ };
11
+ const TEXT_SCALE = {
12
+ small: .9,
13
+ regular: 1,
14
+ large: 1.1
15
+ };
16
+ const GRADIENT_DIRECTIONS = {
17
+ down: "to bottom",
18
+ right: "to right",
19
+ diagonal: "to bottom right"
20
+ };
21
+ const FONT_FAMILIES = {
22
+ system: "system-ui, sans-serif",
23
+ rounded: "ui-rounded, \"SF Pro Rounded\", system-ui, sans-serif",
24
+ serif: "ui-serif, \"New York\", Georgia, serif",
25
+ mono: "ui-monospace, \"SF Mono\", Menlo, monospace"
26
+ };
6
27
  const SHAPE_RADIUS = {
7
28
  square: 0,
8
29
  subtle: 4,
@@ -51,22 +72,71 @@ function resolveTheme(theme, systemScheme, pageColors = {}) {
51
72
  const text = ownColor("text") ?? readableText(defaults.text, background);
52
73
  let subtle = defaults.subtle;
53
74
  if (!onDashBackground) subtle = quieterText(text, background, surface);
54
- const primary = ownColor("primary") ?? defaults.primary;
75
+ const { primary, onPrimary } = primaryAndOnPrimary(ownColor("primary") ?? defaults.primary, surface, text, scheme);
76
+ const primaryReads = contrast(primary, background) >= READABLE_CONTRAST && contrast(primary, surface) >= READABLE_CONTRAST;
77
+ const colors = {
78
+ ...defaults,
79
+ primary,
80
+ onPrimary,
81
+ primaryText: primaryReads ? primary : text,
82
+ background,
83
+ surface,
84
+ text,
85
+ muted: ownColor("muted") ?? surfaces.muted,
86
+ border: ownColor("border") ?? surfaces.border,
87
+ subtle: ownColor("subtle") ?? subtle,
88
+ info: ownColor("info") ?? defaults.info,
89
+ success: ownColor("success") ?? defaults.success,
90
+ warning: ownColor("warning") ?? defaults.warning,
91
+ danger: ownColor("danger") ?? defaults.danger
92
+ };
93
+ const customColors = resolveCustomColors(themeColors.custom ?? {}, scheme);
94
+ const fontFamily = FONT_FAMILIES[theme.font ?? "system"] ?? FONT_FAMILIES.system;
95
+ let headingFontFamily = fontFamily;
96
+ if (theme.headingFont) headingFontFamily = FONT_FAMILIES[theme.headingFont] ?? fontFamily;
55
97
  return {
56
98
  scheme,
57
- colors: {
58
- ...defaults,
59
- ...primaryAndOnPrimary(primary, surface, text, scheme),
60
- background,
61
- surface,
62
- text,
63
- muted: ownColor("muted") ?? surfaces.muted,
64
- border: ownColor("border") ?? surfaces.border,
65
- subtle: ownColor("subtle") ?? subtle
66
- },
67
- customColors: resolveCustomColors(themeColors.custom ?? {}, scheme),
99
+ colors,
100
+ customColors,
68
101
  shape,
69
- radius: SHAPE_RADIUS[shape]
102
+ radius: SHAPE_RADIUS[shape],
103
+ fontFamily,
104
+ headingFontFamily,
105
+ headingWeight: theme.headingWeight,
106
+ fontSize: scaledFontSize(TEXT_SCALE[theme.textSize ?? "regular"] ?? 1),
107
+ components: theme.components ?? {},
108
+ icons: theme.icons ?? {},
109
+ gradients: resolveGradients(theme.gradients ?? {}, colors, customColors),
110
+ space: scaledSpace(DENSITY_SCALE[theme.density ?? "regular"] ?? 1),
111
+ elevation: theme.elevation === "raised" ? "raised" : "flat"
112
+ };
113
+ }
114
+ function scaledFontSize(scale) {
115
+ const sizes = { ...FONT_SIZE };
116
+ for (const variant of Object.keys(sizes)) sizes[variant] = Math.round(FONT_SIZE[variant] * scale);
117
+ return sizes;
118
+ }
119
+ function resolveGradients(gradients, colors, customColors) {
120
+ const resolved = {};
121
+ for (const [name, gradient] of Object.entries(gradients)) {
122
+ if (!gradient) continue;
123
+ const from = themeColor(gradient.from, colors, customColors);
124
+ const to = themeColor(gradient.to, colors, customColors);
125
+ const direction = GRADIENT_DIRECTIONS[gradient.direction ?? "down"] ?? GRADIENT_DIRECTIONS.down;
126
+ if (from && to) resolved[name] = `linear-gradient(${direction}, ${from}, ${to})`;
127
+ }
128
+ return resolved;
129
+ }
130
+ function themeColor(name, colors, customColors) {
131
+ if (Object.hasOwn(colors, name)) return colors[name];
132
+ if (Object.hasOwn(customColors, name)) return customColors[name];
133
+ }
134
+ function scaledSpace(scale) {
135
+ return {
136
+ none: SPACE.none,
137
+ small: Math.round(SPACE.small * scale),
138
+ medium: Math.round(SPACE.medium * scale),
139
+ large: Math.round(SPACE.large * scale)
70
140
  };
71
141
  }
72
142
  function surfacesOn(background) {
@@ -126,4 +196,4 @@ function useTheme() {
126
196
  return useContext(ThemeContext);
127
197
  }
128
198
  //#endregion
129
- export { FONT_FAMILY, ThemeContext, resolveTheme, useTheme };
199
+ export { ThemeContext, resolveTheme, useTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monimesl/dot-renderer-react",
3
- "version": "1.0.0-beta.7",
3
+ "version": "1.0.0-beta.9",
4
4
  "description": "React renderer for Dot webviews",
5
5
  "type": "module",
6
6
  "repository": {
@@ -29,8 +29,8 @@
29
29
  "react": "19.3.0",
30
30
  "react-dom": "19.3.0",
31
31
  "uqr": "^0.1.3",
32
- "@monimesl/dot-core": "1.0.0-beta.7",
33
- "@monimesl/dot-webview": "1.0.0-beta.7"
32
+ "@monimesl/dot-core": "1.0.0-beta.9",
33
+ "@monimesl/dot-webview": "1.0.0-beta.9"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@base-ui/react": "1.8.0",
@@ -46,7 +46,7 @@
46
46
  "tsdown": "^0.23.0",
47
47
  "typescript": "^5.9.3",
48
48
  "vitest": "^5.0.1",
49
- "@monimesl/dot-service": "1.0.0-beta.7"
49
+ "@monimesl/dot-service": "1.0.0-beta.9"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"