@jotis-me/react 0.1.0 → 0.2.0

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.
@@ -2,7 +2,7 @@ import { type AppIconHue, type AppIconShape, type AppIconVariant, type AppName }
2
2
  import type { CSSProperties } from "react";
3
3
  export type { AppIconHue, AppIconShape, AppIconVariant, AppName };
4
4
  /** Apps de la suite con su nombre, letras y tono. */
5
- export declare const appIcons: Record<"emptor" | "ornatus" | "pecunia" | "vinculum", import("@jotis-me/icons/app-icon").AppInfo>;
5
+ export declare const appIcons: Record<"emptor" | "ornatus" | "pecunia" | "regula" | "vinculum", import("@jotis-me/icons/app-icon").AppInfo>;
6
6
  export interface AppIconProps {
7
7
  /** App de la suite. Para una nueva, `letters` + `hue`. */
8
8
  app?: AppName;
@@ -9,5 +9,7 @@ export function BrowserFrame({ variant = "safari", url = "jotis.me", height, chi
9
9
  ? "h-6 bg-primary text-xs shadow-[inset_0_0_0_1px_var(--border-secondary)]"
10
10
  : "h-7 bg-secondary text-[13px] leading-[18px]"), children: [_jsx(Icon, { name: "lock-01", size: 12 }), _jsx("span", { className: "truncate", children: url })] }));
11
11
  const nav = "flex shrink-0 items-center gap-3 max-[560px]:hidden";
12
- return (_jsxs("div", { className: cx("overflow-hidden rounded-xl border border-secondary bg-primary font-body shadow-xl", className), style: style, children: [_jsxs("div", { className: cx("flex items-center gap-4 px-4 text-fg-quaternary shadow-[inset_0_-1px_0_var(--border-secondary)]", minimal ? "h-9 bg-secondary" : "h-11 bg-primary"), children: [_jsxs("div", { "aria-hidden": "true", className: "flex shrink-0 gap-2", children: [_jsx("span", { className: cx(dot, !minimal && "bg-[#ee6a5f]") }), _jsx("span", { className: cx(dot, !minimal && "bg-[#f5bd4f]") }), _jsx("span", { className: cx(dot, !minimal && "bg-[#61c454]") })] }), minimal ? (_jsxs(_Fragment, { children: [address, _jsx("span", { className: "w-[52px] shrink-0" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: nav, children: [_jsx(Icon, { name: "chevron-left", size: 18 }), _jsx(Icon, { name: "chevron-right", size: 18 })] }), address, _jsxs("div", { className: cx(nav, "ml-auto"), children: [_jsx(Icon, { name: "refresh-cw-01", size: 16 }), _jsx(Icon, { name: "plus", size: 18 }), _jsx(Icon, { name: "copy-01", size: 16 })] })] }))] }), _jsx("div", { className: "relative overflow-auto", style: height !== undefined ? { height } : undefined, children: children })] }));
12
+ return (_jsxs("div", { className: cx("overflow-hidden rounded-xl border border-secondary bg-primary font-body shadow-xl", className), style: style, children: [_jsxs("div", { className: cx("flex items-center gap-4 px-4 text-fg-quaternary shadow-[inset_0_-1px_0_var(--border-secondary)]", minimal ? "h-9 bg-secondary" : "h-11 bg-primary"), children: [_jsxs("div", { "aria-hidden": "true", className: "flex shrink-0 gap-2", children: [_jsx("span", { className: cx(dot, !minimal && "bg-[#ee6a5f]") }), _jsx("span", { className: cx(dot, !minimal && "bg-[#f5bd4f]") }), _jsx("span", { className: cx(dot, !minimal && "bg-[#61c454]") })] }), minimal ? (_jsxs(_Fragment, { children: [address, _jsx("span", { className: "w-[52px] shrink-0" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: nav, children: [_jsx(Icon, { name: "chevron-left", size: 18 }), _jsx(Icon, { name: "chevron-right", size: 18 })] }), address, _jsxs("div", { className: cx(nav, "ml-auto"), children: [_jsx(Icon, { name: "refresh-cw-01", size: 16 }), _jsx(Icon, { name: "plus", size: 18 }), _jsx(Icon, { name: "copy-01", size: 16 })] })] }))] }), _jsx("section", { "aria-label": url,
13
+ // biome-ignore lint/a11y/noNoninteractiveTabindex: el contenido puede desplazarse y necesita foco de teclado
14
+ tabIndex: 0, className: "relative overflow-auto focus-visible:shadow-[inset_0_0_0_2px_var(--focus-ring)] focus-visible:outline-none", style: height !== undefined ? { height } : undefined, children: children })] }));
13
15
  }
@@ -9,10 +9,18 @@ export function Checkbox({ label, hint, indeterminate, size = "sm", id: givenId,
9
9
  const autoId = useId();
10
10
  const id = givenId ?? autoId;
11
11
  const ref = useRef(null);
12
+ // La propiedad `indeterminate` no existe en HTML: se pone al montar, y `aria-checked="mixed"` con ella, para
13
+ // que el HTML de servidor no anuncie "mixed" en una casilla nativa que todavía no lo está
12
14
  useEffect(() => {
13
- if (ref.current)
14
- ref.current.indeterminate = !!indeterminate;
15
+ const input = ref.current;
16
+ if (!input)
17
+ return;
18
+ input.indeterminate = !!indeterminate;
19
+ if (indeterminate)
20
+ input.setAttribute("aria-checked", "mixed");
21
+ else
22
+ input.removeAttribute("aria-checked");
15
23
  }, [indeterminate]);
16
24
  const markSize = size === "md" ? 14 : 12;
17
- return (_jsxs("label", { htmlFor: id, className: cx(choiceLabel(size), className), children: [_jsx("input", { ref: ref, id: id, type: "checkbox", "aria-checked": indeterminate ? "mixed" : undefined, ...rest, className: hiddenInput }), _jsx("span", { "aria-hidden": "true", className: cx(choiceBox(size, false), "peer-checked:bg-brand-solid peer-checked:shadow-none peer-focus-visible:shadow-focus-ring peer-disabled:opacity-50", indeterminate && "bg-brand-solid shadow-none"), children: indeterminate ? (_jsx(Icon, { name: "minus", size: markSize, strokeWidth: 1.67 })) : (_jsx("span", { className: "hidden group-has-[:checked]:inline-flex", children: _jsx(Icon, { name: "check", size: markSize, strokeWidth: 1.67 }) })) }), _jsx(ChoiceText, { label: label, hint: hint, size: size, className: "peer-disabled:opacity-50" })] }));
25
+ return (_jsxs("label", { htmlFor: id, className: cx(choiceLabel(size), className), children: [_jsx("input", { ref: ref, id: id, type: "checkbox", ...rest, className: hiddenInput }), _jsx("span", { "aria-hidden": "true", className: cx(choiceBox(size, false), "peer-checked:bg-brand-solid peer-checked:shadow-none peer-focus-visible:shadow-focus-ring peer-disabled:opacity-50", indeterminate && "bg-brand-solid shadow-none"), children: indeterminate ? (_jsx(Icon, { name: "minus", size: markSize, strokeWidth: 1.67 })) : (_jsx("span", { className: "hidden group-has-[:checked]:inline-flex", children: _jsx(Icon, { name: "check", size: markSize, strokeWidth: 1.67 }) })) }), _jsx(ChoiceText, { label: label, hint: hint, size: size, className: "peer-disabled:opacity-50" })] }));
18
26
  }
@@ -9,7 +9,9 @@ export interface ColorPickerProps {
9
9
  swatchesLabel?: string;
10
10
  /** Nombre accesible del campo hexadecimal. @default "Hex" */
11
11
  hexLabel?: string;
12
+ /** Nombre accesible de la barra de tono. @default "Tono" */
13
+ hueLabel?: string;
12
14
  className?: string;
13
15
  }
14
16
  /** Selector de color: área de saturación y brillo, barra de tono, campo hex y muestras guardadas. */
15
- export declare function ColorPicker({ defaultValue, onChange, swatches, swatchesLabel, hexLabel, className, }: ColorPickerProps): import("react").JSX.Element;
17
+ export declare function ColorPicker({ defaultValue, onChange, swatches, swatchesLabel, hexLabel, hueLabel, className, }: ColorPickerProps): import("react").JSX.Element;
@@ -17,7 +17,7 @@ const HAIRLINE = "shadow-[inset_0_0_0_1px_#0000001a]";
17
17
  /** Selector de color: área de saturación y brillo, barra de tono, campo hex y muestras guardadas. */
18
18
  export function ColorPicker({
19
19
  // color-fijo: valor inicial, el índigo de marca de la vista previa (ficha de ColorPicker)
20
- defaultValue = "#4f39f6", onChange, swatches, swatchesLabel = defaultStrings.savedColors, hexLabel = defaultStrings.hex, className, }) {
20
+ defaultValue = "#4f39f6", onChange, swatches, swatchesLabel = defaultStrings.savedColors, hexLabel = defaultStrings.hex, hueLabel = defaultStrings.hue, className, }) {
21
21
  const [color, setColor] = useState(() => toHsb(defaultValue));
22
22
  // Lo que se está tecleando en el campo hex, hasta que sea un color válido
23
23
  const [typing, setTyping] = useState(null);
@@ -27,7 +27,7 @@ defaultValue = "#4f39f6", onChange, swatches, swatchesLabel = defaultStrings.sav
27
27
  setTyping(null);
28
28
  onChange?.(hexOf(next));
29
29
  };
30
- return (_jsx(I18nProvider, { locale: "es-ES", children: _jsxs("div", { className: cx("flex w-80 flex-col gap-4 rounded-2xl bg-primary_alt p-5 font-body shadow-[var(--shadow-xl),inset_0_0_0_1px_var(--border-secondary_alt)]", className), children: [_jsx(ColorArea, { value: color, onChange: set, xChannel: "saturation", yChannel: "brightness", className: "h-50 w-full cursor-crosshair touch-none rounded-lg", children: _jsx(ColorThumb, { className: THUMB }) }), _jsx(ColorSlider, { value: color, onChange: set, channel: "hue", colorSpace: "hsb", children: _jsx(SliderTrack, { className: "h-3 cursor-pointer touch-none rounded-full", children: _jsx(ColorThumb, { className: cx(THUMB, "top-1/2") }) }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": "true", className: cx("size-9 flex-none rounded-lg", HAIRLINE), style: { background: hex } }), _jsx(Input, { size: "sm", "aria-label": hexLabel, className: "flex-1", value: typing ?? hex.toUpperCase(), onChange: (e) => {
30
+ return (_jsx(I18nProvider, { locale: "es-ES", children: _jsxs("div", { className: cx("flex w-80 flex-col gap-4 rounded-2xl bg-primary_alt p-5 font-body shadow-[var(--shadow-xl),inset_0_0_0_1px_var(--border-secondary_alt)]", className), children: [_jsx(ColorArea, { value: color, onChange: set, xChannel: "saturation", yChannel: "brightness", className: "h-50 w-full cursor-crosshair touch-none rounded-lg", children: _jsx(ColorThumb, { className: THUMB }) }), _jsx(ColorSlider, { value: color, onChange: set, channel: "hue", colorSpace: "hsb", "aria-label": hueLabel, children: _jsx(SliderTrack, { className: "h-3 cursor-pointer touch-none rounded-full", children: _jsx(ColorThumb, { className: cx(THUMB, "top-1/2") }) }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": "true", className: cx("size-9 flex-none rounded-lg", HAIRLINE), style: { background: hex } }), _jsx(Input, { size: "sm", "aria-label": hexLabel, className: "flex-1", value: typing ?? hex.toUpperCase(), onChange: (e) => {
31
31
  const v = e.target.value;
32
32
  setTyping(v);
33
33
  if (HEX.test(v)) {
@@ -45,6 +45,7 @@ export declare const defaultStrings: {
45
45
  readonly writeMessage: "Escribe un mensaje";
46
46
  readonly savedColors: "Colores guardados";
47
47
  readonly hex: "Hex";
48
+ readonly hue: "Tono";
48
49
  readonly formatting: "Formato";
49
50
  readonly bold: "Negrita";
50
51
  readonly italic: "Cursiva";
@@ -47,6 +47,7 @@ export const defaultStrings = {
47
47
  writeMessage: "Escribe un mensaje",
48
48
  savedColors: "Colores guardados",
49
49
  hex: "Hex",
50
+ hue: "Tono",
50
51
  formatting: "Formato",
51
52
  bold: "Negrita",
52
53
  italic: "Cursiva",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jotis-me/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Componentes React de la suite jotis.me para Next.js y Astro, con Tailwind CSS v4.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@internationalized/date": "^3.12.4",
40
- "@jotis-me/icons": "0.1.0",
41
- "@jotis-me/tokens": "0.1.0",
40
+ "@jotis-me/icons": "0.2.0",
41
+ "@jotis-me/tokens": "0.2.0",
42
42
  "clsx": "^2.1.1",
43
43
  "react-aria-components": "^1.22.1",
44
44
  "tailwind-merge": "^3.7.0"