@monimesl/dot-renderer-react 1.0.0-beta.10 → 1.0.0-beta.12
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/dist/color.js +1 -1
- package/dist/components/box.js +16 -4
- package/dist/components/carousel.js +3 -15
- package/dist/components/child-elements.js +25 -0
- package/dist/components/disclosure.js +1 -0
- package/dist/components/empty-state.js +2 -1
- package/dist/components/enter.js +28 -0
- package/dist/components/feedback.js +2 -1
- package/dist/components/list.js +2 -1
- package/dist/components/sheet.js +3 -1
- package/dist/components/stack.js +4 -4
- package/dist/components/text.js +11 -2
- package/dist/components/tokens.js +6 -1
- package/dist/components/view.js +6 -3
- package/dist/development-warnings.js +47 -0
- package/dist/page-root.js +4 -1
- package/dist/root.js +4 -1
- package/dist/styles.css +1 -1
- package/dist/theme.js +1 -0
- package/package.json +4 -4
package/dist/color.js
CHANGED
package/dist/components/box.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CARD_RADIUS_SCALE } from "./tokens.js";
|
|
2
2
|
import { isTone, toneBackground, toneColor } from "./tones.js";
|
|
3
|
-
import { BACKGROUNDS } from "@monimesl/dot-core";
|
|
3
|
+
import { BACKGROUNDS, BORDER_TONES } from "@monimesl/dot-core";
|
|
4
4
|
//#region src/components/box.ts
|
|
5
5
|
const RADIUS_SCALE = {
|
|
6
6
|
none: 0,
|
|
@@ -10,16 +10,17 @@ const RADIUS_SCALE = {
|
|
|
10
10
|
};
|
|
11
11
|
const FULL_RADIUS = 9999;
|
|
12
12
|
const BACKGROUND_WORDS = new Set(BACKGROUNDS.words);
|
|
13
|
+
const BORDER_TONE_WORDS = new Set(BORDER_TONES.words);
|
|
13
14
|
function boxStyle(props, theme, look = {}) {
|
|
14
15
|
const background = props.background ?? look.background;
|
|
15
|
-
const border = props.border ?? look.border;
|
|
16
|
+
const border = props.border ?? (props.borderTone ? "subtle" : look.border);
|
|
16
17
|
const radius = props.radius ?? look.radius;
|
|
17
18
|
const style = {};
|
|
18
19
|
if (background !== void 0) style.background = backgroundColor(background, theme);
|
|
19
20
|
if (border === "none") style.border = "none";
|
|
20
|
-
else if (border !== void 0) style.border = `1px solid ${border
|
|
21
|
+
else if (border !== void 0) style.border = `1px solid ${borderColor(border, props.borderTone, theme)}`;
|
|
21
22
|
if (radius !== void 0) {
|
|
22
|
-
style.borderRadius = radius === "full" ? FULL_RADIUS : theme.radius * RADIUS_SCALE[radius];
|
|
23
|
+
style.borderRadius = cornerRadius(radius === "full" ? FULL_RADIUS : theme.radius * RADIUS_SCALE[radius], props.corners);
|
|
23
24
|
style.overflow = "hidden";
|
|
24
25
|
}
|
|
25
26
|
if (props.width !== void 0) {
|
|
@@ -35,6 +36,17 @@ function boxStyle(props, theme, look = {}) {
|
|
|
35
36
|
if (props.maxWidth !== void 0) style.maxWidth = props.maxWidth;
|
|
36
37
|
return style;
|
|
37
38
|
}
|
|
39
|
+
function borderColor(border, tone, theme) {
|
|
40
|
+
const usual = border === "strong" ? theme.colors.subtle : theme.colors.border;
|
|
41
|
+
if (tone === void 0) return usual;
|
|
42
|
+
if (BORDER_TONE_WORDS.has(tone)) return theme.colors[tone];
|
|
43
|
+
return customColor(tone, theme) ?? usual;
|
|
44
|
+
}
|
|
45
|
+
function cornerRadius(size, corners) {
|
|
46
|
+
if (corners === "top") return `${size}px ${size}px 0 0`;
|
|
47
|
+
if (corners === "bottom") return `0 0 ${size}px ${size}px`;
|
|
48
|
+
return size;
|
|
49
|
+
}
|
|
38
50
|
function backgroundColor(background, theme) {
|
|
39
51
|
if (isTone(background)) return toneBackground(theme.colors, background);
|
|
40
52
|
if (BACKGROUND_WORDS.has(background)) return theme.colors[background];
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { SHADOW_REACH, SPACE } from "./tokens.js";
|
|
2
2
|
import { useTheme } from "../theme.js";
|
|
3
|
+
import { childElements } from "./child-elements.js";
|
|
3
4
|
import { clamp } from "./numbers.js";
|
|
4
5
|
import { paddingStyle } from "./padding.js";
|
|
5
6
|
import { SideRoomContext } from "./side-room.js";
|
|
6
7
|
import { createEvent } from "@monimesl/dot-core";
|
|
7
|
-
import {
|
|
8
|
+
import { useContext, useEffect, useEffectEvent, useRef, useState } from "react";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/components/carousel.tsx
|
|
10
11
|
const SLIDE_DURATION = 350;
|
|
@@ -16,7 +17,7 @@ const TAP_SLOP = 6;
|
|
|
16
17
|
const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator = "dots", gap = "none", effect = "slide", index, onChange, children, ...padding }) => {
|
|
17
18
|
const { colors, space, elevation } = useTheme();
|
|
18
19
|
const mayUseSides = useContext(SideRoomContext);
|
|
19
|
-
const slides =
|
|
20
|
+
const slides = childElements(children);
|
|
20
21
|
const count = slides.length;
|
|
21
22
|
const looping = loop && count > 1;
|
|
22
23
|
const firstPosition = looping ? 1 : 0;
|
|
@@ -250,19 +251,6 @@ const Carousel$1 = ({ autoplay = false, interval = 4e3, loop = false, indicator
|
|
|
250
251
|
}) : null]
|
|
251
252
|
});
|
|
252
253
|
};
|
|
253
|
-
function slidesOf(children, keyPrefix = "") {
|
|
254
|
-
const slides = [];
|
|
255
|
-
for (const child of Children.toArray(children)) {
|
|
256
|
-
if (!isValidElement(child)) continue;
|
|
257
|
-
const key = `${keyPrefix}${String(child.key)}`;
|
|
258
|
-
if (child.type === Fragment) slides.push(...slidesOf(child.props.children, `${key}/`));
|
|
259
|
-
else slides.push({
|
|
260
|
-
key,
|
|
261
|
-
node: child
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
return slides;
|
|
265
|
-
}
|
|
266
254
|
function slideIndex(index, count) {
|
|
267
255
|
return clamp(Math.round(index), 0, count - 1);
|
|
268
256
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Children, Fragment, isValidElement } from "react";
|
|
2
|
+
//#region src/components/child-elements.ts
|
|
3
|
+
function childElements(children, keyPrefix = "") {
|
|
4
|
+
const elements = [];
|
|
5
|
+
for (const child of Children.toArray(children)) {
|
|
6
|
+
if (!isValidElement(child)) continue;
|
|
7
|
+
const key = `${keyPrefix}${String(child.key)}`;
|
|
8
|
+
if (child.type === Fragment) elements.push(...childElements(child.props.children, `${key}/`));
|
|
9
|
+
else elements.push({
|
|
10
|
+
key,
|
|
11
|
+
node: child
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return elements;
|
|
15
|
+
}
|
|
16
|
+
function hasContent(children) {
|
|
17
|
+
for (const child of Children.toArray(children)) {
|
|
18
|
+
if (child === "") continue;
|
|
19
|
+
if (!isValidElement(child) || child.type !== Fragment) return true;
|
|
20
|
+
if (hasContent(child.props.children)) return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { childElements, hasContent };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CENTERED, ICON_SIZE, SPACE, WEIGHTS } from "./tokens.js";
|
|
2
2
|
import { useTheme } from "../theme.js";
|
|
3
|
+
import { hasContent } from "./child-elements.js";
|
|
3
4
|
import { insets, paddingStyle } from "./padding.js";
|
|
4
5
|
import { IconGraphic } from "./icon.js";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -50,7 +51,7 @@ const EmptyState = ({ icon, title, text, children, ...padding }) => {
|
|
|
50
51
|
},
|
|
51
52
|
children: text
|
|
52
53
|
}) : null,
|
|
53
|
-
children ? /* @__PURE__ */ jsx("div", {
|
|
54
|
+
hasContent(children) ? /* @__PURE__ */ jsx("div", {
|
|
54
55
|
style: {
|
|
55
56
|
display: "flex",
|
|
56
57
|
gap: SPACE.medium,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef } from "react";
|
|
2
|
+
//#region src/components/enter.ts
|
|
3
|
+
const ENTER_DURATION = 250;
|
|
4
|
+
const ENTER_FRAMES = {
|
|
5
|
+
fade: {
|
|
6
|
+
offset: 0,
|
|
7
|
+
opacity: 0
|
|
8
|
+
},
|
|
9
|
+
rise: {
|
|
10
|
+
offset: 0,
|
|
11
|
+
opacity: 0,
|
|
12
|
+
transform: "translateY(12px)"
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function useEnterAnimation(enter, hidden) {
|
|
16
|
+
const ref = useRef(null);
|
|
17
|
+
useLayoutEffect(() => {
|
|
18
|
+
if (hidden || !Object.hasOwn(ENTER_FRAMES, enter)) return;
|
|
19
|
+
if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
20
|
+
ref.current?.animate([ENTER_FRAMES[enter]], {
|
|
21
|
+
duration: ENTER_DURATION,
|
|
22
|
+
easing: "ease-out"
|
|
23
|
+
});
|
|
24
|
+
}, [enter, hidden]);
|
|
25
|
+
return ref;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { useEnterAnimation };
|
|
@@ -3,6 +3,7 @@ import { useTheme } from "../theme.js";
|
|
|
3
3
|
import { CLOSE_ARTWORK } from "./icon-artwork.js";
|
|
4
4
|
import { IconSvg } from "./icon-svg.js";
|
|
5
5
|
import { STATUS_ARTWORK, toneBackground, toneColor } from "./tones.js";
|
|
6
|
+
import { hasContent } from "./child-elements.js";
|
|
6
7
|
import { clamp } from "./numbers.js";
|
|
7
8
|
import { insets, paddingStyle } from "./padding.js";
|
|
8
9
|
import { IconButton } from "./icon-button.js";
|
|
@@ -64,7 +65,7 @@ const Notice$1 = ({ tone = "default", title, children, onClose, ...padding }) =>
|
|
|
64
65
|
children: [title ? /* @__PURE__ */ jsx("span", {
|
|
65
66
|
style: { fontWeight: WEIGHTS.bold },
|
|
66
67
|
children: title
|
|
67
|
-
}) : null, children ? /* @__PURE__ */ jsx("span", { children }) : null]
|
|
68
|
+
}) : null, hasContent(children) ? /* @__PURE__ */ jsx("span", { children }) : null]
|
|
68
69
|
}),
|
|
69
70
|
onClose ? /* @__PURE__ */ jsx(IconButton, {
|
|
70
71
|
artwork: CLOSE_ARTWORK,
|
package/dist/components/list.js
CHANGED
|
@@ -2,6 +2,7 @@ import { CARD_RADIUS_SCALE, CENTERED, DISABLED_OPACITY, ICON_SIZE, SHADOWS, WEIG
|
|
|
2
2
|
import { useTheme } from "../theme.js";
|
|
3
3
|
import { CHEVRON_RIGHT_ARTWORK } from "./icon-artwork.js";
|
|
4
4
|
import { IconSvg } from "./icon-svg.js";
|
|
5
|
+
import { hasContent } from "./child-elements.js";
|
|
5
6
|
import { insets, paddingStyle } from "./padding.js";
|
|
6
7
|
import { IconGraphic } from "./icon.js";
|
|
7
8
|
import { createEvent } from "@monimesl/dot-core";
|
|
@@ -95,7 +96,7 @@ const ListItem$1 = ({ title, subtitle, value, icon, image, disabled = false, onT
|
|
|
95
96
|
color: colors.text
|
|
96
97
|
})
|
|
97
98
|
});
|
|
98
|
-
const trailing = children
|
|
99
|
+
const trailing = hasContent(children) ? children : value;
|
|
99
100
|
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
100
101
|
leading,
|
|
101
102
|
/* @__PURE__ */ jsxs("div", {
|
package/dist/components/sheet.js
CHANGED
|
@@ -33,7 +33,8 @@ const SheetPanel = ({ open, title, showCloseIcon = false, onClose, children, ...
|
|
|
33
33
|
position: "fixed",
|
|
34
34
|
inset: 0,
|
|
35
35
|
zIndex: 100,
|
|
36
|
-
background: "rgba(0, 0, 0, 0.4)"
|
|
36
|
+
background: "rgba(0, 0, 0, 0.4)",
|
|
37
|
+
transition: "opacity 0.2s"
|
|
37
38
|
}
|
|
38
39
|
}), /* @__PURE__ */ jsx(DialogPopup, {
|
|
39
40
|
className: "dot-sheet",
|
|
@@ -43,6 +44,7 @@ const SheetPanel = ({ open, title, showCloseIcon = false, onClose, children, ...
|
|
|
43
44
|
insetInline: 0,
|
|
44
45
|
bottom: 0,
|
|
45
46
|
zIndex: 101,
|
|
47
|
+
transition: "transform 0.2s ease-out",
|
|
46
48
|
maxHeight: "85vh",
|
|
47
49
|
overflowY: "auto",
|
|
48
50
|
borderTopLeftRadius: cornerRadius,
|
package/dist/components/stack.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useTheme } from "../theme.js";
|
|
2
|
+
import { childElements } from "./child-elements.js";
|
|
2
3
|
import { SideRoomContext } from "./side-room.js";
|
|
3
4
|
import { boxStyle } from "./box.js";
|
|
4
|
-
import { Children } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/stack.tsx
|
|
7
7
|
const Stack = (props) => {
|
|
@@ -14,14 +14,14 @@ const Stack = (props) => {
|
|
|
14
14
|
},
|
|
15
15
|
children: /* @__PURE__ */ jsx(SideRoomContext.Provider, {
|
|
16
16
|
value: false,
|
|
17
|
-
children:
|
|
17
|
+
children: childElements(props.children).map(({ key, node }) => /* @__PURE__ */ jsx("div", {
|
|
18
18
|
style: {
|
|
19
19
|
gridArea: "1 / 1",
|
|
20
20
|
display: "grid",
|
|
21
21
|
minWidth: 0
|
|
22
22
|
},
|
|
23
|
-
children:
|
|
24
|
-
}))
|
|
23
|
+
children: node
|
|
24
|
+
}, key))
|
|
25
25
|
})
|
|
26
26
|
});
|
|
27
27
|
};
|
package/dist/components/text.js
CHANGED
|
@@ -3,6 +3,7 @@ import { useTheme } from "../theme.js";
|
|
|
3
3
|
import { paddingStyle } from "./padding.js";
|
|
4
4
|
import { textToneColor } from "./box.js";
|
|
5
5
|
import { InsideProse } from "./prose-context.js";
|
|
6
|
+
import { warnIfHardToRead } from "../development-warnings.js";
|
|
6
7
|
import { DEFAULT_VARIANTS } from "@monimesl/dot-core";
|
|
7
8
|
import { createContext, useContext } from "react";
|
|
8
9
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -50,7 +51,11 @@ const DECORATION = {
|
|
|
50
51
|
underline: "underline",
|
|
51
52
|
strikethrough: "line-through"
|
|
52
53
|
};
|
|
53
|
-
|
|
54
|
+
function letterSpacing(variant, caps) {
|
|
55
|
+
if (caps) return "0.06em";
|
|
56
|
+
return variant === "display" ? "-0.02em" : "normal";
|
|
57
|
+
}
|
|
58
|
+
const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decoration, numeric, leading, italic, caps, selectable, children, ...padding }) => {
|
|
54
59
|
const theme = useTheme();
|
|
55
60
|
const insideText = useContext(InsideText);
|
|
56
61
|
const inProse = useContext(InsideProse);
|
|
@@ -67,7 +72,8 @@ const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decorati
|
|
|
67
72
|
fontFamily: heading ? theme.headingFontFamily : void 0,
|
|
68
73
|
fontWeight: WEIGHTS[weight ?? (heading ? theme.headingWeight : void 0) ?? defaultWeight],
|
|
69
74
|
fontStyle: italic ? "italic" : void 0,
|
|
70
|
-
letterSpacing: variant
|
|
75
|
+
letterSpacing: letterSpacing(variant, caps),
|
|
76
|
+
textTransform: caps ? "uppercase" : void 0,
|
|
71
77
|
color: tone ? textToneColor(tone, theme) : "inherit",
|
|
72
78
|
textAlign: align,
|
|
73
79
|
textDecorationLine: decoration ? DECORATION[decoration] : void 0,
|
|
@@ -90,12 +96,15 @@ const Text$1 = ({ variant: namedVariant, tone, weight, align, maxLines, decorati
|
|
|
90
96
|
value: true,
|
|
91
97
|
children
|
|
92
98
|
});
|
|
99
|
+
const ref = tone === "muted" ? void 0 : warnIfHardToRead;
|
|
93
100
|
if ((variant === "title" || variant === "subtitle") && !insideText) return /* @__PURE__ */ jsx(variant === "title" ? "h2" : "h3", {
|
|
101
|
+
ref,
|
|
94
102
|
className: "dot-text dot-heading",
|
|
95
103
|
style,
|
|
96
104
|
children: content
|
|
97
105
|
});
|
|
98
106
|
return /* @__PURE__ */ jsx("span", {
|
|
107
|
+
ref,
|
|
99
108
|
className: "dot-text",
|
|
100
109
|
style,
|
|
101
110
|
children: content
|
|
@@ -20,6 +20,11 @@ const WEIGHTS = {
|
|
|
20
20
|
};
|
|
21
21
|
const CARD_RADIUS_SCALE = 1.5;
|
|
22
22
|
const DISABLED_OPACITY = .6;
|
|
23
|
+
const OPACITY = {
|
|
24
|
+
full: 1,
|
|
25
|
+
soft: .7,
|
|
26
|
+
faint: .4
|
|
27
|
+
};
|
|
23
28
|
function pressLook(disabled) {
|
|
24
29
|
return {
|
|
25
30
|
opacity: disabled ? DISABLED_OPACITY : 1,
|
|
@@ -71,4 +76,4 @@ const SELECTABLE = {
|
|
|
71
76
|
WebkitUserSelect: "text"
|
|
72
77
|
};
|
|
73
78
|
//#endregion
|
|
74
|
-
export { CARD_RADIUS_SCALE, CENTERED, DISABLED_OPACITY, FONT_SIZE, ICON_SIZE, PILL_STYLE, SCROLL_SHADOW_ROOM, SELECTABLE, SHADOWS, SHADOW_REACH, SPACE, WEIGHTS, pressLook };
|
|
79
|
+
export { CARD_RADIUS_SCALE, CENTERED, DISABLED_OPACITY, FONT_SIZE, ICON_SIZE, OPACITY, PILL_STYLE, SCROLL_SHADOW_ROOM, SELECTABLE, SHADOWS, SHADOW_REACH, SPACE, WEIGHTS, pressLook };
|
package/dist/components/view.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { DISABLED_OPACITY, SHADOWS } from "./tokens.js";
|
|
1
|
+
import { DISABLED_OPACITY, OPACITY, SHADOWS } from "./tokens.js";
|
|
2
2
|
import { useTheme } from "../theme.js";
|
|
3
3
|
import { insets, paddingStyle } from "./padding.js";
|
|
4
4
|
import { SideRoomContext } from "./side-room.js";
|
|
5
5
|
import { boxStyle } from "./box.js";
|
|
6
6
|
import { pressable } from "./pressable.js";
|
|
7
7
|
import { HiddenViewContext } from "./hidden-view.js";
|
|
8
|
+
import { useEnterAnimation } from "./enter.js";
|
|
8
9
|
import { DEFAULT_VARIANTS, createEvent } from "@monimesl/dot-core";
|
|
9
10
|
import { useContext } from "react";
|
|
10
11
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -31,10 +32,11 @@ const RAISED_CARD_LOOK = {
|
|
|
31
32
|
border: "none"
|
|
32
33
|
};
|
|
33
34
|
const View$1 = (props) => {
|
|
34
|
-
const { id, hidden = false, disabled = false, direction = "column", gap = "medium", align = "stretch", justify = "start", wrap = false, grow = false, variant = DEFAULT_VARIANTS.View, onTap, children } = props;
|
|
35
|
+
const { id, hidden = false, disabled = false, direction = "column", gap = "medium", align = "stretch", justify = "start", wrap = false, grow = false, variant = DEFAULT_VARIANTS.View, opacity = "full", enter = "none", onTap, children } = props;
|
|
35
36
|
const theme = useTheme();
|
|
36
37
|
const sideRoomAround = useContext(SideRoomContext);
|
|
37
38
|
const hiddenAround = useContext(HiddenViewContext);
|
|
39
|
+
const enterRef = useEnterAnimation(enter, hidden || hiddenAround);
|
|
38
40
|
const card = variant === "card";
|
|
39
41
|
const raisedCard = card && theme.elevation === "raised" && props.elevation !== "none";
|
|
40
42
|
let cardLook;
|
|
@@ -54,7 +56,7 @@ const View$1 = (props) => {
|
|
|
54
56
|
"--dot-view-start": `${padding.paddingInlineStart}px`,
|
|
55
57
|
"--dot-view-end": `${padding.paddingInlineEnd}px`,
|
|
56
58
|
...boxStyle(props, theme, cardLook),
|
|
57
|
-
opacity: disabled ? DISABLED_OPACITY :
|
|
59
|
+
opacity: disabled ? DISABLED_OPACITY : OPACITY[opacity],
|
|
58
60
|
cursor: onTap && !disabled ? "pointer" : void 0
|
|
59
61
|
};
|
|
60
62
|
if (elevation !== "none") style.boxShadow = SHADOWS[theme.scheme][elevation];
|
|
@@ -64,6 +66,7 @@ const View$1 = (props) => {
|
|
|
64
66
|
}
|
|
65
67
|
const press = onTap && !disabled ? pressable(() => onTap(createEvent("tap"))) : void 0;
|
|
66
68
|
return /* @__PURE__ */ jsx("div", {
|
|
69
|
+
ref: enterRef,
|
|
67
70
|
className: "dot-view",
|
|
68
71
|
"data-dot-view": id,
|
|
69
72
|
hidden,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { toHex } from "./color.js";
|
|
2
|
+
import { contrast, variantProblem } from "@monimesl/dot-core";
|
|
3
|
+
//#region src/development-warnings.ts
|
|
4
|
+
const developing = process.env.NODE_ENV !== "production";
|
|
5
|
+
const MINIMUM_CONTRAST = 3;
|
|
6
|
+
const warned = /* @__PURE__ */ new Set();
|
|
7
|
+
function warnOnce(problem, about = problem) {
|
|
8
|
+
if (!warned.has(about)) {
|
|
9
|
+
warned.add(about);
|
|
10
|
+
console.warn(problem);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const warnAboutVariant = developing ? (name, props, components) => {
|
|
14
|
+
const problem = variantProblem(name, props, components);
|
|
15
|
+
if (problem !== void 0) warnOnce(problem);
|
|
16
|
+
} : void 0;
|
|
17
|
+
const warnIfHardToRead = developing ? (element) => {
|
|
18
|
+
if (element === null || element.closest(".dot-stack")) return;
|
|
19
|
+
const color = opaqueHex(getComputedStyle(element).color);
|
|
20
|
+
const background = solidBackgroundBehind(element);
|
|
21
|
+
if (color === void 0 || background === void 0) return;
|
|
22
|
+
const measured = contrast(color, background);
|
|
23
|
+
if (measured < MINIMUM_CONTRAST) {
|
|
24
|
+
const pair = `${color} on ${background}`;
|
|
25
|
+
warnOnce(`"${element.textContent?.slice(0, 40)}" is hard to read: ${pair} has a contrast of ${measured.toFixed(1)}. Change its tone or its background to reach at least ${MINIMUM_CONTRAST}.`, pair);
|
|
26
|
+
}
|
|
27
|
+
} : void 0;
|
|
28
|
+
function solidBackgroundBehind(element) {
|
|
29
|
+
for (let current = element; current; current = current.parentElement) {
|
|
30
|
+
const style = getComputedStyle(current);
|
|
31
|
+
if (style.backgroundImage !== "none" && style.backgroundImage !== "") return;
|
|
32
|
+
const background = opaqueHex(style.backgroundColor);
|
|
33
|
+
if (background !== void 0) return background;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function opaqueHex(computed) {
|
|
37
|
+
if (!computed.startsWith("rgb")) return;
|
|
38
|
+
const [red, green, blue, alpha = 1] = computed.match(/[\d.]+/g)?.map(Number) ?? [];
|
|
39
|
+
if (blue === void 0 || alpha < 1) return;
|
|
40
|
+
return toHex([
|
|
41
|
+
red,
|
|
42
|
+
green,
|
|
43
|
+
blue
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { warnAboutVariant, warnIfHardToRead };
|
package/dist/page-root.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useTheme } from "./theme.js";
|
|
2
|
+
import { warnAboutVariant } from "./development-warnings.js";
|
|
2
3
|
import { toList, toText, withComponentDefaults } from "@monimesl/dot-core";
|
|
3
4
|
import { Fragment, createElement, useMemo, useSyncExternalStore } from "react";
|
|
4
5
|
//#region src/page-root.tsx
|
|
@@ -9,7 +10,9 @@ function PageRoot({ page, components }) {
|
|
|
9
10
|
h(type, props, ...children) {
|
|
10
11
|
const component = components[type];
|
|
11
12
|
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.`);
|
|
12
|
-
|
|
13
|
+
const merged = withComponentDefaults(type, props, themeComponents);
|
|
14
|
+
warnAboutVariant?.(type, merged, themeComponents);
|
|
15
|
+
return createElement(component, merged, ...children);
|
|
13
16
|
},
|
|
14
17
|
text: toText,
|
|
15
18
|
on: (handler, data) => page.on(handler, data),
|
package/dist/root.js
CHANGED
|
@@ -27,7 +27,10 @@ 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: {
|
|
30
|
+
style: {
|
|
31
|
+
overflow: "clip",
|
|
32
|
+
WebkitTapHighlightColor: "transparent"
|
|
33
|
+
},
|
|
31
34
|
children: /* @__PURE__ */ jsx(PortalContainerContext.Provider, {
|
|
32
35
|
value: rootElement,
|
|
33
36
|
children: /* @__PURE__ */ jsx(PullToRefresh, {
|
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::-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,[hidden])~*){margin-top:calc(-1 * var(--dot-view-top,0px))}.dot-bar-bottom:not(:has(~:not(.dot-toast,[hidden]))){margin-bottom:calc(-1 * var(--dot-view-bottom,0px) - env(safe-area-inset-bottom,0px))}
|
|
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,[hidden])~*){margin-top:calc(-1 * var(--dot-view-top,0px))}.dot-bar-bottom:not(:has(~:not(.dot-toast,[hidden]))){margin-bottom:calc(-1 * var(--dot-view-bottom,0px) - env(safe-area-inset-bottom,0px))}html{overscroll-behavior-y:none}:is(button,a,summary,[role=button]):active:not(:disabled,[aria-disabled=true],:has(:is(button,a,summary,[role=button]):active)){filter:opacity(.7)}.dot-disclosure>summary::-webkit-details-marker{display:none}.dot-steps>.dot-step:last-child .dot-step-line{display:none}.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.js
CHANGED
|
@@ -128,6 +128,7 @@ function resolveGradients(gradients, colors, customColors) {
|
|
|
128
128
|
return resolved;
|
|
129
129
|
}
|
|
130
130
|
function themeColor(name, colors, customColors) {
|
|
131
|
+
if (name === "transparent") return name;
|
|
131
132
|
if (Object.hasOwn(colors, name)) return colors[name];
|
|
132
133
|
if (Object.hasOwn(customColors, name)) return customColors[name];
|
|
133
134
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monimesl/dot-renderer-react",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.12",
|
|
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.
|
|
33
|
-
"@monimesl/dot-webview": "1.0.0-beta.
|
|
32
|
+
"@monimesl/dot-core": "1.0.0-beta.12",
|
|
33
|
+
"@monimesl/dot-webview": "1.0.0-beta.12"
|
|
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.
|
|
49
|
+
"@monimesl/dot-service": "1.0.0-beta.12"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|