@solidrt/components 0.0.33 → 0.0.35
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 +2 -2
- package/src/button.tsx +32 -24
- package/src/card.tsx +13 -6
- package/src/theme.ts +21 -4
- package/src/types.ts +16 -0
- package/src/view.tsx +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidrt/components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Antoine van Wel",
|
|
6
6
|
"type": "module",
|
|
@@ -18,6 +18,6 @@
|
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@solidjs/signals": "2.0.0-beta.20",
|
|
21
|
-
"@solidrt/core": "0.0.
|
|
21
|
+
"@solidrt/core": "0.0.35"
|
|
22
22
|
}
|
|
23
23
|
}
|
package/src/button.tsx
CHANGED
|
@@ -8,20 +8,30 @@ import type { LayoutProps } from "@solidrt/core"
|
|
|
8
8
|
import type { StyleProps } from "./types"
|
|
9
9
|
|
|
10
10
|
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger"
|
|
11
|
+
export type ButtonSize = "sm" | "md" | "lg"
|
|
11
12
|
|
|
12
13
|
export interface ButtonProps {
|
|
13
14
|
// A string/number is rendered as the themed label; anything else is rendered
|
|
14
15
|
// as-is, so a button can hold custom content (an icon, a row, ...).
|
|
15
16
|
children?: any
|
|
16
|
-
// Visual role: primary (accent fill), secondary (
|
|
17
|
-
//
|
|
17
|
+
// Visual role: primary (accent fill), secondary (darker-blue fill), ghost
|
|
18
|
+
// (no fill until hover), danger (destructive accent fill). None draw a border.
|
|
18
19
|
variant?: ButtonVariant
|
|
20
|
+
// Fixed-width preset: pins the button to a set width (a longer label still
|
|
21
|
+
// expands past it), so a row of buttons lines up. Omitted, the button
|
|
22
|
+
// stretches to the container's width (the default). Padding is the same at
|
|
23
|
+
// every size.
|
|
24
|
+
size?: ButtonSize
|
|
19
25
|
onPress?: () => void
|
|
20
26
|
disabled?: boolean
|
|
21
27
|
layout?: LayoutProps
|
|
22
28
|
style?: StyleProps
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
// Minimum width per size preset (logical px). A minimum, not a hard width, so
|
|
32
|
+
// labels wider than the preset never clip.
|
|
33
|
+
const SIZE_WIDTH: Record<ButtonSize, number> = { sm: 88, md: 120, lg: 160 }
|
|
34
|
+
|
|
25
35
|
// Themed convenience over Pressable: a padded, centered, accent-colored box with
|
|
26
36
|
// a label. Press feedback is a slight scale, hover feedback a tint (non-touch
|
|
27
37
|
// interaction policies only), both driven through Pressable's reactive style so
|
|
@@ -30,30 +40,34 @@ export interface ButtonProps {
|
|
|
30
40
|
// its hover variant.
|
|
31
41
|
export function Button(props: ButtonProps) {
|
|
32
42
|
// Fill, hover fill, and label color per variant, read reactively from the
|
|
33
|
-
// theme.
|
|
43
|
+
// theme. No variant draws a border.
|
|
34
44
|
let colors = () => {
|
|
35
45
|
let c = theme.color
|
|
36
46
|
switch (props.variant ?? "primary") {
|
|
37
47
|
case "secondary":
|
|
38
|
-
return { fill: c.
|
|
48
|
+
return { fill: c.secondary, hover: c.secondaryHover, label: c.onSecondary }
|
|
39
49
|
case "ghost":
|
|
40
|
-
return { fill: "transparent", hover: c.surfaceHover, label: c.text
|
|
50
|
+
return { fill: "transparent", hover: c.surfaceHover, label: c.text }
|
|
41
51
|
case "danger":
|
|
42
|
-
return { fill: c.danger, hover: c.dangerHover, label: c.onPrimary
|
|
52
|
+
return { fill: c.danger, hover: c.dangerHover, label: c.onPrimary }
|
|
43
53
|
default:
|
|
44
|
-
return { fill: c.primary, hover: c.primaryHover, label: c.onPrimary
|
|
54
|
+
return { fill: c.primary, hover: c.primaryHover, label: c.onPrimary }
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
|
-
let
|
|
48
|
-
props.
|
|
49
|
-
(props.disabled
|
|
57
|
+
let idleFill = () =>
|
|
58
|
+
props.disabled
|
|
50
59
|
? props.variant === "ghost"
|
|
51
60
|
? "transparent"
|
|
52
61
|
: theme.color.surface
|
|
62
|
+
: colors().fill
|
|
63
|
+
let bg = (s: PressState) =>
|
|
64
|
+
props.style?.backgroundColor ??
|
|
65
|
+
(props.disabled
|
|
66
|
+
? idleFill()
|
|
53
67
|
: s.hovered && policy.interaction !== "touch"
|
|
54
68
|
? colors().hover
|
|
55
69
|
: colors().fill)
|
|
56
|
-
let radius = () => props.style?.borderRadius ?? theme.radius.
|
|
70
|
+
let radius = () => props.style?.borderRadius ?? theme.radius.md
|
|
57
71
|
let label = () => (props.disabled ? theme.color.textMuted : colors().label)
|
|
58
72
|
// Resolved once via children(): reading the raw children getter builds a new
|
|
59
73
|
// subtree per read, so the typeof probe and the two mount sites below must
|
|
@@ -63,12 +77,7 @@ export function Button(props: ButtonProps) {
|
|
|
63
77
|
// The label's polarity against the idle fill: onPrimary on a saturated fill
|
|
64
78
|
// is light-on-dark even in a light theme, so it needs the low-DPI weight
|
|
65
79
|
// compensation there too.
|
|
66
|
-
let labelOnDark = () =>
|
|
67
|
-
lightOnDark(
|
|
68
|
-
label(),
|
|
69
|
-
props.style?.backgroundColor ??
|
|
70
|
-
(props.disabled ? (props.variant === "ghost" ? "transparent" : theme.color.surface) : colors().fill),
|
|
71
|
-
)
|
|
80
|
+
let labelOnDark = () => lightOnDark(label(), props.style?.backgroundColor ?? idleFill())
|
|
72
81
|
|
|
73
82
|
return (
|
|
74
83
|
<Pressable
|
|
@@ -78,15 +87,14 @@ export function Button(props: ButtonProps) {
|
|
|
78
87
|
flexDirection: "row",
|
|
79
88
|
alignItems: "center",
|
|
80
89
|
justifyContent: "center",
|
|
81
|
-
paddingTop: space("
|
|
82
|
-
paddingBottom: space("
|
|
83
|
-
paddingLeft: space("
|
|
84
|
-
paddingRight: space("
|
|
90
|
+
paddingTop: space("md"),
|
|
91
|
+
paddingBottom: space("md"),
|
|
92
|
+
paddingLeft: space("lg"),
|
|
93
|
+
paddingRight: space("lg"),
|
|
94
|
+
...(props.size ? { minWidth: SIZE_WIDTH[props.size] } : { width: "100%" }),
|
|
85
95
|
...props.layout,
|
|
86
96
|
}}
|
|
87
97
|
style={(s: PressState) => ({
|
|
88
|
-
borderColor: colors().border,
|
|
89
|
-
borderWidth: colors().border != null ? theme.borderWidth.sm : undefined,
|
|
90
98
|
...props.style,
|
|
91
99
|
backgroundColor: bg(s),
|
|
92
100
|
borderRadius: radius(),
|
|
@@ -103,4 +111,4 @@ export function Button(props: ButtonProps) {
|
|
|
103
111
|
</Show>
|
|
104
112
|
</Pressable>
|
|
105
113
|
)
|
|
106
|
-
}
|
|
114
|
+
}
|
package/src/card.tsx
CHANGED
|
@@ -14,14 +14,14 @@ export interface CardProps {
|
|
|
14
14
|
style?: StyleProps
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// A themed surface container: a padded column box with
|
|
18
|
-
//
|
|
19
|
-
//
|
|
17
|
+
// A themed surface container: a padded column box with rounded corners, reading
|
|
18
|
+
// its colors from the theme so it recolors live. Borderless by default; pass
|
|
19
|
+
// style.borderWidth or style.borderColor to draw an outline. Override any paint
|
|
20
|
+
// via style, spacing/sizing via layout.
|
|
20
21
|
export function Card(props: CardProps) {
|
|
21
22
|
let bg = () => props.style?.backgroundColor ?? theme.color.surface
|
|
22
|
-
let border = () => props.style?.borderColor ?? theme.color.border
|
|
23
|
-
let width = () => props.style?.borderWidth ?? theme.borderWidth.sm
|
|
24
23
|
let radius = () => props.style?.borderRadius ?? theme.radius.lg
|
|
24
|
+
let hasBorder = () => props.style?.borderWidth != null || props.style?.borderColor != null
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
27
|
<view
|
|
@@ -44,7 +44,14 @@ export function Card(props: CardProps) {
|
|
|
44
44
|
</text>
|
|
45
45
|
</Show>
|
|
46
46
|
{props.children}
|
|
47
|
-
<
|
|
47
|
+
<Show when={hasBorder()}>
|
|
48
|
+
<d-rect
|
|
49
|
+
drawStyle="stroke"
|
|
50
|
+
color={props.style?.borderColor ?? theme.color.border}
|
|
51
|
+
strokeWidth={props.style?.borderWidth ?? theme.borderWidth.sm}
|
|
52
|
+
radius={radius()}
|
|
53
|
+
/>
|
|
54
|
+
</Show>
|
|
48
55
|
</view>
|
|
49
56
|
)
|
|
50
57
|
}
|
package/src/theme.ts
CHANGED
|
@@ -35,6 +35,11 @@ export type Theme = {
|
|
|
35
35
|
// Hover tint for primary-colored controls.
|
|
36
36
|
primaryHover: string
|
|
37
37
|
onPrimary: string
|
|
38
|
+
// Lower-emphasis accent: the puzzle mark's darker blue.
|
|
39
|
+
secondary: string
|
|
40
|
+
// Hover tint for secondary-colored controls.
|
|
41
|
+
secondaryHover: string
|
|
42
|
+
onSecondary: string
|
|
38
43
|
// Validation / destructive.
|
|
39
44
|
danger: string
|
|
40
45
|
// Hover tint for danger-colored controls.
|
|
@@ -75,9 +80,15 @@ export let darkTheme: Theme = {
|
|
|
75
80
|
// thin on low-DPI and its contrast depends on what sits behind it.
|
|
76
81
|
textMuted: mixColors("#e6edf3", "#0b0f17", 0.4),
|
|
77
82
|
border: "rgba(255,255,255,0.14)",
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
// Accent tuned to the puzzle mark's mid blue; hover lifts toward its
|
|
84
|
+
// lightest segment tone.
|
|
85
|
+
primary: "#547ebf",
|
|
86
|
+
primaryHover: "#7ea9ea",
|
|
80
87
|
onPrimary: "#ffffff",
|
|
88
|
+
// The darker shade of the same puzzle segment; hover lifts toward primary.
|
|
89
|
+
secondary: "#2b5696",
|
|
90
|
+
secondaryHover: "#3a68ab",
|
|
91
|
+
onSecondary: "#ffffff",
|
|
81
92
|
danger: "#f85149",
|
|
82
93
|
dangerHover: "#ff7b72",
|
|
83
94
|
scrim: "rgba(0,0,0,0.6)",
|
|
@@ -97,9 +108,15 @@ export let lightTheme: Theme = {
|
|
|
97
108
|
text: "#1f2328",
|
|
98
109
|
textMuted: mixColors("#1f2328", "#ffffff", 0.4),
|
|
99
110
|
border: "rgba(0,0,0,0.15)",
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
// Accent tuned to the puzzle mark's mid blue; hover deepens toward its
|
|
112
|
+
// darker segment tone.
|
|
113
|
+
primary: "#547ebf",
|
|
114
|
+
primaryHover: "#3f5494",
|
|
102
115
|
onPrimary: "#ffffff",
|
|
116
|
+
// The darker shade of the same puzzle segment; hover deepens it further.
|
|
117
|
+
secondary: "#2b5696",
|
|
118
|
+
secondaryHover: "#1f4176",
|
|
119
|
+
onSecondary: "#ffffff",
|
|
103
120
|
danger: "#cf222e",
|
|
104
121
|
dangerHover: "#a40e26",
|
|
105
122
|
scrim: "rgba(0,0,0,0.4)",
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,22 @@ export interface StyleProps {
|
|
|
17
17
|
y?: number
|
|
18
18
|
rotate?: number
|
|
19
19
|
scale?: number
|
|
20
|
+
// Per-axis scale; overrides `scale` on that axis (e.g. scaleX for a flip).
|
|
21
|
+
scaleX?: number
|
|
22
|
+
scaleY?: number
|
|
23
|
+
// 3D rotation in radians about the horizontal (rotateX) / vertical (rotateY)
|
|
24
|
+
// axis; reads as real depth only with `perspective` set.
|
|
25
|
+
rotateX?: number
|
|
26
|
+
rotateY?: number
|
|
27
|
+
// Perspective viewing distance in pixels, enabling 3D depth for rotateX/Y.
|
|
28
|
+
perspective?: number
|
|
29
|
+
// Transform origin (the pivot for scale/rotate), in pixels from the box's
|
|
30
|
+
// top-left. Defaults to the box center on each axis.
|
|
31
|
+
originX?: number
|
|
32
|
+
originY?: number
|
|
33
|
+
// Corner radii for the clip applied when overflow is non-visible (hidden,
|
|
34
|
+
// clip, scroll); a single number or [tl, tr, br, bl].
|
|
35
|
+
clipRadius?: number | [number, number, number, number]
|
|
20
36
|
opacity?: number
|
|
21
37
|
}
|
|
22
38
|
|
package/src/view.tsx
CHANGED
|
@@ -20,7 +20,15 @@ export function View(props: ViewProps) {
|
|
|
20
20
|
x={props.style?.x}
|
|
21
21
|
y={props.style?.y}
|
|
22
22
|
scale={props.style?.scale}
|
|
23
|
+
scaleX={props.style?.scaleX}
|
|
24
|
+
scaleY={props.style?.scaleY}
|
|
23
25
|
rotate={props.style?.rotate}
|
|
26
|
+
rotateX={props.style?.rotateX}
|
|
27
|
+
rotateY={props.style?.rotateY}
|
|
28
|
+
perspective={props.style?.perspective}
|
|
29
|
+
originX={props.style?.originX}
|
|
30
|
+
originY={props.style?.originY}
|
|
31
|
+
clipRadius={props.style?.clipRadius}
|
|
24
32
|
opacity={props.style?.opacity}
|
|
25
33
|
onPointerEnter={props.onPointerEnter}
|
|
26
34
|
onPointerLeave={props.onPointerLeave}
|