framepexls-ui-lib 1.14.0 → 1.15.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.
@@ -11,7 +11,7 @@ type Props = {
11
11
  placeholder?: React__default.ReactNode;
12
12
  /** columnas en sm+ (1|2|3|4). default: 2 */
13
13
  smCols?: 1 | 2 | 3 | 4;
14
- /** columnas en md+ (opcional) */
14
+ /** columnas en md+ */
15
15
  mdCols?: 1 | 2 | 3 | 4;
16
16
  className?: string;
17
17
  };
@@ -11,7 +11,7 @@ type Props = {
11
11
  placeholder?: React__default.ReactNode;
12
12
  /** columnas en sm+ (1|2|3|4). default: 2 */
13
13
  smCols?: 1 | 2 | 3 | 4;
14
- /** columnas en md+ (opcional) */
14
+ /** columnas en md+ */
15
15
  mdCols?: 1 | 2 | 3 | 4;
16
16
  className?: string;
17
17
  };
@@ -216,7 +216,7 @@ function QuoteEditor({
216
216
  {
217
217
  value: (_f = value.clientId) != null ? _f : "",
218
218
  onChange: readOnly ? void 0 : (e) => update({ clientId: e.currentTarget.value || null }),
219
- placeholder: "ID de cliente (opcional)"
219
+ placeholder: "ID de cliente"
220
220
  }
221
221
  ) })
222
222
  ] })
@@ -253,7 +253,7 @@ function QuoteEditor({
253
253
  value: (_b2 = l.description) != null ? _b2 : "",
254
254
  onChange: readOnly ? void 0 : (e) => updateLine(l.id, { description: e.currentTarget.value || null }),
255
255
  rows: 2,
256
- placeholder: "Descripci\xF3n (opcional)",
256
+ placeholder: "Descripci\xF3n",
257
257
  className: "text-[0.8125rem]"
258
258
  }
259
259
  )
@@ -192,7 +192,7 @@ function QuoteEditor({
192
192
  {
193
193
  value: (_f = value.clientId) != null ? _f : "",
194
194
  onChange: readOnly ? void 0 : (e) => update({ clientId: e.currentTarget.value || null }),
195
- placeholder: "ID de cliente (opcional)"
195
+ placeholder: "ID de cliente"
196
196
  }
197
197
  ) })
198
198
  ] })
@@ -229,7 +229,7 @@ function QuoteEditor({
229
229
  value: (_b2 = l.description) != null ? _b2 : "",
230
230
  onChange: readOnly ? void 0 : (e) => updateLine(l.id, { description: e.currentTarget.value || null }),
231
231
  rows: 2,
232
- placeholder: "Descripci\xF3n (opcional)",
232
+ placeholder: "Descripci\xF3n",
233
233
  className: "text-[0.8125rem]"
234
234
  }
235
235
  )
package/dist/Reviews.js CHANGED
@@ -262,10 +262,10 @@ function Reviews({
262
262
  comment: (_k = labels == null ? void 0 : labels.comment) != null ? _k : "Comentario",
263
263
  commentPlaceholder: (_l = labels == null ? void 0 : labels.commentPlaceholder) != null ? _l : "Cu\xE9ntanos qu\xE9 funcion\xF3 bien y qu\xE9 se puede mejorar\u2026",
264
264
  tags: (_m = labels == null ? void 0 : labels.tags) != null ? _m : "Tags",
265
- tagsHint: (_n = labels == null ? void 0 : labels.tagsHint) != null ? _n : "Separa por comas (opcional).",
265
+ tagsHint: (_n = labels == null ? void 0 : labels.tagsHint) != null ? _n : "Separa por comas.",
266
266
  tagsPlaceholder: (_o = labels == null ? void 0 : labels.tagsPlaceholder) != null ? _o : "ux, rendimiento, dise\xF1o",
267
267
  authorName: (_p = labels == null ? void 0 : labels.authorName) != null ? _p : "Nombre",
268
- authorNamePlaceholder: (_q = labels == null ? void 0 : labels.authorNamePlaceholder) != null ? _q : "Tu nombre (opcional)",
268
+ authorNamePlaceholder: (_q = labels == null ? void 0 : labels.authorNamePlaceholder) != null ? _q : "Tu nombre",
269
269
  submit: (_r = labels == null ? void 0 : labels.submit) != null ? _r : "Publicar",
270
270
  clear: (_s = labels == null ? void 0 : labels.clear) != null ? _s : "Limpiar",
271
271
  emptyTitle: (_t = labels == null ? void 0 : labels.emptyTitle) != null ? _t : "Sin rese\xF1as a\xFAn",
package/dist/Reviews.mjs CHANGED
@@ -229,10 +229,10 @@ function Reviews({
229
229
  comment: (_k = labels == null ? void 0 : labels.comment) != null ? _k : "Comentario",
230
230
  commentPlaceholder: (_l = labels == null ? void 0 : labels.commentPlaceholder) != null ? _l : "Cu\xE9ntanos qu\xE9 funcion\xF3 bien y qu\xE9 se puede mejorar\u2026",
231
231
  tags: (_m = labels == null ? void 0 : labels.tags) != null ? _m : "Tags",
232
- tagsHint: (_n = labels == null ? void 0 : labels.tagsHint) != null ? _n : "Separa por comas (opcional).",
232
+ tagsHint: (_n = labels == null ? void 0 : labels.tagsHint) != null ? _n : "Separa por comas.",
233
233
  tagsPlaceholder: (_o = labels == null ? void 0 : labels.tagsPlaceholder) != null ? _o : "ux, rendimiento, dise\xF1o",
234
234
  authorName: (_p = labels == null ? void 0 : labels.authorName) != null ? _p : "Nombre",
235
- authorNamePlaceholder: (_q = labels == null ? void 0 : labels.authorNamePlaceholder) != null ? _q : "Tu nombre (opcional)",
235
+ authorNamePlaceholder: (_q = labels == null ? void 0 : labels.authorNamePlaceholder) != null ? _q : "Tu nombre",
236
236
  submit: (_r = labels == null ? void 0 : labels.submit) != null ? _r : "Publicar",
237
237
  clear: (_s = labels == null ? void 0 : labels.clear) != null ? _s : "Limpiar",
238
238
  emptyTitle: (_t = labels == null ? void 0 : labels.emptyTitle) != null ? _t : "Sin rese\xF1as a\xFAn",
@@ -387,7 +387,7 @@ function ShareAccess({
387
387
  ] }) }),
388
388
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-4 grid grid-cols-1 gap-3", children: [
389
389
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
390
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-1.5 text-sm font-medium", children: "Nombre (opcional)" }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-1.5 text-sm font-medium", children: "Nombre" }),
391
391
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
392
392
  import_Input.default,
393
393
  {
@@ -423,7 +423,7 @@ function ShareAccess({
423
423
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2 rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_55%,transparent)] p-3 sm:flex-row sm:items-center sm:justify-between", children: [
424
424
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
425
425
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-medium", children: "Usos m\xE1x." }),
426
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-1 text-xs text-[var(--muted)]", children: "Opcional. Vac\xEDo = ilimitado." })
426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-1 text-xs text-[var(--muted)]", children: "Vac\xEDo = ilimitado." })
427
427
  ] }),
428
428
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-2 sm:justify-end", children: [
429
429
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Badge.default, { tone: "slate", size: "sm", children: maxUses.trim() ? `${maxUses.trim()} uso(s)` : "Ilimitado" }),
@@ -354,7 +354,7 @@ function ShareAccess({
354
354
  ] }) }),
355
355
  /* @__PURE__ */ jsxs("div", { className: "mt-4 grid grid-cols-1 gap-3", children: [
356
356
  /* @__PURE__ */ jsxs("div", { children: [
357
- /* @__PURE__ */ jsx("div", { className: "mb-1.5 text-sm font-medium", children: "Nombre (opcional)" }),
357
+ /* @__PURE__ */ jsx("div", { className: "mb-1.5 text-sm font-medium", children: "Nombre" }),
358
358
  /* @__PURE__ */ jsx(
359
359
  Input,
360
360
  {
@@ -390,7 +390,7 @@ function ShareAccess({
390
390
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_55%,transparent)] p-3 sm:flex-row sm:items-center sm:justify-between", children: [
391
391
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
392
392
  /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: "Usos m\xE1x." }),
393
- /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-[var(--muted)]", children: "Opcional. Vac\xEDo = ilimitado." })
393
+ /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-[var(--muted)]", children: "Vac\xEDo = ilimitado." })
394
394
  ] }),
395
395
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 sm:justify-end", children: [
396
396
  /* @__PURE__ */ jsx(Badge, { tone: "slate", size: "sm", children: maxUses.trim() ? `${maxUses.trim()} uso(s)` : "Ilimitado" }),
package/dist/Steps.js CHANGED
@@ -60,7 +60,7 @@ function Steps({
60
60
  children: [
61
61
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: cx("grid grid-cols-1 sm:grid-cols-4", compact ? "gap-2" : "gap-3"), children: steps.map((s, i) => {
62
62
  const state = i < current ? "done" : i === current ? "current" : "upcoming";
63
- const canClick = clickable && i <= current && !s.disabled && onChange;
63
+ const canClick = clickable && !s.disabled && onChange;
64
64
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
65
65
  import_Button.default,
66
66
  {
package/dist/Steps.mjs CHANGED
@@ -26,7 +26,7 @@ function Steps({
26
26
  children: [
27
27
  /* @__PURE__ */ jsx("ol", { className: cx("grid grid-cols-1 sm:grid-cols-4", compact ? "gap-2" : "gap-3"), children: steps.map((s, i) => {
28
28
  const state = i < current ? "done" : i === current ? "current" : "upcoming";
29
- const canClick = clickable && i <= current && !s.disabled && onChange;
29
+ const canClick = clickable && !s.disabled && onChange;
30
30
  return /* @__PURE__ */ jsx("li", { className: "min-w-0", children: /* @__PURE__ */ jsxs(
31
31
  Button,
32
32
  {
@@ -38,6 +38,7 @@ var import_react_dom = require("react-dom");
38
38
  var import_Input = __toESM(require("./Input"));
39
39
  var import_Button = __toESM(require("./Button"));
40
40
  var import_TimePopover = __toESM(require("./TimePopover"));
41
+ var import_ActionIconButton = __toESM(require("./ActionIconButton"));
41
42
  var import_iconos = require("./iconos");
42
43
  const pad2 = (n) => n < 10 ? `0${n}` : String(n);
43
44
  function parseHHmm(v) {
@@ -98,6 +99,8 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
98
99
  if (!anchorRef.current) return;
99
100
  setAnchorRect(anchorRef.current.getBoundingClientRect());
100
101
  setOpen(true);
102
+ setShowFromPop(false);
103
+ setShowToPop(false);
101
104
  };
102
105
  (0, import_react.useEffect)(() => {
103
106
  if (!open) return;
@@ -129,7 +132,9 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
129
132
  }, [open]);
130
133
  const stylePop = (() => {
131
134
  if (!anchorRect) return { visibility: "hidden" };
132
- const W = 360, GAP = 2, MARGIN = 8;
135
+ const MARGIN = 8;
136
+ const W = Math.min(420, Math.max(280, window.innerWidth - MARGIN * 2));
137
+ const GAP = 4;
133
138
  let left = anchorRect.right - W;
134
139
  left = Math.max(MARGIN, Math.min(left, window.innerWidth - (W + MARGIN)));
135
140
  const spaceAbove = anchorRect.top - MARGIN;
@@ -137,10 +142,10 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
137
142
  const placeAbove = spaceAbove >= spaceBelow;
138
143
  if (placeAbove) {
139
144
  const top2 = Math.max(MARGIN, anchorRect.top - GAP);
140
- return { position: "fixed", top: top2, left, zIndex: 1e5, transform: "translateY(-100%)" };
145
+ return { position: "fixed", width: W, top: top2, left, zIndex: 1e5, transform: "translateY(-100%)" };
141
146
  }
142
147
  const top = Math.max(MARGIN, anchorRect.bottom + GAP);
143
- return { position: "fixed", top, left, zIndex: 1e5 };
148
+ return { position: "fixed", width: W, top, left, zIndex: 1e5 };
144
149
  })();
145
150
  const commit = (f, t) => {
146
151
  let f2 = f, t2 = t;
@@ -153,80 +158,122 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
153
158
  setTo(t2);
154
159
  onValueChange == null ? void 0 : onValueChange({ from: f2 ? fmtHHmm(f2.hh, f2.mm) : null, to: t2 ? fmtHHmm(t2.hh, t2.mm) : null });
155
160
  };
156
- const popover = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-trf-pop": true, style: stylePop, className: "w-1/3 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-xl dark:border-white/10 dark:bg-[var(--fx-surface)]", children: [
157
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-2 px-3 py-2 text-sm", children: [
158
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "font-medium", children: "Selecciona horario" }),
159
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
160
- import_Button.default,
161
- {
162
- unstyled: true,
163
- type: "button",
164
- className: "rounded-xl bg-[var(--primary)] px-3 py-1.5 text-[var(--primary-foreground)] hover:brightness-95 active:scale-[0.98]",
165
- onClick: () => setOpen(false),
166
- children: "Aplicar"
167
- }
168
- )
169
- ] }),
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-2 gap-3 border-t border-slate-100 p-3 text-sm dark:border-white/10", children: [
171
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
172
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-1 text-xs text-slate-500 dark:text-slate-300", children: "Desde" }),
173
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
174
- import_Button.default,
175
- {
176
- unstyled: true,
177
- type: "button",
178
- className: "rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 font-medium tracking-wide hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
179
- onClick: () => setShowFromPop((v) => !v),
180
- ref: fromBtnRef,
181
- children: from ? toAMPM(from.hh, from.mm) : "--:--"
182
- }
183
- ),
184
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
185
- import_Button.default,
186
- {
187
- unstyled: true,
188
- type: "button",
189
- className: "ml-2 rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
190
- onClick: () => {
191
- const t = /* @__PURE__ */ new Date();
192
- const rounded = Math.round(t.getMinutes() / step) * step % 60;
193
- commit({ hh: t.getHours(), mm: rounded }, to);
194
- },
195
- children: "Ahora"
196
- }
197
- )
198
- ] }),
199
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
200
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-1 text-xs text-slate-500 dark:text-slate-300", children: "Hasta" }),
201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
202
- import_Button.default,
203
- {
204
- unstyled: true,
205
- type: "button",
206
- className: "rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 font-medium tracking-wide hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
207
- onClick: () => setShowToPop((v) => !v),
208
- ref: toBtnRef,
209
- children: to ? toAMPM(to.hh, to.mm) : "--:--"
210
- }
211
- ),
212
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
213
- import_Button.default,
214
- {
215
- unstyled: true,
216
- type: "button",
217
- className: "ml-2 rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
218
- onClick: () => {
219
- const t = /* @__PURE__ */ new Date();
220
- const rounded = Math.round(t.getMinutes() / step) * step % 60;
221
- const candidate = { hh: t.getHours(), mm: rounded };
222
- commit(from, candidate);
223
- },
224
- children: "Ahora"
225
- }
226
- )
227
- ] })
228
- ] })
229
- ] });
161
+ const canClear = clearable && (from || to) && !disabled;
162
+ const popover = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
163
+ "div",
164
+ {
165
+ "data-trf-pop": true,
166
+ style: stylePop,
167
+ className: "overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)] shadow-xl ring-1 ring-black/5",
168
+ role: "dialog",
169
+ "aria-label": "Selector de rango de horas",
170
+ children: [
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-2 px-3 py-2", children: [
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0", children: [
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "truncate text-sm font-semibold text-[var(--foreground)]", children: "Horario" }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-0.5 truncate text-xs text-[var(--muted)]", children: display ? display : "Selecciona desde y hasta" })
175
+ ] }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
177
+ canClear ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
178
+ import_Button.default,
179
+ {
180
+ variant: "outline",
181
+ size: "sm",
182
+ onClick: () => {
183
+ commit(null, null);
184
+ setShowFromPop(false);
185
+ setShowToPop(false);
186
+ },
187
+ children: "Limpiar"
188
+ }
189
+ ) : null,
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
191
+ import_Button.default,
192
+ {
193
+ variant: "primary",
194
+ size: "sm",
195
+ onClick: () => {
196
+ setOpen(false);
197
+ setShowFromPop(false);
198
+ setShowToPop(false);
199
+ },
200
+ children: "Listo"
201
+ }
202
+ )
203
+ ] })
204
+ ] }),
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-1 gap-3 border-t border-[var(--border)] p-3 sm:grid-cols-2", children: [
206
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_72%,transparent)] p-3", children: [
207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-2 text-xs font-semibold text-[var(--muted)]", children: "Desde" }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
210
+ import_Button.default,
211
+ {
212
+ variant: "outline",
213
+ size: "sm",
214
+ active: showFromPop,
215
+ className: "flex-1 justify-start font-semibold tabular-nums",
216
+ onClick: () => {
217
+ setShowFromPop((v) => !v);
218
+ setShowToPop(false);
219
+ },
220
+ ref: fromBtnRef,
221
+ children: from ? toAMPM(from.hh, from.mm) : "--:--"
222
+ }
223
+ ),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
225
+ import_Button.default,
226
+ {
227
+ variant: "secondary",
228
+ size: "sm",
229
+ onClick: () => {
230
+ const t = /* @__PURE__ */ new Date();
231
+ const rounded = Math.round(t.getMinutes() / step) * step % 60;
232
+ commit({ hh: t.getHours(), mm: rounded }, to);
233
+ },
234
+ children: "Ahora"
235
+ }
236
+ )
237
+ ] })
238
+ ] }),
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_72%,transparent)] p-3", children: [
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-2 text-xs font-semibold text-[var(--muted)]", children: "Hasta" }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
243
+ import_Button.default,
244
+ {
245
+ variant: "outline",
246
+ size: "sm",
247
+ active: showToPop,
248
+ className: "flex-1 justify-start font-semibold tabular-nums",
249
+ onClick: () => {
250
+ setShowToPop((v) => !v);
251
+ setShowFromPop(false);
252
+ },
253
+ ref: toBtnRef,
254
+ children: to ? toAMPM(to.hh, to.mm) : "--:--"
255
+ }
256
+ ),
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
258
+ import_Button.default,
259
+ {
260
+ variant: "secondary",
261
+ size: "sm",
262
+ onClick: () => {
263
+ const t = /* @__PURE__ */ new Date();
264
+ const rounded = Math.round(t.getMinutes() / step) * step % 60;
265
+ const candidate = { hh: t.getHours(), mm: rounded };
266
+ commit(from, candidate);
267
+ },
268
+ children: "Ahora"
269
+ }
270
+ )
271
+ ] })
272
+ ] })
273
+ ] })
274
+ ]
275
+ }
276
+ );
230
277
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: anchorRef, className, children: [
231
278
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
232
279
  import_Input.default,
@@ -241,17 +288,18 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
241
288
  onValueChange == null ? void 0 : onValueChange({ from: null, to: null });
242
289
  },
243
290
  disabled,
244
- rightSlot: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
245
- import_Button.default,
291
+ rightAction: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
292
+ import_ActionIconButton.default,
246
293
  {
247
- unstyled: true,
248
- type: "button",
249
- className: "pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 hover:bg-slate-50 active:scale-95 dark:border-white/10 dark:bg-[var(--fx-surface)]",
294
+ size: "sm",
295
+ title: "Abrir selector de horas",
296
+ disabled,
250
297
  onClick: (e) => {
251
298
  e.preventDefault();
299
+ e.stopPropagation();
252
300
  openPopover();
253
301
  },
254
- title: "Abrir selector de horas",
302
+ className: "h-8 w-8 rounded-xl",
255
303
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ClockIcon, { "aria-hidden": true, className: "h-4.5 w-4.5" })
256
304
  }
257
305
  )
@@ -5,6 +5,7 @@ import { createPortal } from "react-dom";
5
5
  import Input from "./Input.mjs";
6
6
  import Button from "./Button.mjs";
7
7
  import TimePopover from "./TimePopover.mjs";
8
+ import ActionIconButton from "./ActionIconButton.mjs";
8
9
  import { ClockIcon } from "./iconos/index.mjs";
9
10
  const pad2 = (n) => n < 10 ? `0${n}` : String(n);
10
11
  function parseHHmm(v) {
@@ -65,6 +66,8 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
65
66
  if (!anchorRef.current) return;
66
67
  setAnchorRect(anchorRef.current.getBoundingClientRect());
67
68
  setOpen(true);
69
+ setShowFromPop(false);
70
+ setShowToPop(false);
68
71
  };
69
72
  useEffect(() => {
70
73
  if (!open) return;
@@ -96,7 +99,9 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
96
99
  }, [open]);
97
100
  const stylePop = (() => {
98
101
  if (!anchorRect) return { visibility: "hidden" };
99
- const W = 360, GAP = 2, MARGIN = 8;
102
+ const MARGIN = 8;
103
+ const W = Math.min(420, Math.max(280, window.innerWidth - MARGIN * 2));
104
+ const GAP = 4;
100
105
  let left = anchorRect.right - W;
101
106
  left = Math.max(MARGIN, Math.min(left, window.innerWidth - (W + MARGIN)));
102
107
  const spaceAbove = anchorRect.top - MARGIN;
@@ -104,10 +109,10 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
104
109
  const placeAbove = spaceAbove >= spaceBelow;
105
110
  if (placeAbove) {
106
111
  const top2 = Math.max(MARGIN, anchorRect.top - GAP);
107
- return { position: "fixed", top: top2, left, zIndex: 1e5, transform: "translateY(-100%)" };
112
+ return { position: "fixed", width: W, top: top2, left, zIndex: 1e5, transform: "translateY(-100%)" };
108
113
  }
109
114
  const top = Math.max(MARGIN, anchorRect.bottom + GAP);
110
- return { position: "fixed", top, left, zIndex: 1e5 };
115
+ return { position: "fixed", width: W, top, left, zIndex: 1e5 };
111
116
  })();
112
117
  const commit = (f, t) => {
113
118
  let f2 = f, t2 = t;
@@ -120,80 +125,122 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
120
125
  setTo(t2);
121
126
  onValueChange == null ? void 0 : onValueChange({ from: f2 ? fmtHHmm(f2.hh, f2.mm) : null, to: t2 ? fmtHHmm(t2.hh, t2.mm) : null });
122
127
  };
123
- const popover = /* @__PURE__ */ jsxs("div", { "data-trf-pop": true, style: stylePop, className: "w-1/3 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-xl dark:border-white/10 dark:bg-[var(--fx-surface)]", children: [
124
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2 text-sm", children: [
125
- /* @__PURE__ */ jsx("div", { className: "font-medium", children: "Selecciona horario" }),
126
- /* @__PURE__ */ jsx(
127
- Button,
128
- {
129
- unstyled: true,
130
- type: "button",
131
- className: "rounded-xl bg-[var(--primary)] px-3 py-1.5 text-[var(--primary-foreground)] hover:brightness-95 active:scale-[0.98]",
132
- onClick: () => setOpen(false),
133
- children: "Aplicar"
134
- }
135
- )
136
- ] }),
137
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 border-t border-slate-100 p-3 text-sm dark:border-white/10", children: [
138
- /* @__PURE__ */ jsxs("div", { children: [
139
- /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs text-slate-500 dark:text-slate-300", children: "Desde" }),
140
- /* @__PURE__ */ jsx(
141
- Button,
142
- {
143
- unstyled: true,
144
- type: "button",
145
- className: "rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 font-medium tracking-wide hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
146
- onClick: () => setShowFromPop((v) => !v),
147
- ref: fromBtnRef,
148
- children: from ? toAMPM(from.hh, from.mm) : "--:--"
149
- }
150
- ),
151
- /* @__PURE__ */ jsx(
152
- Button,
153
- {
154
- unstyled: true,
155
- type: "button",
156
- className: "ml-2 rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
157
- onClick: () => {
158
- const t = /* @__PURE__ */ new Date();
159
- const rounded = Math.round(t.getMinutes() / step) * step % 60;
160
- commit({ hh: t.getHours(), mm: rounded }, to);
161
- },
162
- children: "Ahora"
163
- }
164
- )
165
- ] }),
166
- /* @__PURE__ */ jsxs("div", { children: [
167
- /* @__PURE__ */ jsx("div", { className: "mb-1 text-xs text-slate-500 dark:text-slate-300", children: "Hasta" }),
168
- /* @__PURE__ */ jsx(
169
- Button,
170
- {
171
- unstyled: true,
172
- type: "button",
173
- className: "rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 font-medium tracking-wide hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
174
- onClick: () => setShowToPop((v) => !v),
175
- ref: toBtnRef,
176
- children: to ? toAMPM(to.hh, to.mm) : "--:--"
177
- }
178
- ),
179
- /* @__PURE__ */ jsx(
180
- Button,
181
- {
182
- unstyled: true,
183
- type: "button",
184
- className: "ml-2 rounded-xl ring-1 ring-slate-200 px-2.5 py-1.5 hover:bg-slate-50 active:scale-[0.98] dark:ring-white/10 dark:hover:bg-white/10",
185
- onClick: () => {
186
- const t = /* @__PURE__ */ new Date();
187
- const rounded = Math.round(t.getMinutes() / step) * step % 60;
188
- const candidate = { hh: t.getHours(), mm: rounded };
189
- commit(from, candidate);
190
- },
191
- children: "Ahora"
192
- }
193
- )
194
- ] })
195
- ] })
196
- ] });
128
+ const canClear = clearable && (from || to) && !disabled;
129
+ const popover = /* @__PURE__ */ jsxs(
130
+ "div",
131
+ {
132
+ "data-trf-pop": true,
133
+ style: stylePop,
134
+ className: "overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)] shadow-xl ring-1 ring-black/5",
135
+ role: "dialog",
136
+ "aria-label": "Selector de rango de horas",
137
+ children: [
138
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2", children: [
139
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
140
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-semibold text-[var(--foreground)]", children: "Horario" }),
141
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate text-xs text-[var(--muted)]", children: display ? display : "Selecciona desde y hasta" })
142
+ ] }),
143
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
144
+ canClear ? /* @__PURE__ */ jsx(
145
+ Button,
146
+ {
147
+ variant: "outline",
148
+ size: "sm",
149
+ onClick: () => {
150
+ commit(null, null);
151
+ setShowFromPop(false);
152
+ setShowToPop(false);
153
+ },
154
+ children: "Limpiar"
155
+ }
156
+ ) : null,
157
+ /* @__PURE__ */ jsx(
158
+ Button,
159
+ {
160
+ variant: "primary",
161
+ size: "sm",
162
+ onClick: () => {
163
+ setOpen(false);
164
+ setShowFromPop(false);
165
+ setShowToPop(false);
166
+ },
167
+ children: "Listo"
168
+ }
169
+ )
170
+ ] })
171
+ ] }),
172
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 border-t border-[var(--border)] p-3 sm:grid-cols-2", children: [
173
+ /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_72%,transparent)] p-3", children: [
174
+ /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold text-[var(--muted)]", children: "Desde" }),
175
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
176
+ /* @__PURE__ */ jsx(
177
+ Button,
178
+ {
179
+ variant: "outline",
180
+ size: "sm",
181
+ active: showFromPop,
182
+ className: "flex-1 justify-start font-semibold tabular-nums",
183
+ onClick: () => {
184
+ setShowFromPop((v) => !v);
185
+ setShowToPop(false);
186
+ },
187
+ ref: fromBtnRef,
188
+ children: from ? toAMPM(from.hh, from.mm) : "--:--"
189
+ }
190
+ ),
191
+ /* @__PURE__ */ jsx(
192
+ Button,
193
+ {
194
+ variant: "secondary",
195
+ size: "sm",
196
+ onClick: () => {
197
+ const t = /* @__PURE__ */ new Date();
198
+ const rounded = Math.round(t.getMinutes() / step) * step % 60;
199
+ commit({ hh: t.getHours(), mm: rounded }, to);
200
+ },
201
+ children: "Ahora"
202
+ }
203
+ )
204
+ ] })
205
+ ] }),
206
+ /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--surface)_72%,transparent)] p-3", children: [
207
+ /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold text-[var(--muted)]", children: "Hasta" }),
208
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
209
+ /* @__PURE__ */ jsx(
210
+ Button,
211
+ {
212
+ variant: "outline",
213
+ size: "sm",
214
+ active: showToPop,
215
+ className: "flex-1 justify-start font-semibold tabular-nums",
216
+ onClick: () => {
217
+ setShowToPop((v) => !v);
218
+ setShowFromPop(false);
219
+ },
220
+ ref: toBtnRef,
221
+ children: to ? toAMPM(to.hh, to.mm) : "--:--"
222
+ }
223
+ ),
224
+ /* @__PURE__ */ jsx(
225
+ Button,
226
+ {
227
+ variant: "secondary",
228
+ size: "sm",
229
+ onClick: () => {
230
+ const t = /* @__PURE__ */ new Date();
231
+ const rounded = Math.round(t.getMinutes() / step) * step % 60;
232
+ const candidate = { hh: t.getHours(), mm: rounded };
233
+ commit(from, candidate);
234
+ },
235
+ children: "Ahora"
236
+ }
237
+ )
238
+ ] })
239
+ ] })
240
+ ] })
241
+ ]
242
+ }
243
+ );
197
244
  return /* @__PURE__ */ jsxs("div", { ref: anchorRef, className, children: [
198
245
  /* @__PURE__ */ jsx(
199
246
  Input,
@@ -208,17 +255,18 @@ function TimeRangeField({ value, onValueChange, portal = true, portalId, clearab
208
255
  onValueChange == null ? void 0 : onValueChange({ from: null, to: null });
209
256
  },
210
257
  disabled,
211
- rightSlot: /* @__PURE__ */ jsx(
212
- Button,
258
+ rightAction: /* @__PURE__ */ jsx(
259
+ ActionIconButton,
213
260
  {
214
- unstyled: true,
215
- type: "button",
216
- className: "pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 hover:bg-slate-50 active:scale-95 dark:border-white/10 dark:bg-[var(--fx-surface)]",
261
+ size: "sm",
262
+ title: "Abrir selector de horas",
263
+ disabled,
217
264
  onClick: (e) => {
218
265
  e.preventDefault();
266
+ e.stopPropagation();
219
267
  openPopover();
220
268
  },
221
- title: "Abrir selector de horas",
269
+ className: "h-8 w-8 rounded-xl",
222
270
  children: /* @__PURE__ */ jsx(ClockIcon, { "aria-hidden": true, className: "h-4.5 w-4.5" })
223
271
  }
224
272
  )
@@ -823,7 +823,7 @@ function WordEditor({ value, onChange, disabled = false, className, labels }) {
823
823
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dialog.default, { open: imageOpen, onClose: () => setImageOpen(false), size: "md", initialFocusRef: imageUrlRef, children: [
824
824
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dialog.default.Header, { title: "Insertar imagen", description: "Sube una imagen o pega una URL (https:// o data:image/...).", onClose: () => setImageOpen(false), showClose: true }),
825
825
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dialog.default.Body, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid gap-3", children: [
826
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dialog.default.Field, { label: "URL (opcional)", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
826
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dialog.default.Field, { label: "URL", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
827
827
  import_Input.default,
828
828
  {
829
829
  ref: imageUrlRef,
@@ -813,7 +813,7 @@ function WordEditor({ value, onChange, disabled = false, className, labels }) {
813
813
  /* @__PURE__ */ jsxs(Dialog, { open: imageOpen, onClose: () => setImageOpen(false), size: "md", initialFocusRef: imageUrlRef, children: [
814
814
  /* @__PURE__ */ jsx(Dialog.Header, { title: "Insertar imagen", description: "Sube una imagen o pega una URL (https:// o data:image/...).", onClose: () => setImageOpen(false), showClose: true }),
815
815
  /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
816
- /* @__PURE__ */ jsx(Dialog.Field, { label: "URL (opcional)", children: /* @__PURE__ */ jsx(
816
+ /* @__PURE__ */ jsx(Dialog.Field, { label: "URL", children: /* @__PURE__ */ jsx(
817
817
  Input,
818
818
  {
819
819
  ref: imageUrlRef,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framepexls-ui-lib",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Componentes UI de Framepexls para React/Next.",