@realiizlabs/admin 0.10.2 → 0.11.1

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.
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { C as ContentTypeEntry } from '../types-DyryuIX1.cjs';
3
+ import { C as ContentTypeEntry } from '../types-D46UjOfv.cjs';
4
4
  import 'zod';
5
5
 
6
6
  /**
@@ -189,6 +189,22 @@ interface ImageInputProps {
189
189
  }
190
190
  declare function ImageInput(p: ImageInputProps): react.JSX.Element;
191
191
 
192
+ interface SelectInputProps {
193
+ id: string;
194
+ name: string;
195
+ value: string;
196
+ options: string[];
197
+ /** Optional display names, keyed by value. */
198
+ labels?: Record<string, string>;
199
+ /** Adds a "—" choice at the top that sets the value to "". */
200
+ allowEmpty?: boolean;
201
+ invalid?: boolean;
202
+ disabled?: boolean;
203
+ onChange: (v: string) => void;
204
+ onBlur?: () => void;
205
+ }
206
+ declare function SelectInput(p: SelectInputProps): react.JSX.Element;
207
+
192
208
  /**
193
209
  * ChipsInput — tags as chips. Enter or comma adds, × or Backspace-on-empty
194
210
  * removes. The value is a string[]; the schema's length/regex rules still run
@@ -206,4 +222,4 @@ declare function ChipsInput({ id, name, value, onChange, onBlur, placeholder, in
206
222
  max?: number;
207
223
  }): react.JSX.Element;
208
224
 
209
- export { ChipsInput, ContentForm, type ContentFormProps, type ContentFormSubmission, IMAGE_HINT, ImageInput, type ImageInputProps, type LabelRenderer, LengthGauge, MarkdownEditor, type MarkdownEditorProps, type PickImage, type PickResult, type PickedImage, type SubmittedImages, TOOLS, slugify };
225
+ export { ChipsInput, ContentForm, type ContentFormProps, type ContentFormSubmission, IMAGE_HINT, ImageInput, type ImageInputProps, type LabelRenderer, LengthGauge, MarkdownEditor, type MarkdownEditorProps, type PickImage, type PickResult, type PickedImage, SelectInput, type SelectInputProps, type SubmittedImages, TOOLS, slugify };
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { C as ContentTypeEntry } from '../types-DyryuIX1.js';
3
+ import { C as ContentTypeEntry } from '../types-D46UjOfv.js';
4
4
  import 'zod';
5
5
 
6
6
  /**
@@ -189,6 +189,22 @@ interface ImageInputProps {
189
189
  }
190
190
  declare function ImageInput(p: ImageInputProps): react.JSX.Element;
191
191
 
192
+ interface SelectInputProps {
193
+ id: string;
194
+ name: string;
195
+ value: string;
196
+ options: string[];
197
+ /** Optional display names, keyed by value. */
198
+ labels?: Record<string, string>;
199
+ /** Adds a "—" choice at the top that sets the value to "". */
200
+ allowEmpty?: boolean;
201
+ invalid?: boolean;
202
+ disabled?: boolean;
203
+ onChange: (v: string) => void;
204
+ onBlur?: () => void;
205
+ }
206
+ declare function SelectInput(p: SelectInputProps): react.JSX.Element;
207
+
192
208
  /**
193
209
  * ChipsInput — tags as chips. Enter or comma adds, × or Backspace-on-empty
194
210
  * removes. The value is a string[]; the schema's length/regex rules still run
@@ -206,4 +222,4 @@ declare function ChipsInput({ id, name, value, onChange, onBlur, placeholder, in
206
222
  max?: number;
207
223
  }): react.JSX.Element;
208
224
 
209
- export { ChipsInput, ContentForm, type ContentFormProps, type ContentFormSubmission, IMAGE_HINT, ImageInput, type ImageInputProps, type LabelRenderer, LengthGauge, MarkdownEditor, type MarkdownEditorProps, type PickImage, type PickResult, type PickedImage, type SubmittedImages, TOOLS, slugify };
225
+ export { ChipsInput, ContentForm, type ContentFormProps, type ContentFormSubmission, IMAGE_HINT, ImageInput, type ImageInputProps, type LabelRenderer, LengthGauge, MarkdownEditor, type MarkdownEditorProps, type PickImage, type PickResult, type PickedImage, SelectInput, type SelectInputProps, type SubmittedImages, TOOLS, slugify };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { walkSchema } from '../chunk-J6FI73PF.js';
3
- import { FORM_CSS } from '../chunk-FTD4WE3V.js';
4
- import { useState, useRef, useCallback, useEffect, useId, useMemo } from 'react';
2
+ import { walkSchema, titlecase } from '../chunk-JLR5K6RP.js';
3
+ import { FORM_CSS } from '../chunk-2GSYBARR.js';
4
+ import { useState, useRef, useId, useEffect, useCallback, useMemo } from 'react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
6
 
7
7
  function ChipsInput({ id, name, value, onChange, onBlur, placeholder, invalid, max }) {
@@ -48,6 +48,128 @@ function ChipsInput({ id, name, value, onChange, onBlur, placeholder, invalid, m
48
48
  ] })
49
49
  ] });
50
50
  }
51
+ function labelFor(value, labels) {
52
+ if (labels?.[value]) return labels[value];
53
+ return value ? titlecase(value) : "\u2014";
54
+ }
55
+ function SelectInput(p) {
56
+ const [open, setOpen] = useState(false);
57
+ const [active, setActive] = useState(0);
58
+ const wrap = useRef(null);
59
+ const list = useRef(null);
60
+ const listId = useId();
61
+ const choices = p.allowEmpty ? ["", ...p.options] : p.options;
62
+ const latest = useRef({ choices, value: p.value });
63
+ latest.current = { choices, value: p.value };
64
+ useEffect(() => {
65
+ if (!open) return;
66
+ setActive(Math.max(0, latest.current.choices.indexOf(latest.current.value)));
67
+ const onDoc = (e) => {
68
+ if (!wrap.current?.contains(e.target)) close();
69
+ };
70
+ document.addEventListener("mousedown", onDoc);
71
+ return () => document.removeEventListener("mousedown", onDoc);
72
+ }, [open]);
73
+ useEffect(() => {
74
+ if (!open) return;
75
+ list.current?.querySelectorAll("[role=option]")[active]?.scrollIntoView?.({ block: "nearest" });
76
+ }, [open, active]);
77
+ const close = () => {
78
+ setOpen(false);
79
+ p.onBlur?.();
80
+ };
81
+ const pick = (v) => {
82
+ if (v !== p.value) p.onChange(v);
83
+ close();
84
+ };
85
+ const onKey = (e) => {
86
+ if (!open) {
87
+ if (e.key === "ArrowDown" || e.key === "ArrowUp" || e.key === "Enter" || e.key === " ") {
88
+ e.preventDefault();
89
+ setOpen(true);
90
+ }
91
+ return;
92
+ }
93
+ switch (e.key) {
94
+ case "ArrowDown":
95
+ e.preventDefault();
96
+ setActive((i) => Math.min(choices.length - 1, i + 1));
97
+ break;
98
+ case "ArrowUp":
99
+ e.preventDefault();
100
+ setActive((i) => Math.max(0, i - 1));
101
+ break;
102
+ case "Home":
103
+ e.preventDefault();
104
+ setActive(0);
105
+ break;
106
+ case "End":
107
+ e.preventDefault();
108
+ setActive(choices.length - 1);
109
+ break;
110
+ case "Enter":
111
+ case " ":
112
+ e.preventDefault();
113
+ pick(choices[active] ?? "");
114
+ break;
115
+ case "Escape":
116
+ e.preventDefault();
117
+ close();
118
+ break;
119
+ case "Tab":
120
+ close();
121
+ break;
122
+ default: {
123
+ if (e.key.length === 1 && /\S/.test(e.key)) {
124
+ const k = e.key.toLowerCase();
125
+ const idx = choices.findIndex((c) => labelFor(c, p.labels).toLowerCase().startsWith(k));
126
+ if (idx >= 0) setActive(idx);
127
+ }
128
+ }
129
+ }
130
+ };
131
+ const cls = ["realiiz-form__control", "realiiz-select__btn", p.invalid && "realiiz-form__control--invalid", open && "realiiz-select__btn--open"].filter(Boolean).join(" ");
132
+ return /* @__PURE__ */ jsxs("div", { ref: wrap, className: "realiiz-select", onKeyDown: onKey, children: [
133
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: p.name, value: p.value, readOnly: true }),
134
+ /* @__PURE__ */ jsxs(
135
+ "button",
136
+ {
137
+ type: "button",
138
+ id: p.id,
139
+ className: cls,
140
+ role: "combobox",
141
+ "aria-haspopup": "listbox",
142
+ "aria-expanded": open,
143
+ "aria-controls": listId,
144
+ "aria-invalid": p.invalid || void 0,
145
+ "aria-describedby": p.invalid ? `${p.id}-error` : void 0,
146
+ disabled: p.disabled,
147
+ onClick: () => open ? close() : setOpen(true),
148
+ children: [
149
+ /* @__PURE__ */ jsx("span", { className: p.value ? void 0 : "realiiz-select__placeholder", children: labelFor(p.value, p.labels) }),
150
+ /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" }) })
151
+ ]
152
+ }
153
+ ),
154
+ open && /* @__PURE__ */ jsx("ul", { ref: list, id: listId, className: "realiiz-select__menu", role: "listbox", "aria-labelledby": p.id, tabIndex: -1, children: choices.map((c, i) => /* @__PURE__ */ jsxs(
155
+ "li",
156
+ {
157
+ role: "option",
158
+ "aria-selected": c === p.value,
159
+ "data-value": c,
160
+ className: ["realiiz-select__item", i === active && "realiiz-select__item--active", c === p.value && "realiiz-select__item--selected"].filter(Boolean).join(" "),
161
+ onMouseEnter: () => setActive(i),
162
+ onMouseDown: (e) => e.preventDefault(),
163
+ onClick: () => pick(c),
164
+ children: [
165
+ /* @__PURE__ */ jsx("span", { className: "realiiz-select__check", "aria-hidden": "true", children: c === p.value ? "\u2713" : "" }),
166
+ /* @__PURE__ */ jsx("span", { children: labelFor(c, p.labels) })
167
+ ]
168
+ },
169
+ c || "__empty"
170
+ )) })
171
+ ] });
172
+ }
51
173
  function Control(p) {
52
174
  const cls = ["realiiz-form__control", p.invalid && "realiiz-form__control--invalid"].filter(Boolean).join(" ");
53
175
  const common = {
@@ -62,10 +184,19 @@ function Control(p) {
62
184
  return /* @__PURE__ */ jsx("div", { className: cls, "aria-busy": "true", style: { color: "var(--_muted)" }, children: "Loading options\u2026" });
63
185
  }
64
186
  if (p.options || p.spec.input === "select") {
65
- return /* @__PURE__ */ jsxs("select", { ...common, className: cls, value: str, onChange: (e) => p.onChange(e.target.value), children: [
66
- !p.spec.required && /* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
67
- (p.options ?? []).map((o) => /* @__PURE__ */ jsx("option", { value: o, children: o }, o))
68
- ] });
187
+ return /* @__PURE__ */ jsx(
188
+ SelectInput,
189
+ {
190
+ id: p.id,
191
+ name: p.spec.name,
192
+ value: str,
193
+ options: p.options ?? [],
194
+ allowEmpty: !p.spec.required,
195
+ invalid: p.invalid,
196
+ onChange: p.onChange,
197
+ onBlur: p.onBlur
198
+ }
199
+ );
69
200
  }
70
201
  switch (p.spec.input) {
71
202
  case "textarea":
@@ -101,6 +232,8 @@ function Control(p) {
101
232
  onChange: (e) => p.onChange(e.target.value)
102
233
  }
103
234
  );
235
+ case "time":
236
+ return /* @__PURE__ */ jsx("input", { ...common, type: "time", className: cls, value: str, onChange: (e) => p.onChange(e.target.value) });
104
237
  case "url":
105
238
  return /* @__PURE__ */ jsx(
106
239
  "input",
@@ -1171,6 +1304,6 @@ function sameValue(a, b) {
1171
1304
  return a === b;
1172
1305
  }
1173
1306
 
1174
- export { ChipsInput, ContentForm, IMAGE_HINT, ImageInput, LengthGauge, MarkdownEditor, TOOLS, slugify };
1307
+ export { ChipsInput, ContentForm, IMAGE_HINT, ImageInput, LengthGauge, MarkdownEditor, SelectInput, TOOLS, slugify };
1175
1308
  //# sourceMappingURL=index.js.map
1176
1309
  //# sourceMappingURL=index.js.map