@boyernick/standard-ui-react 0.1.0 → 0.1.1-canary.2
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/package.json +9 -3
- package/src/carousel.tsx +28 -19
- package/src/lib/cn.ts +43 -1
- package/src/markdown-editor.tsx +1 -1
- package/src/text-animate.tsx +0 -2
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boyernick/standard-ui-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-canary.2",
|
|
4
4
|
"description": "React components for StandardUI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"files": [
|
|
9
|
-
"src"
|
|
9
|
+
"src",
|
|
10
|
+
"!src/**/*.test.ts"
|
|
10
11
|
],
|
|
11
12
|
"exports": {
|
|
12
13
|
".": {
|
|
@@ -15,6 +16,10 @@
|
|
|
15
16
|
},
|
|
16
17
|
"./package.json": "./package.json"
|
|
17
18
|
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
21
|
+
"test": "node --test --experimental-strip-types \"src/**/*.test.ts\""
|
|
22
|
+
},
|
|
18
23
|
"peerDependencies": {
|
|
19
24
|
"react": "^18.0.0 || ^19.0.0",
|
|
20
25
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
@@ -28,7 +33,8 @@
|
|
|
28
33
|
"embla-carousel-react": "^8.6.0",
|
|
29
34
|
"react-day-picker": "^10.0.1",
|
|
30
35
|
"recharts": "^3.10.1",
|
|
31
|
-
"sugar-high": "^2.0.1"
|
|
36
|
+
"sugar-high": "^2.0.1",
|
|
37
|
+
"tailwind-merge": "^3.6.0"
|
|
32
38
|
},
|
|
33
39
|
"devDependencies": {
|
|
34
40
|
"@types/react": "^19",
|
package/src/carousel.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useCallback,
|
|
9
9
|
useContext,
|
|
10
10
|
useEffect,
|
|
11
|
-
|
|
11
|
+
useSyncExternalStore,
|
|
12
12
|
type ComponentProps,
|
|
13
13
|
type KeyboardEvent,
|
|
14
14
|
} from "react"
|
|
@@ -70,14 +70,34 @@ export const Carousel = ({
|
|
|
70
70
|
},
|
|
71
71
|
plugins,
|
|
72
72
|
)
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
// Embla is an external store, so read from it directly rather than mirroring
|
|
74
|
+
// it into state. Seeding mirrored state meant calling setState synchronously
|
|
75
|
+
// inside an effect, costing an extra render on mount and on every slide
|
|
76
|
+
// change. Both snapshots return booleans, so there is no object identity to
|
|
77
|
+
// keep stable between reads.
|
|
78
|
+
const subscribe = useCallback(
|
|
79
|
+
(onStoreChange: () => void) => {
|
|
80
|
+
if (!api) return () => {}
|
|
81
|
+
api.on("reInit", onStoreChange)
|
|
82
|
+
api.on("select", onStoreChange)
|
|
83
|
+
return () => {
|
|
84
|
+
api.off("reInit", onStoreChange)
|
|
85
|
+
api.off("select", onStoreChange)
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
[api],
|
|
89
|
+
)
|
|
75
90
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
const canScrollPrev = useSyncExternalStore(
|
|
92
|
+
subscribe,
|
|
93
|
+
() => api?.canScrollPrev() ?? false,
|
|
94
|
+
() => false,
|
|
95
|
+
)
|
|
96
|
+
const canScrollNext = useSyncExternalStore(
|
|
97
|
+
subscribe,
|
|
98
|
+
() => api?.canScrollNext() ?? false,
|
|
99
|
+
() => false,
|
|
100
|
+
)
|
|
81
101
|
|
|
82
102
|
const scrollPrev = useCallback(() => {
|
|
83
103
|
api?.scrollPrev()
|
|
@@ -107,17 +127,6 @@ export const Carousel = ({
|
|
|
107
127
|
setApi(api)
|
|
108
128
|
}, [api, setApi])
|
|
109
129
|
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
if (!api) return
|
|
112
|
-
handleSelect(api)
|
|
113
|
-
api.on("reInit", handleSelect)
|
|
114
|
-
api.on("select", handleSelect)
|
|
115
|
-
return () => {
|
|
116
|
-
api.off("reInit", handleSelect)
|
|
117
|
-
api.off("select", handleSelect)
|
|
118
|
-
}
|
|
119
|
-
}, [api, handleSelect])
|
|
120
|
-
|
|
121
130
|
return (
|
|
122
131
|
<CarouselContext.Provider
|
|
123
132
|
value={{
|
package/src/lib/cn.ts
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
1
|
import { clsx, type ClassValue } from "clsx"
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge"
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* StandardUI defines typography composites as custom `@utility` rules
|
|
6
|
+
* (`text-sm-strong`, `text-md`, …). Tailwind Merge cannot know those are font
|
|
7
|
+
* sizes, so it files them under text *color* — and then drops them whenever a
|
|
8
|
+
* real color follows, as in `cn("text-sm-strong", "text-fg-primary")`. Grouping
|
|
9
|
+
* them with font sizes keeps them conflicting with each other and with
|
|
10
|
+
* `text-sm`, while leaving colors alone.
|
|
11
|
+
*
|
|
12
|
+
* `shadow-hairline` has the same problem in the shadow group. Custom font sizes
|
|
13
|
+
* (`text-2xs`) and radii (`rounded-2xs`, `rounded-4xl`) already resolve
|
|
14
|
+
* correctly and need no help.
|
|
15
|
+
*
|
|
16
|
+
* Keep this in step with the `@utility` rules in
|
|
17
|
+
* @boyernick/standard-ui-tokens/css/tokens.css.
|
|
18
|
+
*/
|
|
19
|
+
const twMerge = extendTailwindMerge({
|
|
20
|
+
extend: {
|
|
21
|
+
classGroups: {
|
|
22
|
+
"font-size": [
|
|
23
|
+
{
|
|
24
|
+
text: [
|
|
25
|
+
"2xs-strong",
|
|
26
|
+
"xs-strong",
|
|
27
|
+
"sm-strong",
|
|
28
|
+
"md",
|
|
29
|
+
"md-strong",
|
|
30
|
+
"lg-strong",
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
shadow: [{ shadow: ["hairline"] }],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Combines class names and resolves Tailwind conflicts so the last value wins.
|
|
41
|
+
* Without the merge step a caller's `className` cannot override a built-in
|
|
42
|
+
* utility: both classes land in the DOM and the winner is whichever rule sits
|
|
43
|
+
* later in the stylesheet, which the caller does not control.
|
|
44
|
+
*/
|
|
45
|
+
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs))
|
package/src/markdown-editor.tsx
CHANGED
|
@@ -108,7 +108,7 @@ type Format = {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
const formats: Format[] = [
|
|
111
|
-
{ label: "Bold", marker: "**", className: "font-
|
|
111
|
+
{ label: "Bold", marker: "**", className: "font-medium" },
|
|
112
112
|
{ label: "Italic", marker: "*", className: "italic" },
|
|
113
113
|
{ label: "Code", marker: "`", className: "font-mono" },
|
|
114
114
|
]
|
package/src/text-animate.tsx
CHANGED
|
@@ -45,7 +45,6 @@ export const TextAnimate = ({
|
|
|
45
45
|
|
|
46
46
|
useEffect(() => {
|
|
47
47
|
let cancelled = false
|
|
48
|
-
let frame = 0
|
|
49
48
|
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
|
50
49
|
|
|
51
50
|
const start = () => {
|
|
@@ -114,7 +113,6 @@ export const TextAnimate = ({
|
|
|
114
113
|
return () => {
|
|
115
114
|
cancelled = true
|
|
116
115
|
if (timeoutId) clearTimeout(timeoutId)
|
|
117
|
-
cancelAnimationFrame(frame)
|
|
118
116
|
}
|
|
119
117
|
}, [chars, delay, effect, replay, speed, text])
|
|
120
118
|
|