@phreshos/react-ui 0.1.36 → 0.1.38

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 (202) hide show
  1. package/README.md +147 -167
  2. package/dist/accordion.d.ts +7 -6
  3. package/dist/accordion.js +5 -5
  4. package/dist/alert-dialog.d.ts +2 -2
  5. package/dist/alert-dialog.js +3 -1
  6. package/dist/alert.d.ts +17 -0
  7. package/dist/alert.js +30 -0
  8. package/dist/app-layout.d.ts +13 -0
  9. package/dist/app-layout.js +84 -0
  10. package/dist/button.d.ts +31 -27
  11. package/dist/button.js +48 -40
  12. package/dist/calendar-layout.d.ts +9 -5
  13. package/dist/calendar-layout.js +67 -78
  14. package/dist/calendar-trigger.d.ts +4 -4
  15. package/dist/calendar-trigger.js +7 -29
  16. package/dist/calendar.d.ts +2 -2
  17. package/dist/calendar.js +6 -5
  18. package/dist/checkbox.d.ts +4 -4
  19. package/dist/checkbox.js +16 -6
  20. package/dist/color-area.d.ts +20 -0
  21. package/dist/color-area.js +30 -0
  22. package/dist/color-field.d.ts +17 -0
  23. package/dist/color-field.js +17 -0
  24. package/dist/color-picker.d.ts +24 -0
  25. package/dist/color-picker.js +30 -0
  26. package/dist/color-slider.d.ts +19 -0
  27. package/dist/color-slider.js +29 -0
  28. package/dist/color-swatch-picker.d.ts +23 -0
  29. package/dist/color-swatch-picker.js +36 -0
  30. package/dist/color-swatch.d.ts +18 -0
  31. package/dist/color-swatch.js +20 -0
  32. package/dist/combo-box.d.ts +18 -10
  33. package/dist/combo-box.js +43 -58
  34. package/dist/context-menu.d.ts +14 -10
  35. package/dist/context-menu.js +7 -6
  36. package/dist/control/color-thumb.d.ts +13 -0
  37. package/dist/control/color-thumb.js +33 -0
  38. package/dist/control/color-value.d.ts +6 -0
  39. package/dist/control/color-value.js +7 -0
  40. package/dist/control/control.d.ts +60 -0
  41. package/dist/control/control.js +71 -0
  42. package/dist/control/field-button.d.ts +13 -0
  43. package/dist/control/field-button.js +28 -0
  44. package/dist/control/field.d.ts +20 -0
  45. package/dist/control/field.js +34 -0
  46. package/dist/control/icon.d.ts +11 -0
  47. package/dist/control/icon.js +8 -0
  48. package/dist/control/item.d.ts +37 -0
  49. package/dist/control/item.js +57 -0
  50. package/dist/control/open-state.d.ts +25 -0
  51. package/dist/control/open-state.js +8 -0
  52. package/dist/control/selection-track.d.ts +39 -0
  53. package/dist/control/selection-track.js +122 -0
  54. package/dist/control/selection.d.ts +44 -0
  55. package/dist/{selection.js → control/selection.js} +16 -0
  56. package/dist/control/surface-render.d.ts +10 -0
  57. package/dist/control/surface-render.js +12 -0
  58. package/dist/control/text-control.d.ts +32 -0
  59. package/dist/control/text-control.js +31 -0
  60. package/dist/control/toggle.d.ts +32 -0
  61. package/dist/control/toggle.js +80 -0
  62. package/dist/date-control.d.ts +9 -9
  63. package/dist/date-control.js +31 -34
  64. package/dist/date-field.d.ts +7 -6
  65. package/dist/date-field.js +8 -6
  66. package/dist/date-picker.d.ts +13 -6
  67. package/dist/date-picker.js +9 -6
  68. package/dist/date-range-picker.d.ts +15 -6
  69. package/dist/date-range-picker.js +12 -9
  70. package/dist/dialog.d.ts +29 -15
  71. package/dist/dialog.js +37 -28
  72. package/dist/disclosure.d.ts +8 -7
  73. package/dist/disclosure.js +31 -52
  74. package/dist/drop-zone.d.ts +18 -0
  75. package/dist/drop-zone.js +32 -0
  76. package/dist/dropdown-menu.d.ts +11 -8
  77. package/dist/dropdown-menu.js +5 -4
  78. package/dist/fieldset.d.ts +13 -0
  79. package/dist/fieldset.js +26 -0
  80. package/dist/file-trigger.d.ts +13 -0
  81. package/dist/file-trigger.js +6 -0
  82. package/dist/flex.d.ts +1 -1
  83. package/dist/flex.js +5 -4
  84. package/dist/foundation/appearance.d.ts +83 -0
  85. package/dist/foundation/appearance.js +38 -0
  86. package/dist/{aria-direction.js → foundation/aria-direction.js} +3 -1
  87. package/dist/foundation/color.d.ts +61 -0
  88. package/dist/foundation/color.js +247 -0
  89. package/dist/{layout.d.ts → foundation/layout.d.ts} +6 -4
  90. package/dist/{layout.js → foundation/layout.js} +7 -5
  91. package/dist/foundation/motion-style.d.ts +9 -0
  92. package/dist/foundation/motion-style.js +50 -0
  93. package/dist/foundation/preferences.d.ts +15 -0
  94. package/dist/{ui-provider.d.ts → foundation/provider.d.ts} +9 -5
  95. package/dist/{ui-provider.js → foundation/provider.js} +18 -27
  96. package/dist/{radius.d.ts → foundation/radius.d.ts} +6 -3
  97. package/dist/foundation/radius.js +11 -0
  98. package/dist/foundation/spacing.d.ts +11 -0
  99. package/dist/foundation/spacing.js +4 -0
  100. package/dist/foundation/visual.d.ts +42 -0
  101. package/dist/{appearance-context.js → foundation/visual.js} +41 -27
  102. package/dist/grid.d.ts +1 -1
  103. package/dist/grid.js +5 -4
  104. package/dist/icons/main.d.ts +5 -3
  105. package/dist/icons/main.js +5 -3
  106. package/dist/input.d.ts +2 -2
  107. package/dist/input.js +9 -7
  108. package/dist/list-box.d.ts +24 -19
  109. package/dist/list-box.js +52 -109
  110. package/dist/main.d.ts +28 -17
  111. package/dist/main.js +22 -9
  112. package/dist/menu.d.ts +28 -32
  113. package/dist/menu.js +47 -93
  114. package/dist/number-field.d.ts +17 -0
  115. package/dist/number-field.js +28 -0
  116. package/dist/panel.d.ts +10 -5
  117. package/dist/panel.js +33 -12
  118. package/dist/popover.d.ts +26 -12
  119. package/dist/popover.js +22 -15
  120. package/dist/progress-bar.d.ts +8 -10
  121. package/dist/progress-bar.js +18 -34
  122. package/dist/radio.d.ts +15 -12
  123. package/dist/radio.js +27 -37
  124. package/dist/range-calendar.d.ts +5 -3
  125. package/dist/range-calendar.js +8 -7
  126. package/dist/scroll-area.d.ts +2 -2
  127. package/dist/scroll-area.js +10 -9
  128. package/dist/segmented-control.d.ts +23 -0
  129. package/dist/segmented-control.js +38 -0
  130. package/dist/select.d.ts +40 -13
  131. package/dist/select.js +63 -18
  132. package/dist/slider.d.ts +7 -3
  133. package/dist/slider.js +51 -50
  134. package/dist/spinner.d.ts +5 -8
  135. package/dist/spinner.js +12 -27
  136. package/dist/surface/grain.d.ts +12 -0
  137. package/dist/surface/grain.js +65 -0
  138. package/dist/{material-options.d.ts → surface/material-options.d.ts} +1 -2
  139. package/dist/{material-options.js → surface/material-options.js} +2 -2
  140. package/dist/surface/paint-class.d.ts +13 -0
  141. package/dist/surface/paint-class.js +47 -0
  142. package/dist/{shadow-options.d.ts → surface/shadow-options.d.ts} +13 -1
  143. package/dist/{shadow-options.js → surface/shadow-options.js} +17 -2
  144. package/dist/surface/surface.d.ts +80 -0
  145. package/dist/surface/surface.js +239 -0
  146. package/dist/switch.d.ts +4 -5
  147. package/dist/switch.js +17 -8
  148. package/dist/table.d.ts +29 -32
  149. package/dist/table.js +68 -106
  150. package/dist/tabs.d.ts +16 -15
  151. package/dist/tabs.js +44 -86
  152. package/dist/textarea.d.ts +2 -2
  153. package/dist/textarea.js +10 -14
  154. package/dist/time-field.d.ts +6 -6
  155. package/dist/time-field.js +8 -6
  156. package/dist/toolbar.d.ts +6 -4
  157. package/dist/toolbar.js +16 -15
  158. package/dist/tooltip.d.ts +17 -8
  159. package/dist/tooltip.js +17 -15
  160. package/dist/tree.d.ts +22 -27
  161. package/dist/tree.js +54 -99
  162. package/dist/window.d.ts +4 -4
  163. package/dist/window.js +18 -20
  164. package/package.json +3 -3
  165. package/dist/appearance-context.d.ts +0 -54
  166. package/dist/appearance.d.ts +0 -11
  167. package/dist/appearance.js +0 -11
  168. package/dist/color.d.ts +0 -69
  169. package/dist/color.js +0 -192
  170. package/dist/control-surface.d.ts +0 -20
  171. package/dist/control-surface.js +0 -11
  172. package/dist/control.d.ts +0 -70
  173. package/dist/control.js +0 -105
  174. package/dist/field-style.d.ts +0 -3
  175. package/dist/field-style.js +0 -12
  176. package/dist/material-paint.d.ts +0 -13
  177. package/dist/material-paint.js +0 -119
  178. package/dist/motion-style.d.ts +0 -13
  179. package/dist/motion-style.js +0 -75
  180. package/dist/preferences.d.ts +0 -12
  181. package/dist/radius.js +0 -11
  182. package/dist/readiness.d.ts +0 -37
  183. package/dist/readiness.js +0 -134
  184. package/dist/selection.d.ts +0 -18
  185. package/dist/spacing.d.ts +0 -8
  186. package/dist/spacing.js +0 -8
  187. package/dist/surface-edge.d.ts +0 -11
  188. package/dist/surface-edge.js +0 -55
  189. package/dist/surface.d.ts +0 -27
  190. package/dist/surface.js +0 -92
  191. package/dist/text-control.d.ts +0 -10
  192. package/dist/text-control.js +0 -0
  193. package/dist/toggle-indicator.d.ts +0 -18
  194. package/dist/toggle-indicator.js +0 -49
  195. /package/dist/{aria-direction.d.ts → foundation/aria-direction.d.ts} +0 -0
  196. /package/dist/{direction.d.ts → foundation/direction.d.ts} +0 -0
  197. /package/dist/{direction.js → foundation/direction.js} +0 -0
  198. /package/dist/{overlay-placement.d.ts → foundation/overlay-placement.d.ts} +0 -0
  199. /package/dist/{overlay-placement.js → foundation/overlay-placement.js} +0 -0
  200. /package/dist/{preferences.js → foundation/preferences.js} +0 -0
  201. /package/dist/{scale.d.ts → foundation/scale.d.ts} +0 -0
  202. /package/dist/{scale.js → foundation/scale.js} +0 -0
package/dist/color.js DELETED
@@ -1,192 +0,0 @@
1
- import { useMemo } from "react";
2
- import { useResolvedAppearance } from "./appearance-context.js";
3
- import ColorSpace from "colorjs.io/src/ColorSpace.js";
4
- import { mix } from "colorjs.io/src/interpolation.js";
5
- import parse from "colorjs.io/src/parse.js";
6
- import serialize from "colorjs.io/src/serialize.js";
7
- import to from "colorjs.io/src/to.js";
8
- import toGamut from "colorjs.io/src/toGamut.js";
9
- import sRGB from "colorjs.io/src/spaces/srgb.js";
10
- import sRGB_Linear from "colorjs.io/src/spaces/srgb-linear.js";
11
- import HSL from "colorjs.io/src/spaces/hsl.js";
12
- import HWB from "colorjs.io/src/spaces/hwb.js";
13
- import Lab from "colorjs.io/src/spaces/lab.js";
14
- import LCH from "colorjs.io/src/spaces/lch.js";
15
- import OKLab from "colorjs.io/src/spaces/oklab.js";
16
- import OKLCH from "colorjs.io/src/spaces/oklch.js";
17
- import P3 from "colorjs.io/src/spaces/p3.js";
18
- import A98RGB from "colorjs.io/src/spaces/a98rgb.js";
19
- import ProPhoto from "colorjs.io/src/spaces/prophoto.js";
20
- import REC_2020 from "colorjs.io/src/spaces/rec2020.js";
21
- import XYZ_D50 from "colorjs.io/src/spaces/xyz-d50.js";
22
- import XYZ_D65 from "colorjs.io/src/spaces/xyz-d65.js";
23
- // Register the CSS color spaces, without bundling unrelated color-model APIs.
24
- for (const space of [sRGB, sRGB_Linear, HSL, HWB, Lab, LCH, OKLab, OKLCH, P3, A98RGB, ProPhoto, REC_2020, XYZ_D50, XYZ_D65])
25
- ColorSpace.register(space);
26
- export const defaultColor = "background:base";
27
- const treatments = {
28
- subtle: { weight: 25, target: "white" },
29
- soft: { weight: 60, target: "white" },
30
- strong: { weight: 82, target: "black" },
31
- intense: { weight: 68, target: "black" }
32
- };
33
- /** Distinguishes derived color levels from direct CSS colors. */
34
- export function isColorLevel(value) {
35
- return value === "subtle" || value === "soft" || value === "base" || value === "strong" || value === "intense";
36
- }
37
- /** Derives visual treatments while preserving the concrete value at `base`. */
38
- export function color(value) {
39
- const shade = (level) => {
40
- const { weight, target } = treatments[level];
41
- return `color-mix(in oklch, ${value} ${weight}%, ${target})`;
42
- };
43
- return Object.freeze({
44
- subtle: shade("subtle"),
45
- soft: shade("soft"),
46
- base: value,
47
- strong: shade("strong"),
48
- intense: shade("intense")
49
- });
50
- }
51
- /** Resolves the same named treatment for solid paint and contrast calculations. */
52
- export function resolveColorLevel(value, level) {
53
- if (level === "base")
54
- return resolvableOpaqueColor(value) ?? value;
55
- const opaque = resolvableOpaqueColor(value);
56
- if (opaque == null)
57
- return color(value)[level];
58
- const { weight, target } = treatments[level];
59
- return opaqueColor(serialize(mix(opaque, target, 1 - weight / 100, { space: "oklch" })));
60
- }
61
- /** Returns the complete visual treatments derived from one concrete color. */
62
- export function useColor(value) {
63
- return useMemo(() => color(value), [value]);
64
- }
65
- /** Returns the readable Appearance content color for one semantic or CSS fill. */
66
- export function useContrastingColor(value) {
67
- const { colors } = useResolvedAppearance();
68
- return useMemo(() => contrastingColor(value, colors), [value, colors]);
69
- }
70
- /** Resolves a semantic color from an already selected Appearance branch. */
71
- export function resolveColor(value, colors) {
72
- const resolved = value ?? defaultColor;
73
- const semantic = parseSemanticColor(resolved, colors);
74
- return semantic ? color(semantic.source)[semantic.level] : resolved;
75
- }
76
- /** Resolves opaque control paint from an already selected Appearance branch. */
77
- export function resolveSolidColor(value, colors) {
78
- const semantic = parseSemanticColor(value, colors);
79
- return semantic ? resolveColorLevel(semantic.source, semantic.level) : resolvableOpaqueColor(value) ?? value;
80
- }
81
- /** Applies material opacity without restricting the source CSS color syntax. */
82
- export function colorOpacity(value, opacity) {
83
- const percentage = Math.round(opacity * 10_000) / 100;
84
- return `color-mix(in srgb, ${value} ${percentage}%, transparent)`;
85
- }
86
- /** Reads perceptual lightness from a resolved CSS color. */
87
- export function colorLightness(value) {
88
- // A missing CSS channel (`none`) contributes zero outside interpolation.
89
- return OKLCH.from(parse(value))[0] ?? 0;
90
- }
91
- /** Orders resolved CSS colors by perceptual lightness, never by their role. */
92
- export function orderColors(first, second) {
93
- const difference = colorLightness(first) - colorLightness(second);
94
- return difference > 0 || (difference === 0 && first >= second)
95
- ? { lighter: first, darker: second }
96
- : { lighter: second, darker: first };
97
- }
98
- /** Solid controls use opaque, gamut-mapped colors for both paint and contrast. */
99
- export function opaqueColor(value) {
100
- return serialize(toGamut(to({ ...parse(value), alpha: 1 }, "srgb")), { format: "rgb", precision: 6 });
101
- }
102
- /** Changes only perceptual lightness; theme names never determine direction. */
103
- export function colorShade(value, amount) {
104
- const result = to(value, "oklch");
105
- const lightness = result.coords[0] ?? 0;
106
- result.coords[0] = Math.max(0, Math.min(1, lightness + (lightness > 0.6 ? -amount : amount)));
107
- return opaqueColor(serialize(result));
108
- }
109
- /** Chooses the perceptually opposite Appearance color for the actual painted fill. */
110
- export function onColor(fill, background, foreground) {
111
- const first = opaqueColor(background);
112
- const second = opaqueColor(foreground);
113
- const candidates = orderColors(first, second);
114
- const darker = colorLightness(candidates.darker);
115
- const lighter = colorLightness(candidates.lighter);
116
- const threshold = darker + (lighter - darker) * 2 / 3;
117
- return colorLightness(opaqueColor(fill)) < threshold ? candidates.lighter : candidates.darker;
118
- }
119
- /** Chooses readable content when the fill can be evaluated, otherwise preserves Appearance foreground. */
120
- export function contrastingColor(value, colors) {
121
- try {
122
- return onColor(resolveSolidColor(value, colors), colors.background, colors.foreground);
123
- }
124
- catch {
125
- return colors.foreground;
126
- }
127
- }
128
- /** Solid controls start at base; interaction shades preserve its text choice. */
129
- export function solidColors(base, background, foreground) {
130
- const fill = resolveColorLevel(base, "base");
131
- const content = resolvableOnColor(fill, background, foreground) ?? foreground;
132
- const paint = (background) => ({ background, color: content });
133
- const concrete = resolvableOpaqueColor(fill);
134
- if (concrete == null)
135
- return {
136
- rest: paint(fill),
137
- hover: paint(cssInteractionColor(fill, foreground, 0.045)),
138
- pressed: paint(cssInteractionColor(fill, foreground, 0.085))
139
- };
140
- return {
141
- rest: paint(concrete),
142
- hover: paint(colorShade(concrete, 0.045)),
143
- pressed: paint(colorShade(concrete, 0.085))
144
- };
145
- }
146
- /** Low-emphasis interaction paints derived from the same component-owned base. */
147
- export function subtleColors(base, background, foreground) {
148
- const fill = resolveColorLevel(base, "base");
149
- const canvas = resolvableOpaqueColor(background) ?? background;
150
- const concrete = resolvableOpaqueColor(fill);
151
- const paint = (weight) => {
152
- const mixed = concrete == null
153
- ? `color-mix(in oklch, ${canvas} ${Math.round((1 - weight) * 10_000) / 100}%, ${fill})`
154
- : opaqueColor(serialize(mix(canvas, concrete, weight, { space: "oklch" })));
155
- return { background: mixed, color: resolvableOnColor(mixed, background, foreground) ?? foreground };
156
- };
157
- return {
158
- rest: paint(0.25),
159
- hover: paint(0.5),
160
- pressed: paint(0.65)
161
- };
162
- }
163
- function resolvableOpaqueColor(value) {
164
- try {
165
- return opaqueColor(value);
166
- }
167
- catch {
168
- return null;
169
- }
170
- }
171
- function resolvableOnColor(fill, background, foreground) {
172
- try {
173
- return onColor(fill, background, foreground);
174
- }
175
- catch {
176
- return null;
177
- }
178
- }
179
- function cssInteractionColor(base, target, amount) {
180
- return `color-mix(in oklch, ${base} ${Math.round((1 - amount) * 10_000) / 100}%, ${target})`;
181
- }
182
- function parseSemanticColor(value, colors) {
183
- const separator = value.indexOf(":");
184
- if (separator < 0 || value.indexOf(":", separator + 1) >= 0)
185
- return null;
186
- const name = value.slice(0, separator);
187
- const level = value.slice(separator + 1);
188
- const source = Reflect.get(colors, name);
189
- if (typeof source !== "string" || !isColorLevel(level))
190
- return null;
191
- return { source, level };
192
- }
@@ -1,20 +0,0 @@
1
- import type { ComponentPropsWithRef, CSSProperties, ReactNode } from "react";
2
- import type { MaterialOverrides } from "./material-options.js";
3
- import type { ShadowOverrides } from "./shadow-options.js";
4
- type Paint = Readonly<{
5
- background: string;
6
- color: string;
7
- }>;
8
- /** A native button uses Surface without changing its native contract. */
9
- export declare function SurfaceButton({ native, paint, material: options, shadow }: {
10
- readonly native: ComponentPropsWithRef<"button">;
11
- readonly paint: Paint;
12
- } & MaterialOverrides & ShadowOverrides): import("react").JSX.Element;
13
- /** Void text controls use a material host while retaining native sizing. */
14
- export declare function SurfaceField({ paint, radius, children, material: options, shadow, style }: Readonly<{
15
- paint: Paint;
16
- radius: CSSProperties["borderRadius"];
17
- children: ReactNode;
18
- style?: CSSProperties;
19
- } & MaterialOverrides & ShadowOverrides>): import("react").JSX.Element;
20
- export {};
@@ -1,11 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Surface } from "./surface.js";
3
- /** A native button uses Surface without changing its native contract. */
4
- export function SurfaceButton({ native, paint, material: options, shadow }) {
5
- const { ref, style, children, ...properties } = native;
6
- return _jsx(Surface, { ...properties, as: "button", color: paint.background, material: options, shadow: shadow, ref: ref, style: { ...style, color: paint.color }, children: children });
7
- }
8
- /** Void text controls use a material host while retaining native sizing. */
9
- export function SurfaceField({ paint, radius, children, material: options, shadow, style }) {
10
- return _jsx(Surface, { as: "span", color: paint.background, material: options, shadow: shadow, radius: radius, style: { color: paint.color, display: "grid", minWidth: 0, ...style }, children: children });
11
- }
package/dist/control.d.ts DELETED
@@ -1,70 +0,0 @@
1
- import type { CSSProperties, ReactNode } from "react";
2
- import { type RadiusProps } from "./radius.js";
3
- import { type ScaleLevel } from "./scale.js";
4
- import { solidColors } from "./color.js";
5
- import { type Color } from "./color.js";
6
- import type { Transition } from "motion/react";
7
- /** Semantic Appearance colors accepted by interactive controls. */
8
- export type ControlColor = Color;
9
- export interface ControlProps {
10
- readonly size?: ScaleLevel;
11
- readonly color?: ControlColor;
12
- readonly className?: string;
13
- readonly style?: CSSProperties;
14
- readonly disabled?: boolean;
15
- }
16
- export interface FieldProps {
17
- /** Visible accessible label. Otherwise supply aria-label or aria-labelledby. */
18
- readonly label?: ReactNode;
19
- readonly description?: ReactNode;
20
- readonly errorMessage?: ReactNode;
21
- readonly required?: boolean;
22
- readonly invalid?: boolean;
23
- }
24
- /** Names owned by our contract, not a second set of primitive-specific aliases. */
25
- export type ControlOverrides = keyof ControlProps | keyof FieldProps | "children" | "isDisabled" | "isRequired" | "isInvalid";
26
- export declare const controlFontSizes: Readonly<Record<ScaleLevel, CSSProperties["fontSize"]>>;
27
- /** Shared interaction attenuation, derived consistently across control families. */
28
- export declare const controlOpacity: Readonly<{
29
- disabled: 0.46;
30
- pending: 0.68;
31
- secondary: 0.7;
32
- placeholder: 0.6;
33
- separator: 0.14;
34
- }>;
35
- /** Shared emphasis used by labels and control-owned headings. */
36
- export declare const controlFontWeight = 600;
37
- type SolidColors = ReturnType<typeof solidColors>;
38
- export interface ControlTheme {
39
- readonly transition: CSSProperties;
40
- readonly motionTransition: Transition;
41
- readonly animations: boolean;
42
- readonly spacing: number;
43
- readonly foreground: string;
44
- readonly background: string;
45
- readonly tint: string;
46
- readonly focusColor: string;
47
- readonly danger: string;
48
- readonly paints: {
49
- readonly palette: SolidColors;
50
- readonly subtle: SolidColors;
51
- readonly danger: SolidColors;
52
- };
53
- readonly radius: CSSProperties["borderRadius"];
54
- readonly fontSize: CSSProperties["fontSize"];
55
- readonly indicatorSize: number;
56
- readonly height: number;
57
- readonly gap: number;
58
- }
59
- export declare function useControlTheme({ size, color, radius }: ControlProps & RadiusProps): ControlTheme;
60
- export declare function fieldStyle(theme: ControlTheme, disabled?: boolean, style?: CSSProperties): CSSProperties;
61
- export declare function controlPaint(theme: ControlTheme, focused: boolean, invalid: boolean, hovered?: boolean): {
62
- background: string;
63
- color: string;
64
- };
65
- export declare function controlStyle(theme: ControlTheme, focused: boolean, invalid: boolean, hovered?: boolean, focusVisible?: boolean): CSSProperties;
66
- export declare function FieldLabel({ label }: Pick<FieldProps, "label">): import("react").JSX.Element | null;
67
- export declare function FieldFeedback({ description, errorMessage, theme }: Pick<FieldProps, "description" | "errorMessage"> & {
68
- readonly theme: ControlTheme;
69
- }): import("react").JSX.Element;
70
- export {};
package/dist/control.js DELETED
@@ -1,105 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo } from "react";
3
- import { FieldError, Label, Text } from "react-aria-components";
4
- import { useResolvedAppearance } from "./appearance-context.js";
5
- import { resolveRadius } from "./radius.js";
6
- import { scale } from "./scale.js";
7
- import { colorOpacity, solidColors, subtleColors } from "./color.js";
8
- import { resolveSolidColor } from "./color.js";
9
- import { controlTransition, visualTransition } from "./motion-style.js";
10
- export const controlFontSizes = Object.freeze({
11
- xsmall: "0.6875em",
12
- small: "0.75em",
13
- medium: "0.8125em",
14
- large: "0.875em",
15
- xlarge: "0.9375em"
16
- });
17
- /** Shared interaction attenuation, derived consistently across control families. */
18
- export const controlOpacity = Object.freeze({
19
- disabled: 0.46,
20
- pending: 0.68,
21
- secondary: 0.7,
22
- placeholder: 0.6,
23
- separator: 0.14
24
- });
25
- /** Shared emphasis used by labels and control-owned headings. */
26
- export const controlFontWeight = 600;
27
- export function useControlTheme({ size = "medium", color, radius = "medium" }) {
28
- const resolved = useResolvedAppearance();
29
- const { appearance, colors } = resolved;
30
- const transition = useMemo(() => visualTransition(resolved.transaction, resolved.preferences.animations), [resolved.transaction, resolved.preferences.animations]);
31
- const motionTransition = useMemo(() => controlTransition(resolved.transaction, resolved.preferences.animations), [resolved.transaction, resolved.preferences.animations]);
32
- const spacing = scale(appearance.spacing, size);
33
- const foreground = colors.foreground;
34
- const background = colors.background;
35
- // One owner-provided base drives every ordinary interaction state; adding
36
- // hidden state colors here would make the public color contract incomplete.
37
- const tint = resolveSolidColor(color ?? "default:base", colors);
38
- const danger = resolveSolidColor("danger:base", colors);
39
- const paints = useMemo(() => ({
40
- palette: solidColors(tint, background, foreground),
41
- subtle: subtleColors(tint, background, foreground),
42
- danger: solidColors(danger, background, foreground)
43
- }), [tint, background, foreground, danger]);
44
- return useMemo(() => ({
45
- transition,
46
- motionTransition,
47
- animations: resolved.preferences.animations,
48
- spacing,
49
- foreground,
50
- background,
51
- tint,
52
- focusColor: colorOpacity(tint, 0.2),
53
- danger,
54
- paints,
55
- radius: resolveRadius(radius, appearance),
56
- fontSize: controlFontSizes[size],
57
- indicatorSize: Math.max(16, 16 + spacing / 4),
58
- height: Math.max(24, 24 + spacing),
59
- gap: Math.max(4, spacing / 2)
60
- }), [appearance, colors, transition, motionTransition, resolved.preferences.animations, spacing, foreground, background, tint, danger, paints, radius, size]);
61
- }
62
- export function fieldStyle(theme, disabled = false, style) {
63
- return {
64
- ...style,
65
- ...theme.transition,
66
- display: "grid",
67
- gap: theme.gap,
68
- minWidth: 0,
69
- fontFamily: "inherit",
70
- fontSize: theme.fontSize,
71
- lineHeight: 1.5,
72
- opacity: disabled ? controlOpacity.disabled : 1
73
- };
74
- }
75
- export function controlPaint(theme, focused, invalid, hovered = false) {
76
- const paints = invalid ? theme.paints.danger : theme.paints.palette;
77
- return focused ? paints.pressed : hovered ? paints.hover : paints.rest;
78
- }
79
- export function controlStyle(theme, focused, invalid, hovered = false, focusVisible = focused) {
80
- const paint = controlPaint(theme, focused, invalid, hovered);
81
- return {
82
- ...theme.transition,
83
- appearance: "none",
84
- boxSizing: "border-box",
85
- width: "100%",
86
- minWidth: 0,
87
- height: theme.height,
88
- paddingBlock: 0,
89
- paddingInline: Math.max(8, theme.spacing),
90
- border: 0,
91
- borderRadius: theme.radius,
92
- outline: focusVisible ? `1px solid ${theme.focusColor}` : "none",
93
- outlineOffset: 1,
94
- ...paint,
95
- caretColor: paint.color,
96
- font: "inherit",
97
- lineHeight: 1.5
98
- };
99
- }
100
- export function FieldLabel({ label }) {
101
- return label == null ? null : _jsx(Label, { style: { fontWeight: controlFontWeight }, children: label });
102
- }
103
- export function FieldFeedback({ description, errorMessage, theme }) {
104
- return _jsxs(_Fragment, { children: [description != null && _jsx(Text, { slot: "description", style: { fontSize: "0.92em", opacity: controlOpacity.secondary }, children: description }), _jsx(FieldError, { style: { fontSize: "0.92em", color: theme.danger }, children: errorMessage })] });
105
- }
@@ -1,3 +0,0 @@
1
- export declare const textControlClass = "phreshos-ui-text-control";
2
- /** Placeholder contrast follows the control's chosen text color, not browser gray. */
3
- export default function FieldStyle(): import("react").JSX.Element;
@@ -1,12 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { controlOpacity } from "./control.js";
3
- export const textControlClass = "phreshos-ui-text-control";
4
- /** Placeholder contrast follows the control's chosen text color, not browser gray. */
5
- export default function FieldStyle() {
6
- return _jsx("style", { href: "phreshos-react-ui-fields", precedence: "phreshos", children: `
7
- .phreshos-ui-text-control::placeholder {
8
- color: inherit;
9
- opacity: ${controlOpacity.placeholder};
10
- }
11
- ` });
12
- }
@@ -1,13 +0,0 @@
1
- import type { CSSProperties } from "react";
2
- interface MaterialPaintProps {
3
- readonly color: string;
4
- readonly distortion: number;
5
- readonly grain: number;
6
- readonly grainAmount: number;
7
- readonly identity: string;
8
- readonly opacity: number;
9
- readonly transition: CSSProperties;
10
- }
11
- /** The fill, refraction definition, and grain of one Surface. */
12
- export declare function MaterialPaint({ color, distortion, grain, grainAmount, identity, opacity, transition }: MaterialPaintProps): import("react").JSX.Element | null;
13
- export {};
@@ -1,119 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo } from "react";
3
- /** The fill, refraction definition, and grain of one Surface. */
4
- export function MaterialPaint({ color, distortion, grain, grainAmount, identity, opacity, transition }) {
5
- const grainSeed = useMemo(() => grainSeeds[seedFrom(identity) % grainSeeds.length] ?? grainSeeds[0], [identity]);
6
- const hasPaint = opacity > 0;
7
- const hasGrain = hasPaint && grain > 0 && grainAmount > 0;
8
- const hasDistortion = distortion > 0;
9
- const grainResource = useMemo(() => hasGrain ? grainImage(grainSeed, grainAmount) : null, [grainSeed, grainAmount, hasGrain]);
10
- if (!hasPaint && !hasDistortion)
11
- return null;
12
- return _jsxs("span", { "data-material-paint": "", "aria-hidden": "true", style: {
13
- position: "absolute",
14
- zIndex: -1,
15
- inset: 0,
16
- display: "block",
17
- width: "100%",
18
- height: "100%",
19
- overflow: "hidden",
20
- borderRadius: "inherit",
21
- pointerEvents: "none"
22
- }, children: [hasDistortion && _jsx("svg", { width: "0", height: "0", focusable: "false", style: { position: "absolute" }, children: _jsx("defs", { children: _jsx(DistortionFilter, { distortion: distortion, identity: identity }) }) }), hasPaint && _jsx("span", { "data-material-fill": "", "data-material-base": "", style: {
23
- position: "absolute",
24
- inset: 0,
25
- borderRadius: "inherit",
26
- background: color,
27
- opacity,
28
- ...transition,
29
- transitionProperty: "background-color, opacity"
30
- }, children: hasGrain && _jsx("span", { "data-material-grain": "", style: {
31
- position: "absolute",
32
- inset: 0,
33
- borderRadius: "inherit",
34
- backgroundImage: grainResource ?? undefined,
35
- backgroundRepeat: "repeat",
36
- backgroundSize: `${patternSize}px ${patternSize}px`,
37
- opacity: grain,
38
- ...transition,
39
- transitionProperty: "opacity"
40
- } }) })] });
41
- }
42
- function DistortionFilter({ distortion, identity }) {
43
- return _jsxs("filter", { id: `${identity}-distortion`, "data-material-distortion": "", x: "-20%", y: "-20%", width: "140%", height: "140%", colorInterpolationFilters: "sRGB", children: [_jsx("feTurbulence", { "data-material-distortion-noise": "", "data-material-distortion-field": "organic", type: "fractalNoise", baseFrequency: "0.008 0.008", numOctaves: 2, seed: 92, result: `${identity}-organic-noise` }), _jsx("feGaussianBlur", { in: `${identity}-organic-noise`, stdDeviation: 2, result: `${identity}-organic-noise-blurred` }), _jsx("feDisplacementMap", { "data-material-distortion-stage": "organic", in: "SourceGraphic", in2: `${identity}-organic-noise-blurred`, scale: distortion, xChannelSelector: "R", yChannelSelector: "G" })] });
44
- }
45
- function grainImage(seed, amount) {
46
- const key = `${seed}:${amount}`;
47
- const cached = grainResources.get(key);
48
- if (cached !== undefined) {
49
- grainResources.delete(key);
50
- grainResources.set(key, cached);
51
- return cached;
52
- }
53
- const paths = grainPaths(seed, amount)
54
- .map((path, tone) => path
55
- ? `<path d="${path}" fill="rgb(${toneChannel(tone)} ${toneChannel(tone)} ${toneChannel(tone)})"/>`
56
- : "")
57
- .join("");
58
- const resource = `url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="${patternSize}" height="${patternSize}" viewBox="0 0 ${patternSize} ${patternSize}">${paths}</svg>`)}")`;
59
- // Preserve the exact texture while avoiding the same expensive SVG generation
60
- // for every Surface. The bound prevents live grain controls from growing it forever.
61
- if (grainResources.size >= grainResourceLimit) {
62
- const oldest = grainResources.keys().next().value;
63
- if (oldest !== undefined)
64
- grainResources.delete(oldest);
65
- }
66
- grainResources.set(key, resource);
67
- return resource;
68
- }
69
- /** Preserve the original contract: amount is the monotonic density of visible grain pixels. */
70
- function grainPaths(seed, amount) {
71
- const tones = Array.from({ length: toneCount }, () => []);
72
- for (let y = 0; y < patternSize; y += 1) {
73
- for (let x = 0; x < patternSize; x += 1) {
74
- const pointX = x + seed * 41;
75
- const pointY = y + seed * 17;
76
- const presence = shaderHash(pointX + 71.9, pointY + 13.7);
77
- if (presence > amount)
78
- continue;
79
- const fine = shaderHash(Math.floor(pointX * 1.18), Math.floor(pointY * 1.18));
80
- const clustered = shaderHash(Math.floor(pointX * 0.47) + 31.7, Math.floor(pointY * 0.47) + 31.7);
81
- const value = clamp(fine * 0.8 + clustered * 0.2, 0, 1);
82
- const tone = Math.min(toneCount - 1, Math.floor(value * toneCount));
83
- tones[tone]?.push(`M${x} ${y}h1v1h-1z`);
84
- }
85
- }
86
- return tones.map(tone => tone.join(""));
87
- }
88
- function toneChannel(tone) {
89
- return Math.round(tone / (toneCount - 1) * 255);
90
- }
91
- function shaderHash(x, y) {
92
- let red = fract(x * 0.1031);
93
- let green = fract(y * 0.1031);
94
- let blue = fract(x * 0.1031);
95
- const product = red * (green + 33.33) + green * (blue + 33.33) + blue * (red + 33.33);
96
- red += product;
97
- green += product;
98
- blue += product;
99
- return fract((red + green) * blue);
100
- }
101
- function seedFrom(value) {
102
- let seed = 2166136261;
103
- for (const character of value) {
104
- seed ^= character.codePointAt(0) ?? 0;
105
- seed = Math.imul(seed, 16777619);
106
- }
107
- return (seed >>> 0) % 997 + 1;
108
- }
109
- function fract(value) {
110
- return value - Math.floor(value);
111
- }
112
- function clamp(value, minimum, maximum) {
113
- return Math.min(maximum, Math.max(minimum, value));
114
- }
115
- const patternSize = 64;
116
- const toneCount = 16;
117
- const grainSeeds = [19, 47, 83, 131];
118
- const grainResourceLimit = 64;
119
- const grainResources = new Map();
@@ -1,13 +0,0 @@
1
- import type { CSSProperties } from "react";
2
- import type { Transition } from "motion/react";
3
- import type { AppearanceTransaction } from "./appearance.js";
4
- export declare function controlTransition(transaction: AppearanceTransaction, animations: boolean): Transition;
5
- /** Per-overlay variables consumed by the shared entrance keyframes. */
6
- export declare function overlayTransition(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
7
- export declare const overlayMotionClass = "phreshos-ui-overlay";
8
- export declare const backdropMotionClass = "phreshos-ui-backdrop";
9
- export declare function transitionTiming(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
10
- export declare function visualTransition(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
11
- export declare function paintTransition(transaction: AppearanceTransaction, animations: boolean): CSSProperties;
12
- /** React hoists and deduplicates this stylesheet, including portals. */
13
- export default function MotionStyle(): import("react").JSX.Element;
@@ -1,75 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- const visualProperties = "background-color, color, border-color, border-radius, box-shadow";
3
- const paintProperties = "fill, stroke, opacity";
4
- export function controlTransition(transaction, animations) {
5
- return {
6
- type: "tween",
7
- duration: animations ? transaction.duration / 1_000 : 0,
8
- ease: motionEasing(transaction.easing)
9
- };
10
- }
11
- /** Per-overlay variables consumed by the shared entrance keyframes. */
12
- export function overlayTransition(transaction, animations) {
13
- return {
14
- "--phreshos-ui-motion-duration": `${animations ? transaction.duration : 0}ms`,
15
- "--phreshos-ui-motion-easing": cssEasing(transaction.easing)
16
- };
17
- }
18
- export const overlayMotionClass = "phreshos-ui-overlay";
19
- export const backdropMotionClass = "phreshos-ui-backdrop";
20
- export function transitionTiming(transaction, animations) {
21
- return {
22
- transitionDuration: `${animations ? transaction.duration : 0}ms`,
23
- transitionTimingFunction: cssEasing(transaction.easing)
24
- };
25
- }
26
- export function visualTransition(transaction, animations) {
27
- return { ...transitionTiming(transaction, animations), transitionProperty: visualProperties };
28
- }
29
- export function paintTransition(transaction, animations) {
30
- return { ...transitionTiming(transaction, animations), transitionProperty: paintProperties };
31
- }
32
- function cssEasing(easing) {
33
- return typeof easing === "string" ? easing : `cubic-bezier(${easing.join(", ")})`;
34
- }
35
- function motionEasing(easing) {
36
- if (typeof easing !== "string")
37
- return [...easing];
38
- if (easing === "ease")
39
- return [0.25, 0.1, 0.25, 1];
40
- if (easing === "ease-in")
41
- return "easeIn";
42
- if (easing === "ease-out")
43
- return "easeOut";
44
- if (easing === "ease-in-out")
45
- return "easeInOut";
46
- return easing;
47
- }
48
- const stylesheet = `
49
- .phreshos-ui-overlay[data-entering] {
50
- animation: phreshos-ui-overlay-enter var(--phreshos-ui-motion-duration) var(--phreshos-ui-motion-easing) both;
51
- }
52
- .phreshos-ui-overlay[data-exiting] {
53
- animation: phreshos-ui-overlay-enter var(--phreshos-ui-motion-duration) var(--phreshos-ui-motion-easing) reverse both;
54
- pointer-events: none;
55
- }
56
- .phreshos-ui-backdrop[data-entering] {
57
- animation: phreshos-ui-backdrop-enter var(--phreshos-ui-motion-duration) var(--phreshos-ui-motion-easing) both;
58
- }
59
- .phreshos-ui-backdrop[data-exiting] {
60
- animation: phreshos-ui-backdrop-enter var(--phreshos-ui-motion-duration) var(--phreshos-ui-motion-easing) reverse both;
61
- pointer-events: none;
62
- }
63
- @keyframes phreshos-ui-overlay-enter {
64
- from { opacity: 0; scale: 1.05; }
65
- to { opacity: 1; scale: 1; }
66
- }
67
- @keyframes phreshos-ui-backdrop-enter {
68
- from { opacity: 0; }
69
- to { opacity: 1; }
70
- }
71
- `;
72
- /** React hoists and deduplicates this stylesheet, including portals. */
73
- export default function MotionStyle() {
74
- return _jsx("style", { href: "phreshos-react-ui-motion", precedence: "phreshos", children: stylesheet });
75
- }
@@ -1,12 +0,0 @@
1
- import type { DesktopPreferences } from "@phreshos/core";
2
- /** Complete environment preferences consumed by React UI. */
3
- export type Preferences = Pick<DesktopPreferences, "theme" | "animations">;
4
- /** At least one preference merged with the nearest UIProvider. */
5
- export type PreferencesUpdate = Readonly<{
6
- theme: Preferences["theme"];
7
- animations?: Preferences["animations"];
8
- }> | Readonly<{
9
- theme?: Preferences["theme"];
10
- animations: Preferences["animations"];
11
- }>;
12
- export type Theme = Preferences["theme"];