@sincpro/web-ui 1.6.0 → 1.7.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.
Files changed (108) hide show
  1. package/dist/components/attachments/Attachments.d.ts +56 -0
  2. package/dist/components/attachments/Attachments.js +93 -0
  3. package/dist/components/attachments/files.d.ts +28 -0
  4. package/dist/components/attachments/files.js +100 -0
  5. package/dist/components/attachments/index.d.ts +2 -0
  6. package/dist/components/attachments/index.js +3 -0
  7. package/dist/components/flow/StepFlow.d.ts +37 -0
  8. package/dist/components/flow/StepFlow.js +74 -0
  9. package/dist/components/flow/index.d.ts +1 -0
  10. package/dist/components/flow/index.js +2 -0
  11. package/dist/components/index.d.ts +2 -0
  12. package/dist/components/index.js +2 -0
  13. package/dist/components/lines/LineItems.js +55 -45
  14. package/dist/components/listing/SortMenu.d.ts +24 -20
  15. package/dist/components/listing/SortMenu.js +51 -91
  16. package/dist/components/listing/index.d.ts +2 -1
  17. package/dist/components/listing/index.js +2 -1
  18. package/dist/components/listing/modelCells.d.ts +7 -0
  19. package/dist/components/listing/modelCells.js +56 -0
  20. package/dist/components/listing/modelColumns.d.ts +9 -0
  21. package/dist/components/listing/modelColumns.js +59 -14
  22. package/dist/components/search/index.d.ts +1 -1
  23. package/dist/components/search/index.js +1 -1
  24. package/dist/components/search/model.d.ts +1 -0
  25. package/dist/components/search/model.js +7 -0
  26. package/dist/components/search/query.js +2 -3
  27. package/dist/foundations/copy.d.ts +131 -0
  28. package/dist/foundations/copy.es.js +86 -0
  29. package/dist/foundations/copy.js +86 -0
  30. package/dist/foundations/form.d.ts +25 -0
  31. package/dist/foundations/form.js +27 -0
  32. package/dist/foundations/index.d.ts +1 -1
  33. package/dist/foundations/index.js +1 -1
  34. package/dist/primitives/button/Button.js +3 -1
  35. package/dist/primitives/choice/ComboBox.js +2 -2
  36. package/dist/primitives/choice/SearchField.js +2 -5
  37. package/dist/primitives/choice/SegmentedControl.js +2 -2
  38. package/dist/primitives/choice/Slider.js +2 -2
  39. package/dist/primitives/date/DatePicker.js +4 -4
  40. package/dist/primitives/date/DateTime.js +3 -3
  41. package/dist/primitives/field/CardNumberInput.d.ts +41 -0
  42. package/dist/primitives/field/CardNumberInput.js +117 -0
  43. package/dist/primitives/field/CodeInput.d.ts +18 -0
  44. package/dist/primitives/field/CodeInput.js +37 -0
  45. package/dist/primitives/field/ColorInput.d.ts +21 -0
  46. package/dist/primitives/field/ColorInput.js +42 -0
  47. package/dist/primitives/field/ContactInputs.d.ts +18 -0
  48. package/dist/primitives/field/ContactInputs.js +70 -0
  49. package/dist/primitives/field/DurationInput.d.ts +20 -0
  50. package/dist/primitives/field/DurationInput.js +54 -0
  51. package/dist/primitives/field/Field.d.ts +26 -5
  52. package/dist/primitives/field/Field.js +19 -6
  53. package/dist/primitives/field/ImageInput.d.ts +51 -0
  54. package/dist/primitives/field/ImageInput.js +267 -0
  55. package/dist/primitives/field/PasswordInput.d.ts +29 -0
  56. package/dist/primitives/field/PasswordInput.js +44 -0
  57. package/dist/primitives/field/PatternInput.d.ts +23 -0
  58. package/dist/primitives/field/PatternInput.js +48 -0
  59. package/dist/primitives/field/RichTextEditor.d.ts +18 -0
  60. package/dist/primitives/field/RichTextEditor.js +224 -0
  61. package/dist/primitives/field/RichTextInput.d.ts +30 -0
  62. package/dist/primitives/field/RichTextInput.js +38 -0
  63. package/dist/primitives/field/SignatureInput.d.ts +34 -0
  64. package/dist/primitives/field/SignatureInput.js +215 -0
  65. package/dist/primitives/field/Suggest.js +2 -2
  66. package/dist/primitives/field/card.d.ts +33 -0
  67. package/dist/primitives/field/card.js +173 -0
  68. package/dist/primitives/field/clipboard.js +3 -2
  69. package/dist/primitives/field/contact.d.ts +17 -0
  70. package/dist/primitives/field/contact.js +65 -0
  71. package/dist/primitives/field/duration.d.ts +5 -0
  72. package/dist/primitives/field/duration.js +48 -0
  73. package/dist/primitives/field/index.d.ts +14 -0
  74. package/dist/primitives/field/index.js +20 -0
  75. package/dist/primitives/field/mask.d.ts +33 -0
  76. package/dist/primitives/field/mask.js +56 -0
  77. package/dist/primitives/field/media.d.ts +99 -0
  78. package/dist/primitives/field/media.js +136 -0
  79. package/dist/primitives/field/pattern.d.ts +10 -0
  80. package/dist/primitives/field/pattern.js +68 -0
  81. package/dist/primitives/menu/Menu.d.ts +16 -0
  82. package/dist/primitives/menu/Menu.js +26 -8
  83. package/dist/primitives/menu/index.d.ts +1 -1
  84. package/dist/primitives/menu/index.js +1 -1
  85. package/dist/primitives/picker/Picker.js +2 -2
  86. package/dist/primitives/picker/TagsField.js +2 -2
  87. package/dist/primitives/table/DataTable.d.ts +6 -4
  88. package/dist/primitives/table/DataTable.js +12 -8
  89. package/dist/primitives/table/EditableTable.js +102 -99
  90. package/dist/primitives/table/Table.d.ts +5 -8
  91. package/dist/primitives/table/Table.js +63 -15
  92. package/dist/primitives/table/index.d.ts +2 -1
  93. package/dist/primitives/table/index.js +2 -1
  94. package/dist/primitives/table/sorting.d.ts +42 -0
  95. package/dist/primitives/table/sorting.js +110 -0
  96. package/dist/primitives/value/RichText.d.ts +10 -0
  97. package/dist/primitives/value/RichText.js +130 -0
  98. package/dist/primitives/value/Value.d.ts +20 -2
  99. package/dist/primitives/value/Value.js +26 -7
  100. package/dist/primitives/value/index.d.ts +3 -1
  101. package/dist/primitives/value/index.js +4 -1
  102. package/dist/primitives/value/rounding.d.ts +9 -0
  103. package/dist/primitives/value/rounding.js +48 -0
  104. package/dist/tokens.css +103 -0
  105. package/dist/views/list/List.d.ts +5 -4
  106. package/dist/views/list/ListView.js +2 -2
  107. package/dist/views/list/types.d.ts +6 -6
  108. package/package.json +9 -1
@@ -122,6 +122,22 @@ export type UiCopy = {
122
122
  none: string;
123
123
  /** Next to the label of a field whose value changed and is not saved yet. */
124
124
  changed: string;
125
+ /** The eye button of a password: show it… */
126
+ showSecret: string;
127
+ /** …and hide it again. */
128
+ hideSecret: string;
129
+ /** Under a password while Caps Lock is on: the most common reason a right password fails. */
130
+ capsLock: string;
131
+ /** What a stored password says in read mode, for a screen reader: there is one, not which. */
132
+ secretSet: string;
133
+ /** Said after a link that leaves for another tab — a website, WhatsApp. */
134
+ newTab: string;
135
+ /** The unit after a duration: «8:30 h». */
136
+ hours: string;
137
+ /** The name of the unit (or currency) chooser inside a number field. */
138
+ unit: string;
139
+ /** The row of colours a colour field offers. */
140
+ palette: string;
125
141
  };
126
142
  /** The choice controls: combo boxes, selects, suggestion lists. */
127
143
  choice: {
@@ -343,6 +359,32 @@ export type UiCopy = {
343
359
  /** A field with nothing in it: said, not a silent success. */
344
360
  empty: string;
345
361
  };
362
+ /** The rich-text editor: its toolbar, its link form, its list of people to mention. */
363
+ richText: {
364
+ toolbar: string;
365
+ bold: string;
366
+ italic: string;
367
+ underline: string;
368
+ strike: string;
369
+ heading: string;
370
+ subheading: string;
371
+ bulletList: string;
372
+ orderedList: string;
373
+ quote: string;
374
+ link: string;
375
+ linkUrl: string;
376
+ applyLink: string;
377
+ removeLink: string;
378
+ /** Said when the address is not a website, an email or a phone: what to write instead. */
379
+ badLink: string;
380
+ table: string;
381
+ undo: string;
382
+ redo: string;
383
+ clear: string;
384
+ /** The list that opens on «@»: people to mention. */
385
+ mentions: string;
386
+ noMatches: string;
387
+ };
346
388
  /** Ordenar, cuando la presentación no tiene encabezados donde hacer clic. */
347
389
  sorting: {
348
390
  label: string;
@@ -350,6 +392,24 @@ export type UiCopy = {
350
392
  asc: string;
351
393
  desc: string;
352
394
  clear: string;
395
+ /** On a sortable header: how to add it as a tie-breaker instead of replacing the order. */
396
+ addHint: string;
397
+ /** What a tie-breaker header says of itself: «sort 2, descending». */
398
+ place: string;
399
+ /** Adds one more criterion at the end of the order. */
400
+ add: string;
401
+ /** Where the criteria are listed when there are none: the rows go as they came. */
402
+ none: string;
403
+ /** The direction button of a criterion, named by its field. */
404
+ flip: string;
405
+ /** Takes one criterion out, named by its field. */
406
+ remove: string;
407
+ /** The handle a criterion is dragged by, named by its field. */
408
+ move: string;
409
+ /** The trigger with more than one criterion: «Fecha +2». */
410
+ more: string;
411
+ /** The second and later criteria: they only decide between rows the first leaves equal. */
412
+ then: string;
353
413
  };
354
414
  /** Los recortes de fecha RELATIVOS. Las unidades van en singular y plural
355
415
  * porque «1 mes» y «3 meses» no se resuelven con una `s` en todos los
@@ -470,6 +530,26 @@ export type UiCopy = {
470
530
  finish: string;
471
531
  busy: string;
472
532
  };
533
+ /** A multi-step operation done in one sitting (`StepFlow`): where you are, what is left,
534
+ * and its own buttons. */
535
+ stepFlow: {
536
+ /** The accessible name of the strip of steps. */
537
+ steps: string;
538
+ /** Over the step's title: «Step 2 of 3». */
539
+ position: string;
540
+ /** Read by the live region on every move: `{position}` is the line above, `{title}`
541
+ * the step's. */
542
+ announce: string;
543
+ /** The state of a step on the strip, said to a screen reader — never only a colour. */
544
+ done: string;
545
+ pending: string;
546
+ cancel: string;
547
+ back: string;
548
+ next: string;
549
+ finish: string;
550
+ /** Read by the live region while the operation runs. */
551
+ working: string;
552
+ };
473
553
  report: {
474
554
  open: string;
475
555
  close: string;
@@ -701,6 +781,28 @@ export type UiCopy = {
701
781
  /** The accessible name of an editable cell. `{column}` is the column's header. */
702
782
  editCell: string;
703
783
  };
784
+ /** The files of a record (`Attachments`): listing, adding, removing and their states. */
785
+ attachments: {
786
+ /** The accessible name of the list. */
787
+ label: string;
788
+ /** An empty list in read mode. */
789
+ empty: string;
790
+ /** The drop zone's sentence when there is no file yet… */
791
+ emptyAdd: string;
792
+ /** …and when there already are some. */
793
+ add: string;
794
+ download: string;
795
+ remove: string;
796
+ /** The notice after removing. `{name}` is the file. */
797
+ removed: string;
798
+ undo: string;
799
+ /** The name of an upload's progress bar. `{name}` is the file. */
800
+ uploading: string;
801
+ cancelUpload: string;
802
+ /** A failed upload, when the server did not say why. */
803
+ failed: string;
804
+ retry: string;
805
+ };
704
806
  /** The theme switch: the three choices an app offers for the palette. */
705
807
  theme: {
706
808
  /** The name of the control — what a screen reader says for the group. */
@@ -763,6 +865,35 @@ export type UiCopy = {
763
865
  /** A value that was empty before the change. */
764
866
  empty: string;
765
867
  };
868
+ /** The media controls: an image chosen and cropped (`ImageInput`), a signature drawn or
869
+ * typed (`SignatureInput`). */
870
+ media: {
871
+ /** What the empty image zone says. */
872
+ dropImage: string;
873
+ chooseImage: string;
874
+ changeImage: string;
875
+ removeImage: string;
876
+ /** A file that is not an image: what to choose instead. */
877
+ notImage: string;
878
+ /** The name of the crop area, and how it is moved from the keyboard. */
879
+ crop: string;
880
+ cropHelp: string;
881
+ zoomIn: string;
882
+ zoomOut: string;
883
+ applyCrop: string;
884
+ cancel: string;
885
+ /** The name of the drawing pad, and what to do there. */
886
+ signaturePad: string;
887
+ signatureHelp: string;
888
+ clear: string;
889
+ /** The way in for whoever cannot draw: type the name, and it is written as the signature. */
890
+ typeName: string;
891
+ drawInstead: string;
892
+ /** The label of the box the name is typed in. */
893
+ typedName: string;
894
+ /** The image of a signature, when the form does not say whose it is. */
895
+ signature: string;
896
+ };
766
897
  };
767
898
  export declare const ENGLISH_COPY: UiCopy;
768
899
  /** What changes of web-ui's words, at any depth: `{ search: { joins: { any: "o" } } }`. */
@@ -110,6 +110,14 @@ export const SPANISH_COPY = {
110
110
  unset: "sin definir",
111
111
  none: "Ninguna",
112
112
  changed: "modificado",
113
+ showSecret: "Mostrar contraseña",
114
+ hideSecret: "Ocultar contraseña",
115
+ capsLock: "Bloq Mayús está activado",
116
+ secretSet: "contraseña establecida",
117
+ newTab: "se abre en otra pestaña",
118
+ hours: "h",
119
+ unit: "Unidad",
120
+ palette: "Paleta",
113
121
  },
114
122
  choice: {
115
123
  noResults: "Sin resultados",
@@ -260,12 +268,44 @@ export const SPANISH_COPY = {
260
268
  field: "Copiar «{label}»",
261
269
  empty: "No hay nada que copiar",
262
270
  },
271
+ richText: {
272
+ toolbar: "Formato del texto",
273
+ bold: "Negrita",
274
+ italic: "Cursiva",
275
+ underline: "Subrayado",
276
+ strike: "Tachado",
277
+ heading: "Título",
278
+ subheading: "Subtítulo",
279
+ bulletList: "Lista con viñetas",
280
+ orderedList: "Lista numerada",
281
+ quote: "Cita",
282
+ link: "Enlace",
283
+ linkUrl: "Dirección del enlace",
284
+ applyLink: "Aplicar",
285
+ removeLink: "Quitar enlace",
286
+ badLink: "Escribí una web (empresa.bo), un correo o un teléfono.",
287
+ table: "Insertar tabla",
288
+ undo: "Deshacer",
289
+ redo: "Rehacer",
290
+ clear: "Quitar formato",
291
+ mentions: "Personas",
292
+ noMatches: "No coincide nadie",
293
+ },
263
294
  sorting: {
264
295
  label: "Ordenar",
265
296
  by: "ordenar por",
266
297
  asc: "ascendente",
267
298
  desc: "descendente",
268
299
  clear: "sin orden",
300
+ addHint: "Mayús+clic para agregarla como desempate",
301
+ place: "orden {place}, {direction}",
302
+ add: "Agregar criterio",
303
+ none: "Sin orden: las filas van como llegaron.",
304
+ flip: "Invertir el orden por {field}",
305
+ remove: "Quitar el orden por {field}",
306
+ move: "Mover el orden por {field}",
307
+ more: "{label} +{count}",
308
+ then: "luego por",
269
309
  },
270
310
  time: { justNow: "recién", invalid: "fecha inválida", noDate: "sin fecha" },
271
311
  calendar: {
@@ -324,6 +364,18 @@ export const SPANISH_COPY = {
324
364
  finish: "Terminar",
325
365
  busy: "Procesando…",
326
366
  },
367
+ stepFlow: {
368
+ steps: "Pasos",
369
+ position: "Paso {index} de {total}",
370
+ announce: "{position}: {title}",
371
+ done: "hecho",
372
+ pending: "pendiente",
373
+ cancel: "Cancelar",
374
+ back: "Atrás",
375
+ next: "Siguiente",
376
+ finish: "Terminar",
377
+ working: "Procesando…",
378
+ },
327
379
  report: { open: "Abrir {name}", close: "Cerrar {name}", concept: "Concepto" },
328
380
  builder: {
329
381
  filter: "Filtro",
@@ -478,6 +530,20 @@ export const SPANISH_COPY = {
478
530
  createTag: "Crear «{name}»",
479
531
  editCell: "Editar {column}",
480
532
  },
533
+ attachments: {
534
+ label: "Adjuntos",
535
+ empty: "Todavía no hay archivos adjuntos.",
536
+ emptyAdd: "Todavía no hay archivos adjuntos. Soltá uno acá o elegilo.",
537
+ add: "Soltá otro archivo acá o elegilo.",
538
+ download: "Descargar",
539
+ remove: "Quitar el archivo",
540
+ removed: "Se quitó «{name}».",
541
+ undo: "Deshacer",
542
+ uploading: "Subiendo {name}",
543
+ cancelUpload: "Cancelar la subida",
544
+ failed: "No se pudo subir el archivo.",
545
+ retry: "Reintentar",
546
+ },
481
547
  theme: {
482
548
  label: "Tema",
483
549
  system: "Sistema",
@@ -517,4 +583,24 @@ export const SPANISH_COPY = {
517
583
  to: "a",
518
584
  empty: "vacío",
519
585
  },
586
+ media: {
587
+ dropImage: "Soltá una imagen acá, pegala o elegila.",
588
+ chooseImage: "Elegir imagen",
589
+ changeImage: "Cambiar imagen",
590
+ removeImage: "Quitar imagen",
591
+ notImage: "El archivo no es una imagen. Elegí un PNG, JPG o WebP.",
592
+ crop: "Recorte de la imagen",
593
+ cropHelp: "Arrastrá la imagen o movela con las flechas; + y − acercan y alejan.",
594
+ zoomIn: "Acercar",
595
+ zoomOut: "Alejar",
596
+ applyCrop: "Aplicar recorte",
597
+ cancel: "Cancelar",
598
+ signaturePad: "Área de firma",
599
+ signatureHelp: "Firmá con el mouse o con el dedo sobre la línea.",
600
+ clear: "Limpiar",
601
+ typeName: "Escribir mi nombre",
602
+ drawInstead: "Dibujar la firma",
603
+ typedName: "Nombre para la firma",
604
+ signature: "Firma",
605
+ },
520
606
  };
@@ -86,6 +86,14 @@ export const ENGLISH_COPY = {
86
86
  unset: "not set",
87
87
  none: "None",
88
88
  changed: "changed",
89
+ showSecret: "Show password",
90
+ hideSecret: "Hide password",
91
+ capsLock: "Caps Lock is on",
92
+ secretSet: "password set",
93
+ newTab: "opens in a new tab",
94
+ hours: "h",
95
+ unit: "Unit",
96
+ palette: "Palette",
89
97
  },
90
98
  choice: {
91
99
  noResults: "No results",
@@ -208,12 +216,44 @@ export const ENGLISH_COPY = {
208
216
  field: "Copy «{label}»",
209
217
  empty: "Nothing to copy",
210
218
  },
219
+ richText: {
220
+ toolbar: "Text formatting",
221
+ bold: "Bold",
222
+ italic: "Italic",
223
+ underline: "Underline",
224
+ strike: "Strikethrough",
225
+ heading: "Heading",
226
+ subheading: "Subheading",
227
+ bulletList: "Bulleted list",
228
+ orderedList: "Numbered list",
229
+ quote: "Quote",
230
+ link: "Link",
231
+ linkUrl: "Link address",
232
+ applyLink: "Apply",
233
+ removeLink: "Remove link",
234
+ badLink: "Write a website (example.com), an email or a phone number.",
235
+ table: "Insert table",
236
+ undo: "Undo",
237
+ redo: "Redo",
238
+ clear: "Clear formatting",
239
+ mentions: "People",
240
+ noMatches: "Nobody matches",
241
+ },
211
242
  sorting: {
212
243
  label: "Sort",
213
244
  by: "sort by",
214
245
  asc: "ascending",
215
246
  desc: "descending",
216
247
  clear: "no order",
248
+ addHint: "Shift+click to add as a tie-breaker",
249
+ place: "sort {place}, {direction}",
250
+ add: "Add a criterion",
251
+ none: "No order: the rows go as they came.",
252
+ flip: "Flip the order by {field}",
253
+ remove: "Remove the order by {field}",
254
+ move: "Move the order by {field}",
255
+ more: "{label} +{count}",
256
+ then: "then by",
217
257
  },
218
258
  dates: {
219
259
  last: "last {count} {unit}",
@@ -310,6 +350,18 @@ export const ENGLISH_COPY = {
310
350
  finish: "Finish",
311
351
  busy: "Working…",
312
352
  },
353
+ stepFlow: {
354
+ steps: "Steps",
355
+ position: "Step {index} of {total}",
356
+ announce: "{position}: {title}",
357
+ done: "done",
358
+ pending: "to do",
359
+ cancel: "Cancel",
360
+ back: "Back",
361
+ next: "Next",
362
+ finish: "Finish",
363
+ working: "Working…",
364
+ },
313
365
  report: {
314
366
  open: "Open {name}",
315
367
  close: "Close {name}",
@@ -473,6 +525,20 @@ export const ENGLISH_COPY = {
473
525
  createTag: "Create «{name}»",
474
526
  editCell: "Edit {column}",
475
527
  },
528
+ attachments: {
529
+ label: "Attachments",
530
+ empty: "No files attached yet.",
531
+ emptyAdd: "No files attached yet. Drop one here, or choose it.",
532
+ add: "Drop another file here, or choose one.",
533
+ download: "Download",
534
+ remove: "Remove the file",
535
+ removed: "Removed «{name}».",
536
+ undo: "Undo",
537
+ uploading: "Uploading {name}",
538
+ cancelUpload: "Cancel the upload",
539
+ failed: "The file could not be uploaded.",
540
+ retry: "Try again",
541
+ },
476
542
  theme: {
477
543
  label: "Theme",
478
544
  system: "System",
@@ -512,6 +578,26 @@ export const ENGLISH_COPY = {
512
578
  to: "to",
513
579
  empty: "empty",
514
580
  },
581
+ media: {
582
+ dropImage: "Drop an image here, paste it, or choose one.",
583
+ chooseImage: "Choose image",
584
+ changeImage: "Change image",
585
+ removeImage: "Remove image",
586
+ notImage: "That file is not an image. Choose a PNG, JPG or WebP.",
587
+ crop: "Image crop",
588
+ cropHelp: "Drag the image, or move it with the arrow keys; + and − zoom in and out.",
589
+ zoomIn: "Zoom in",
590
+ zoomOut: "Zoom out",
591
+ applyCrop: "Apply crop",
592
+ cancel: "Cancel",
593
+ signaturePad: "Signature pad",
594
+ signatureHelp: "Sign with the mouse or a finger over the line.",
595
+ clear: "Clear",
596
+ typeName: "Type my name instead",
597
+ drawInstead: "Draw the signature",
598
+ typedName: "Name for the signature",
599
+ signature: "Signature",
600
+ },
515
601
  };
516
602
  /* The mechanism is `catalog.tsx`, the same one an application uses for its own words. */
517
603
  const ui = createCopy(ENGLISH_COPY);
@@ -34,6 +34,19 @@ export type ControlState = {
34
34
  invalid?: boolean;
35
35
  };
36
36
  export declare const ControlStateContext: import("react").Context<ControlState | null>;
37
+ /**
38
+ * Where a `Field` stops reaching: what is inside belongs to no field.
39
+ *
40
+ * Context: a `Field` hands its id and its label to the control inside it through context —
41
+ * and context goes all the way down. An editable table placed inside a field (the lines of a
42
+ * one2many under its label) gave EVERY cell's control the field's id and `aria-labelledby`:
43
+ * forty inputs with the same id, all named «Líneas». A collection is many controls, each
44
+ * named by its column, so the tables put this around themselves; an application's own
45
+ * composite control does the same.
46
+ */
47
+ export declare function FieldBoundary({ children }: {
48
+ children: ReactNode;
49
+ }): import("react").JSX.Element;
37
50
  /** The ids of the enclosing `Field` — or, outside one, the state of the place the control
38
51
  * sits in (a cell) — resolved against the control's own props. */
39
52
  export declare function useFieldIds(own: {
@@ -46,6 +59,18 @@ export declare function useFieldIds(own: {
46
59
  describedBy: string | undefined;
47
60
  invalid: boolean;
48
61
  };
62
+ /**
63
+ * The `aria-label` a control puts on itself: none inside a `Field` — the field's label names
64
+ * it — and outside one the control's own, or as a last resort its placeholder (what a browser
65
+ * reads for a bare `<input placeholder>` anyway, so a control outside a form is never
66
+ * nameless).
67
+ *
68
+ * Context: a control inside a `Field` used to keep its `aria-label` too. With both, React Aria
69
+ * names the control by the label AND itself («Cliente Cliente», «Cantidad cantidad»), so the
70
+ * name was read twice. The field wins: an `aria-label` is for a control that has no label
71
+ * on screen — a table cell, a search box — and inside a field it is ignored.
72
+ */
73
+ export declare function controlName(labelledBy: string | undefined, ariaLabel: string | undefined, fallback?: string): string | undefined;
49
74
  export type FormMode = "edit" | "read";
50
75
  export declare function FormModeProvider({ value, children, }: {
51
76
  value: FormMode;
@@ -3,6 +3,19 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext } from "react";
4
4
  export const FieldIdsContext = createContext(null);
5
5
  export const ControlStateContext = createContext(null);
6
+ /**
7
+ * Where a `Field` stops reaching: what is inside belongs to no field.
8
+ *
9
+ * Context: a `Field` hands its id and its label to the control inside it through context —
10
+ * and context goes all the way down. An editable table placed inside a field (the lines of a
11
+ * one2many under its label) gave EVERY cell's control the field's id and `aria-labelledby`:
12
+ * forty inputs with the same id, all named «Líneas». A collection is many controls, each
13
+ * named by its column, so the tables put this around themselves; an application's own
14
+ * composite control does the same.
15
+ */
16
+ export function FieldBoundary({ children }) {
17
+ return _jsx(FieldIdsContext.Provider, { value: null, children: children });
18
+ }
6
19
  /** The ids of the enclosing `Field` — or, outside one, the state of the place the control
7
20
  * sits in (a cell) — resolved against the control's own props. */
8
21
  export function useFieldIds(own) {
@@ -15,6 +28,20 @@ export function useFieldIds(own) {
15
28
  invalid: own.invalid ?? field?.invalid ?? place?.invalid ?? false,
16
29
  };
17
30
  }
31
+ /**
32
+ * The `aria-label` a control puts on itself: none inside a `Field` — the field's label names
33
+ * it — and outside one the control's own, or as a last resort its placeholder (what a browser
34
+ * reads for a bare `<input placeholder>` anyway, so a control outside a form is never
35
+ * nameless).
36
+ *
37
+ * Context: a control inside a `Field` used to keep its `aria-label` too. With both, React Aria
38
+ * names the control by the label AND itself («Cliente Cliente», «Cantidad cantidad»), so the
39
+ * name was read twice. The field wins: an `aria-label` is for a control that has no label
40
+ * on screen — a table cell, a search box — and inside a field it is ignored.
41
+ */
42
+ export function controlName(labelledBy, ariaLabel, fallback) {
43
+ return labelledBy === undefined ? (ariaLabel ?? fallback) : undefined;
44
+ }
18
45
  const Ctx = createContext("edit");
19
46
  export function FormModeProvider({ value, children, }) {
20
47
  return _jsx(Ctx.Provider, { value: value, children: children });
@@ -1,7 +1,7 @@
1
1
  export { type CopyCatalog, type CopyOverride, createCopy, mergeCopyOver, } from "./catalog.js";
2
2
  export { currentCopy, currentCopyLocale, ENGLISH_COPY, fillCopy, installCopy, mergeCopy, type UiCopy, type UiCopyOverride, UiCopyProvider, useUiCopy, } from "./copy.js";
3
3
  export { byDensity, type Density, DensityProvider, useDensity, } from "./density.js";
4
- export { type FieldIds, type FormMode, FormModeProvider, useFieldIds, useFormMode, } from "./form.js";
4
+ export { controlName, FieldBoundary, type FieldIds, type FormMode, FormModeProvider, useFieldIds, useFormMode, } from "./form.js";
5
5
  export { bySurface, SurfaceProvider, useSurface } from "./surface.js";
6
6
  export { applyTheme, isTheme, type ResolvedTheme, resolveTheme, type Theme, THEME_ATTRIBUTE, ThemeProvider, type ThemeProviderProps, THEMES, useTheme, } from "./theme.js";
7
7
  export type { OpenTarget, Size, Surface, Tone } from "./types.js";
@@ -5,7 +5,7 @@ export { currentCopy, currentCopyLocale, ENGLISH_COPY, fillCopy, installCopy, me
5
5
  export { byDensity, DensityProvider, useDensity, } from "./density.js";
6
6
  /* EL CONTRATO DE UN CONTROL — los ids que le da su `Field` y si se mira o se escribe. Lo
7
7
  hablan los controles del paquete y los de la aplicación por igual. */
8
- export { FormModeProvider, useFieldIds, useFormMode, } from "./form.js";
8
+ export { controlName, FieldBoundary, FormModeProvider, useFieldIds, useFormMode, } from "./form.js";
9
9
  /* EL TEMA — claro, oscuro o el del sistema operativo. Las paletas viven en `tokens.css`; esto
10
10
  sólo escribe `data-theme`. */
11
11
  export { bySurface, SurfaceProvider, useSurface } from "./surface.js";
@@ -195,7 +195,9 @@ const TONE = {
195
195
  chrome: "text-chrome-bad hover:bg-chrome-bad/12",
196
196
  },
197
197
  };
198
- const BOX = "inline-flex shrink-0 items-center justify-center rounded-pill transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary";
198
+ /* `aria-pressed` is a TOGGLE's state — bold on, a filter applied: it reads as the action
199
+ tint, the same as a chosen option, so «on» is seen and not only heard. */
200
+ const BOX = "inline-flex shrink-0 items-center justify-center rounded-pill transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary aria-pressed:bg-primary/12 aria-pressed:text-primary";
199
201
  const SQUARE = {
200
202
  sm: "size-(--size-control-sm)",
201
203
  md: "size-(--size-control-md)",
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { AriaLocale } from "../../foundations/aria.js";
4
- import { useFieldIds } from "../../foundations/form.js";
4
+ import { controlName, useFieldIds } from "../../foundations/form.js";
5
5
  import { useFormMode } from "../../foundations/form.js";
6
6
  import { labelOf } from "./options.js";
7
7
  import { FieldButton, FieldGroup, listOptionStyles, } from "../field/aria.js";
@@ -48,7 +48,7 @@ function EditableComboBox({ value, onChange, options, placeholder, disabled = fa
48
48
  invalid: ownInvalid,
49
49
  });
50
50
  const { contains } = useFilter({ sensitivity: "base" });
51
- return (_jsxs(AriaComboBox, { id: id, "aria-labelledby": labelledBy, "aria-label": ariaLabel, "aria-describedby": describedBy,
51
+ return (_jsxs(AriaComboBox, { id: id, "aria-labelledby": labelledBy, "aria-label": controlName(labelledBy, ariaLabel), "aria-describedby": describedBy,
52
52
  /* `defaultItems` and not `items`: with `items` React Aria hands the filtering to the
53
53
  consumer. These are the same options every render — only the filtering is React
54
54
  Aria's. */
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { AriaLocale } from "../../foundations/aria.js";
4
4
  import { useUiCopy } from "../../foundations/copy.js";
5
- import { useFieldIds } from "../../foundations/form.js";
5
+ import { controlName, useFieldIds } from "../../foundations/form.js";
6
6
  import { FieldButton, FieldGroup } from "../field/aria.js";
7
7
  import { Search, X } from "lucide-react";
8
8
  import { Input, SearchField as AriaSearchField } from "react-aria-components";
@@ -38,9 +38,6 @@ export function SearchField({ value, onChange, placeholder, onSubmit, disabled =
38
38
  describedBy: ownDescribedBy,
39
39
  });
40
40
  /* A `Field` names it through its label; anything else falls back so it always has one. */
41
- const name = ariaLabel ??
42
- (labelledBy === undefined
43
- ? (nameFromPlaceholder(placeholder) ?? copy.choice.search)
44
- : undefined);
41
+ const name = controlName(labelledBy, ariaLabel, nameFromPlaceholder(placeholder) ?? copy.choice.search);
45
42
  return (_jsx(AriaLocale, { children: _jsx(AriaSearchField, { id: id, "aria-labelledby": labelledBy, "aria-label": name, "aria-describedby": describedBy, value: value, onChange: (next) => onChange?.(next), onSubmit: onSubmit, isDisabled: disabled, className: "group w-full min-w-0", children: _jsxs(FieldGroup, { children: [_jsx(Search, { "aria-hidden": true, className: "ml-2.5 size-(--size-icon-md) shrink-0 text-muted forced-colors:text-[ButtonText]", strokeWidth: 2 }), _jsx(Input, { placeholder: placeholder, className: "h-full min-w-0 flex-1 bg-transparent px-2 text-meta text-ink outline-0 placeholder:text-muted disabled:cursor-not-allowed [&::-webkit-search-cancel-button]:hidden" }), _jsx(FieldButton, { className: "mr-1 group-data-empty:invisible", children: _jsx(X, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }) }) }));
46
43
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useFieldIds } from "../../foundations/form.js";
3
+ import { controlName, useFieldIds } from "../../foundations/form.js";
4
4
  import { useFormMode } from "../../foundations/form.js";
5
5
  import { tv } from "../../foundations/variants.js";
6
6
  import { labelOf } from "./options.js";
@@ -56,7 +56,7 @@ export function SegmentedControl({ value, onChange, options, size = "md", disabl
56
56
  const inForm = mode !== undefined || labelledBy !== undefined;
57
57
  if (inForm && formMode === "read")
58
58
  return _jsx(ReadValue, { value: labelOf(options, value) });
59
- return (_jsx(ToggleButtonGroup, { "aria-label": ariaLabel, "aria-labelledby": labelledBy, "aria-describedby": describedBy, selectionMode: "single",
59
+ return (_jsx(ToggleButtonGroup, { "aria-label": controlName(labelledBy, ariaLabel), "aria-labelledby": labelledBy, "aria-describedby": describedBy, selectionMode: "single",
60
60
  /* Always one: a period switcher with nothing pressed shows a report of no period. */
61
61
  disallowEmptySelection: true, selectedKeys: [value], onSelectionChange: (keys) => {
62
62
  const [next] = keys;
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { AriaLocale } from "../../foundations/aria.js";
4
4
  import { useUiCopy } from "../../foundations/copy.js";
5
- import { useFieldIds } from "../../foundations/form.js";
5
+ import { controlName, useFieldIds } from "../../foundations/form.js";
6
6
  import { useFormMode } from "../../foundations/form.js";
7
7
  import { tv } from "../../foundations/variants.js";
8
8
  import { ReadValue } from "../field/Field.js";
@@ -42,5 +42,5 @@ export function Slider({ value, onChange, min, max, step = 1, format, disabled =
42
42
  if (useFormMode(mode) === "read") {
43
43
  return _jsx(ReadValue, { value: new Intl.NumberFormat(copy.locale, format).format(value) });
44
44
  }
45
- return (_jsx(AriaLocale, { children: _jsxs(AriaSlider, { id: id, "aria-labelledby": labelledBy, "aria-label": ariaLabel, "aria-describedby": describedBy, value: value, onChange: (next) => onChange?.(next), minValue: min, maxValue: max, step: step, formatOptions: format, isDisabled: disabled, className: "flex h-(--size-control-md) w-full min-w-0 items-center gap-3 disabled:opacity-45", children: [_jsxs(SliderTrack, { className: "relative flex h-(--size-control-md) min-w-0 flex-1 cursor-pointer items-center disabled:cursor-not-allowed", children: [_jsx("span", { className: "relative h-1 w-full rounded-pill bg-line-strong forced-colors:bg-[ButtonBorder]", children: _jsx(SliderFill, { className: "rounded-pill bg-primary disabled:bg-muted forced-colors:bg-[Highlight]" }) }), _jsx(SliderThumb, { "aria-labelledby": labelledBy, "aria-label": ariaLabel, "aria-describedby": describedBy, className: thumb })] }), output ? (_jsx(SliderOutput, { className: "w-14 shrink-0 text-right text-meta text-ink tabular-nums" })) : null] }) }));
45
+ return (_jsx(AriaLocale, { children: _jsxs(AriaSlider, { id: id, "aria-labelledby": labelledBy, "aria-label": controlName(labelledBy, ariaLabel), "aria-describedby": describedBy, value: value, onChange: (next) => onChange?.(next), minValue: min, maxValue: max, step: step, formatOptions: format, isDisabled: disabled, className: "flex h-(--size-control-md) w-full min-w-0 items-center gap-3 disabled:opacity-45", children: [_jsxs(SliderTrack, { className: "relative flex h-(--size-control-md) min-w-0 flex-1 cursor-pointer items-center disabled:cursor-not-allowed", children: [_jsx("span", { className: "relative h-1 w-full rounded-pill bg-line-strong forced-colors:bg-[ButtonBorder]", children: _jsx(SliderFill, { className: "rounded-pill bg-primary disabled:bg-muted forced-colors:bg-[Highlight]" }) }), _jsx(SliderThumb, { "aria-labelledby": labelledBy, "aria-label": controlName(labelledBy, ariaLabel), "aria-describedby": describedBy, className: thumb })] }), output ? (_jsx(SliderOutput, { className: "w-14 shrink-0 text-right text-meta text-ink tabular-nums" })) : null] }) }));
46
46
  }
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { DateFormatter, parseDate } from "@internationalized/date";
4
4
  import { AriaLocale } from "../../foundations/aria.js";
5
5
  import { useUiCopy } from "../../foundations/copy.js";
6
- import { useFieldIds } from "../../foundations/form.js";
6
+ import { controlName, useFieldIds } from "../../foundations/form.js";
7
7
  import { useFormMode } from "../../foundations/form.js";
8
8
  import { tv } from "../../foundations/variants.js";
9
9
  import { Calendar, RangeCalendar } from "./Calendar.js";
@@ -71,7 +71,7 @@ export function DateField({ value, onChange, id: ownId, describedBy: ownDescribe
71
71
  const read = useReadDate();
72
72
  if (useFormMode(mode) === "read")
73
73
  return _jsx(ReadValue, { value: read(value) });
74
- return (_jsx(AriaLocale, { children: _jsx(AriaDateField, { id: id, "aria-labelledby": labelledBy, "aria-label": ariaLabel, "aria-describedby": describedBy, value: parse(value), onChange: (next) => onChange?.(next === null ? null : next.toString()), minValue: parse(min) ?? undefined, maxValue: parse(max) ?? undefined, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: _jsx(FieldGroup, { children: _jsx(Segments, {}) }) }) }));
74
+ return (_jsx(AriaLocale, { children: _jsx(AriaDateField, { id: id, "aria-labelledby": labelledBy, "aria-label": controlName(labelledBy, ariaLabel), "aria-describedby": describedBy, value: parse(value), onChange: (next) => onChange?.(next === null ? null : next.toString()), minValue: parse(min) ?? undefined, maxValue: parse(max) ?? undefined, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: _jsx(FieldGroup, { children: _jsx(Segments, {}) }) }) }));
75
75
  }
76
76
  /** A date typed by segments, or picked on a calendar from the button. */
77
77
  export function DatePicker({ value, onChange, id: ownId, describedBy: ownDescribedBy, invalid: ownInvalid, disabled = false, min, max, mode, "aria-label": ariaLabel, }) {
@@ -83,7 +83,7 @@ export function DatePicker({ value, onChange, id: ownId, describedBy: ownDescrib
83
83
  const read = useReadDate();
84
84
  if (useFormMode(mode) === "read")
85
85
  return _jsx(ReadValue, { value: read(value) });
86
- return (_jsx(AriaLocale, { children: _jsxs(AriaDatePicker, { id: id, "aria-labelledby": labelledBy, "aria-label": ariaLabel, "aria-describedby": describedBy, value: parse(value), onChange: (next) => onChange?.(next === null ? null : next.toString()), minValue: parse(min) ?? undefined, maxValue: parse(max) ?? undefined, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: [_jsxs(FieldGroup, { children: [_jsx(Segments, {}), _jsx(FieldButton, { className: "mr-1 ml-auto", children: _jsx(CalendarDays, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }), _jsx(Popover, { placement: "bottom end", children: _jsx(Dialog, { className: "p-3 outline-0", children: _jsx(Calendar, {}) }) })] }) }));
86
+ return (_jsx(AriaLocale, { children: _jsxs(AriaDatePicker, { id: id, "aria-labelledby": labelledBy, "aria-label": controlName(labelledBy, ariaLabel), "aria-describedby": describedBy, value: parse(value), onChange: (next) => onChange?.(next === null ? null : next.toString()), minValue: parse(min) ?? undefined, maxValue: parse(max) ?? undefined, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: [_jsxs(FieldGroup, { children: [_jsx(Segments, {}), _jsx(FieldButton, { className: "mr-1 ml-auto", children: _jsx(CalendarDays, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }), _jsx(Popover, { placement: "bottom end", children: _jsx(Dialog, { className: "p-3 outline-0", children: _jsx(Calendar, {}) }) })] }) }));
87
87
  }
88
88
  /** A span of days — a period of a report, the validity of a contract — typed or picked on
89
89
  * two months at once. */
@@ -99,5 +99,5 @@ export function DateRangePicker({ value, onChange, id: ownId, describedBy: ownDe
99
99
  }
100
100
  const start = parse(value?.start);
101
101
  const end = parse(value?.end);
102
- return (_jsx(AriaLocale, { children: _jsxs(AriaDateRangePicker, { id: id, "aria-labelledby": labelledBy, "aria-label": ariaLabel, "aria-describedby": describedBy, value: start === null || end === null ? null : { start, end }, onChange: (next) => onChange?.(next === null ? null : { start: next.start.toString(), end: next.end.toString() }), minValue: parse(min) ?? undefined, maxValue: parse(max) ?? undefined, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: [_jsxs(FieldGroup, { children: [_jsx(Segments, { slot: "start" }), _jsx("span", { "aria-hidden": true, className: "text-muted", children: "\u2013" }), _jsx(Segments, { slot: "end" }), _jsx(FieldButton, { className: "mr-1 ml-auto", children: _jsx(CalendarDays, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }), _jsx(Popover, { placement: "bottom end", children: _jsx(Dialog, { className: "p-3 outline-0", children: _jsx(RangeCalendar, { months: 2 }) }) })] }) }));
102
+ return (_jsx(AriaLocale, { children: _jsxs(AriaDateRangePicker, { id: id, "aria-labelledby": labelledBy, "aria-label": controlName(labelledBy, ariaLabel), "aria-describedby": describedBy, value: start === null || end === null ? null : { start, end }, onChange: (next) => onChange?.(next === null ? null : { start: next.start.toString(), end: next.end.toString() }), minValue: parse(min) ?? undefined, maxValue: parse(max) ?? undefined, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: [_jsxs(FieldGroup, { children: [_jsx(Segments, { slot: "start" }), _jsx("span", { "aria-hidden": true, className: "text-muted", children: "\u2013" }), _jsx(Segments, { slot: "end" }), _jsx(FieldButton, { className: "mr-1 ml-auto", children: _jsx(CalendarDays, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }), _jsx(Popover, { placement: "bottom end", children: _jsx(Dialog, { className: "p-3 outline-0", children: _jsx(RangeCalendar, { months: 2 }) }) })] }) }));
103
103
  }