@clicoh/orion-library 1.1.0-beta.7 → 1.1.0-beta.9

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 (121) hide show
  1. package/assets/css/base.css +18 -0
  2. package/dist/components/00-base/Each/Each.d.ts +13 -0
  3. package/dist/components/00-base/Each/Each.d.ts.map +1 -1
  4. package/dist/components/00-base/Each/Each.jsx +19 -2
  5. package/dist/components/00-base/Each/Each.jsx.map +1 -1
  6. package/dist/components/01-atoms/Badge/Badge.d.ts +14 -4
  7. package/dist/components/01-atoms/Badge/Badge.d.ts.map +1 -1
  8. package/dist/components/01-atoms/Badge/Badge.jsx +34 -4
  9. package/dist/components/01-atoms/Badge/Badge.jsx.map +1 -1
  10. package/dist/components/01-atoms/CountryFlag/CountryFlag.d.ts +30 -0
  11. package/dist/components/01-atoms/CountryFlag/CountryFlag.d.ts.map +1 -0
  12. package/dist/components/01-atoms/CountryFlag/CountryFlag.jsx +105 -0
  13. package/dist/components/01-atoms/CountryFlag/CountryFlag.jsx.map +1 -0
  14. package/dist/components/01-atoms/CountryFlag/index.d.ts +3 -0
  15. package/dist/components/01-atoms/CountryFlag/index.d.ts.map +1 -0
  16. package/dist/components/01-atoms/CountryFlag/index.js +2 -0
  17. package/dist/components/01-atoms/CountryFlag/index.js.map +1 -0
  18. package/dist/components/01-atoms/Input/Input.d.ts +3 -1
  19. package/dist/components/01-atoms/Input/Input.d.ts.map +1 -1
  20. package/dist/components/01-atoms/Input/Input.jsx +5 -2
  21. package/dist/components/01-atoms/Input/Input.jsx.map +1 -1
  22. package/dist/components/01-atoms/Input/utils/border.d.ts +6 -1
  23. package/dist/components/01-atoms/Input/utils/border.d.ts.map +1 -1
  24. package/dist/components/01-atoms/Input/utils/border.js +13 -3
  25. package/dist/components/01-atoms/Input/utils/border.js.map +1 -1
  26. package/dist/components/01-atoms/MultiSelect/MultiSelect.d.ts +3 -1
  27. package/dist/components/01-atoms/MultiSelect/MultiSelect.d.ts.map +1 -1
  28. package/dist/components/01-atoms/MultiSelect/MultiSelect.jsx +17 -5
  29. package/dist/components/01-atoms/MultiSelect/MultiSelect.jsx.map +1 -1
  30. package/dist/components/01-atoms/Paragraph/Paragraph.d.ts +14 -4
  31. package/dist/components/01-atoms/Paragraph/Paragraph.d.ts.map +1 -1
  32. package/dist/components/01-atoms/Paragraph/Paragraph.jsx +30 -8
  33. package/dist/components/01-atoms/Paragraph/Paragraph.jsx.map +1 -1
  34. package/dist/components/01-atoms/Select/Select.d.ts +3 -1
  35. package/dist/components/01-atoms/Select/Select.d.ts.map +1 -1
  36. package/dist/components/01-atoms/Select/Select.jsx +10 -4
  37. package/dist/components/01-atoms/Select/Select.jsx.map +1 -1
  38. package/dist/components/01-atoms/index.d.ts +1 -0
  39. package/dist/components/01-atoms/index.d.ts.map +1 -1
  40. package/dist/components/01-atoms/index.js +1 -0
  41. package/dist/components/01-atoms/index.js.map +1 -1
  42. package/dist/components/02-molecules/Autocomplete/Autocomplete.d.ts +3 -1
  43. package/dist/components/02-molecules/Autocomplete/Autocomplete.d.ts.map +1 -1
  44. package/dist/components/02-molecules/Autocomplete/Autocomplete.jsx +3 -2
  45. package/dist/components/02-molecules/Autocomplete/Autocomplete.jsx.map +1 -1
  46. package/dist/components/02-molecules/BellNotifications/index.d.ts +1 -1
  47. package/dist/components/02-molecules/BellNotifications/index.d.ts.map +1 -1
  48. package/dist/components/02-molecules/Filter/Filter.d.ts +13 -50
  49. package/dist/components/02-molecules/Filter/Filter.d.ts.map +1 -1
  50. package/dist/components/02-molecules/Filter/Filter.jsx +156 -165
  51. package/dist/components/02-molecules/Filter/Filter.jsx.map +1 -1
  52. package/dist/components/02-molecules/Filter/index.d.ts +2 -0
  53. package/dist/components/02-molecules/Filter/index.d.ts.map +1 -1
  54. package/dist/components/02-molecules/Filter/index.js +2 -0
  55. package/dist/components/02-molecules/Filter/index.js.map +1 -1
  56. package/dist/components/02-molecules/Filter/types.d.ts +59 -0
  57. package/dist/components/02-molecules/Filter/types.d.ts.map +1 -0
  58. package/dist/components/02-molecules/Filter/types.js +2 -0
  59. package/dist/components/02-molecules/Filter/types.js.map +1 -0
  60. package/dist/components/02-molecules/Filter/utils/getFilterChips.d.ts +23 -0
  61. package/dist/components/02-molecules/Filter/utils/getFilterChips.d.ts.map +1 -0
  62. package/dist/components/02-molecules/Filter/utils/getFilterChips.js +47 -0
  63. package/dist/components/02-molecules/Filter/utils/getFilterChips.js.map +1 -0
  64. package/dist/components/02-molecules/FilterChips/FilterChips.d.ts +44 -0
  65. package/dist/components/02-molecules/FilterChips/FilterChips.d.ts.map +1 -0
  66. package/dist/components/02-molecules/FilterChips/FilterChips.jsx +60 -0
  67. package/dist/components/02-molecules/FilterChips/FilterChips.jsx.map +1 -0
  68. package/dist/components/02-molecules/FilterChips/index.d.ts +2 -0
  69. package/dist/components/02-molecules/FilterChips/index.d.ts.map +1 -0
  70. package/dist/components/02-molecules/FilterChips/index.js +2 -0
  71. package/dist/components/02-molecules/FilterChips/index.js.map +1 -0
  72. package/dist/components/02-molecules/ImageViewer/ImageViewer.d.ts +60 -0
  73. package/dist/components/02-molecules/ImageViewer/ImageViewer.d.ts.map +1 -0
  74. package/dist/components/02-molecules/ImageViewer/ImageViewer.jsx +144 -0
  75. package/dist/components/02-molecules/ImageViewer/ImageViewer.jsx.map +1 -0
  76. package/dist/components/02-molecules/ImageViewer/index.d.ts +2 -0
  77. package/dist/components/02-molecules/ImageViewer/index.d.ts.map +1 -0
  78. package/dist/components/02-molecules/ImageViewer/index.js +2 -0
  79. package/dist/components/02-molecules/ImageViewer/index.js.map +1 -0
  80. package/dist/components/02-molecules/SideNav/SideNav.d.ts +58 -0
  81. package/dist/components/02-molecules/SideNav/SideNav.d.ts.map +1 -0
  82. package/dist/components/02-molecules/SideNav/SideNav.jsx +53 -0
  83. package/dist/components/02-molecules/SideNav/SideNav.jsx.map +1 -0
  84. package/dist/components/02-molecules/SideNav/SideNavItem.d.ts +36 -0
  85. package/dist/components/02-molecules/SideNav/SideNavItem.d.ts.map +1 -0
  86. package/dist/components/02-molecules/SideNav/SideNavItem.jsx +38 -0
  87. package/dist/components/02-molecules/SideNav/SideNavItem.jsx.map +1 -0
  88. package/dist/components/02-molecules/SideNav/index.d.ts +3 -0
  89. package/dist/components/02-molecules/SideNav/index.d.ts.map +1 -0
  90. package/dist/components/02-molecules/SideNav/index.js +3 -0
  91. package/dist/components/02-molecules/SideNav/index.js.map +1 -0
  92. package/dist/components/02-molecules/Tab/TabItem.d.ts +4 -1
  93. package/dist/components/02-molecules/Tab/TabItem.d.ts.map +1 -1
  94. package/dist/components/02-molecules/Tab/TabItem.jsx +9 -2
  95. package/dist/components/02-molecules/Tab/TabItem.jsx.map +1 -1
  96. package/dist/components/02-molecules/Tab/TabList.d.ts +12 -2
  97. package/dist/components/02-molecules/Tab/TabList.d.ts.map +1 -1
  98. package/dist/components/02-molecules/Tab/TabList.jsx +40 -9
  99. package/dist/components/02-molecules/Tab/TabList.jsx.map +1 -1
  100. package/dist/components/02-molecules/Table/Table.d.ts.map +1 -1
  101. package/dist/components/02-molecules/Table/Table.jsx +6 -3
  102. package/dist/components/02-molecules/Table/Table.jsx.map +1 -1
  103. package/dist/components/02-molecules/index.d.ts +3 -0
  104. package/dist/components/02-molecules/index.d.ts.map +1 -1
  105. package/dist/components/02-molecules/index.js +3 -0
  106. package/dist/components/02-molecules/index.js.map +1 -1
  107. package/dist/components/03-organisms/AsideFilters/AsideFilters.jsx +1 -1
  108. package/dist/components/03-organisms/AsideFilters/AsideFilters.jsx.map +1 -1
  109. package/dist/components/03-organisms/DataTable/DataTable.d.ts +10 -1
  110. package/dist/components/03-organisms/DataTable/DataTable.d.ts.map +1 -1
  111. package/dist/components/03-organisms/DataTable/DataTable.jsx +16 -3
  112. package/dist/components/03-organisms/DataTable/DataTable.jsx.map +1 -1
  113. package/dist/components/03-organisms/Header/HeaderActions.d.ts.map +1 -1
  114. package/dist/components/03-organisms/Header/HeaderActions.jsx +1 -1
  115. package/dist/components/03-organisms/Header/HeaderActions.jsx.map +1 -1
  116. package/dist/components/04-templates/SideFilterTemplate/SideFilterTemplate.d.ts +3 -1
  117. package/dist/components/04-templates/SideFilterTemplate/SideFilterTemplate.d.ts.map +1 -1
  118. package/dist/components/04-templates/SideFilterTemplate/SideFilterTemplate.jsx +3 -1
  119. package/dist/components/04-templates/SideFilterTemplate/SideFilterTemplate.jsx.map +1 -1
  120. package/package.json +1 -1
  121. package/tailwind.config.ts +21 -0
@@ -0,0 +1,44 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ import { type OptionType } from '../../01-atoms';
3
+ import type { TFilterInputs, TFilterValues } from '../Filter/types';
4
+ type FilterChipsProps = {
5
+ inputs: TFilterInputs;
6
+ values: Record<string, TFilterValues>;
7
+ onRemove: (key: string) => void;
8
+ onClearAll?: () => void;
9
+ clearAllText?: string;
10
+ removeFilterLabel?: (filterLabel: string) => string;
11
+ extraOptions?: Record<string, OptionType[]>;
12
+ } & Omit<HTMLAttributes<HTMLUListElement>, 'children'>;
13
+ /**
14
+ * FilterChips component for rendering the applied filters as removable chips.
15
+ *
16
+ * Resolves every chip label against the same configuration the Filter uses, so
17
+ * the chips read the same inside the panel and outside of it. Renders nothing
18
+ * while no filter is applied.
19
+ *
20
+ * @component
21
+ * @example
22
+ * <FilterChips
23
+ * inputs={config}
24
+ * values={{ country: [{ value: 1, label: 'México' }] }}
25
+ * onRemove={(key) => removeFilter(key)}
26
+ * onClearAll={() => resetFilters()}
27
+ * clearAllText="Limpiar todos"
28
+ * />
29
+ *
30
+ * @param {Object} props - The properties of the FilterChips component.
31
+ * @param {TFilterInputs} props.inputs - Filter configuration used to resolve the labels.
32
+ * @param {Object} props.values - Applied filter values, keyed by filter key.
33
+ * @param {Function} props.onRemove - Callback triggered with the key of the removed filter.
34
+ * @param {Function} [props.onClearAll] - Callback that clears every filter. The action is hidden when omitted.
35
+ * @param {string} [props.clearAllText] - Text of the clear all action.
36
+ * @param {Function} [props.removeFilterLabel] - Builds the accessible label of each remove button.
37
+ * @param {Object} [props.extraOptions] - Asynchronously loaded options, keyed by filter key.
38
+ * @param {HTMLAttributes<HTMLUListElement>} [props.restProps] - Additional attributes for the list element.
39
+ *
40
+ * @returns {ReactNode} The rendered FilterChips component.
41
+ */
42
+ export declare function FilterChips({ inputs, values, onRemove, onClearAll, clearAllText, removeFilterLabel, extraOptions, ...restProps }: FilterChipsProps): ReactNode;
43
+ export {};
44
+ //# sourceMappingURL=FilterChips.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterChips.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/FilterChips/FilterChips.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG5D,OAAO,EAAQ,KAAK,UAAU,EAAa,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGpE,KAAK,gBAAgB,GAAG;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACtC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;CAC7C,GAAG,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,GAAG,SAAS,EACb,EAAE,gBAAgB,GAAG,SAAS,CA4C9B"}
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+ import clsx from 'clsx';
3
+ import { MdClose } from 'react-icons/md';
4
+ import { Each, Show } from '../../00-base';
5
+ import { List, Paragraph } from '../../01-atoms';
6
+ import { getFilterChips } from '../Filter/utils/getFilterChips';
7
+ /**
8
+ * FilterChips component for rendering the applied filters as removable chips.
9
+ *
10
+ * Resolves every chip label against the same configuration the Filter uses, so
11
+ * the chips read the same inside the panel and outside of it. Renders nothing
12
+ * while no filter is applied.
13
+ *
14
+ * @component
15
+ * @example
16
+ * <FilterChips
17
+ * inputs={config}
18
+ * values={{ country: [{ value: 1, label: 'México' }] }}
19
+ * onRemove={(key) => removeFilter(key)}
20
+ * onClearAll={() => resetFilters()}
21
+ * clearAllText="Limpiar todos"
22
+ * />
23
+ *
24
+ * @param {Object} props - The properties of the FilterChips component.
25
+ * @param {TFilterInputs} props.inputs - Filter configuration used to resolve the labels.
26
+ * @param {Object} props.values - Applied filter values, keyed by filter key.
27
+ * @param {Function} props.onRemove - Callback triggered with the key of the removed filter.
28
+ * @param {Function} [props.onClearAll] - Callback that clears every filter. The action is hidden when omitted.
29
+ * @param {string} [props.clearAllText] - Text of the clear all action.
30
+ * @param {Function} [props.removeFilterLabel] - Builds the accessible label of each remove button.
31
+ * @param {Object} [props.extraOptions] - Asynchronously loaded options, keyed by filter key.
32
+ * @param {HTMLAttributes<HTMLUListElement>} [props.restProps] - Additional attributes for the list element.
33
+ *
34
+ * @returns {ReactNode} The rendered FilterChips component.
35
+ */
36
+ export function FilterChips({ inputs, values, onRemove, onClearAll, clearAllText, removeFilterLabel, extraOptions, ...restProps }) {
37
+ const chips = getFilterChips({ inputs, values, extraOptions });
38
+ if (!chips.length)
39
+ return null;
40
+ return (<List {...restProps} orientation="horizontal" className={clsx('flex-wrap gap-2', restProps.className)}>
41
+ <Each of={chips} render={({ key, label, text }) => (<li key={key}>
42
+ <div className="bg-gray-5 dark:bg-gray-1 inline-flex items-center rounded-full px-4 py-2 dark:text-white">
43
+ <Paragraph size="chips">{text}</Paragraph>
44
+ <button type="button" aria-label={removeFilterLabel?.(label) ?? label} onClick={() => onRemove(key)} className="bg-error ml-1.5 inline-flex h-4 w-4 cursor-pointer items-center justify-center rounded-full p-0.5 text-white">
45
+ <MdClose aria-hidden="true"/>
46
+ </button>
47
+ </div>
48
+ </li>)}/>
49
+ <Show>
50
+ <Show.When isTrue={Boolean(onClearAll && clearAllText)}>
51
+ <li>
52
+ <button type="button" onClick={onClearAll} className="hover:text-gray-1 dark:hover:text-gray-2 cursor-pointer text-sm underline underline-offset-2 dark:text-white">
53
+ {clearAllText}
54
+ </button>
55
+ </li>
56
+ </Show.When>
57
+ </Show>
58
+ </List>);
59
+ }
60
+ //# sourceMappingURL=FilterChips.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterChips.jsx","sourceRoot":"","sources":["../../../../src/components/02-molecules/FilterChips/FilterChips.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAmB,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAYhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,GAAG,SAAS,EACK;IACjB,MAAM,KAAK,GAAG,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IAE/D,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,CACL,CAAC,IAAI,CACH,IAAI,SAAS,CAAC,CACd,WAAW,CAAC,YAAY,CACxB,SAAS,CAAC,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAExD;MAAA,CAAC,IAAI,CACH,EAAE,CAAC,CAAC,KAAK,CAAC,CACV,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAChC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CACX;YAAA,CAAC,GAAG,CAAC,SAAS,CAAC,0FAA0F,CACvG;cAAA,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,CACzC;cAAA,CAAC,MAAM,CACL,IAAI,CAAC,QAAQ,CACb,UAAU,CAAC,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAChD,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAC7B,SAAS,CAAC,8GAA8G,CAExH;gBAAA,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,EAC7B;cAAA,EAAE,MAAM,CACV;YAAA,EAAE,GAAG,CACP;UAAA,EAAE,EAAE,CAAC,CACN,CAAC,EAEJ;MAAA,CAAC,IAAI,CACH;QAAA,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,YAAY,CAAC,CAAC,CACrD;UAAA,CAAC,EAAE,CACD;YAAA,CAAC,MAAM,CACL,IAAI,CAAC,QAAQ,CACb,OAAO,CAAC,CAAC,UAAU,CAAC,CACpB,SAAS,CAAC,8GAA8G,CAExH;cAAA,CAAC,YAAY,CACf;YAAA,EAAE,MAAM,CACV;UAAA,EAAE,EAAE,CACN;QAAA,EAAE,IAAI,CAAC,IAAI,CACb;MAAA,EAAE,IAAI,CACR;IAAA,EAAE,IAAI,CAAC,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './FilterChips';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/FilterChips/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './FilterChips';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/02-molecules/FilterChips/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ export type TImageView = {
3
+ id: string;
4
+ label: string;
5
+ src: string;
6
+ alt: string;
7
+ };
8
+ export type TImageViewerLabels = {
9
+ zoomIn: string;
10
+ zoomOut: string;
11
+ rotate: string;
12
+ enterFullscreen: string;
13
+ exitFullscreen: string;
14
+ };
15
+ type ImageViewerProps = {
16
+ views: TImageView[];
17
+ labels: TImageViewerLabels;
18
+ title?: ReactNode;
19
+ emptyState?: ReactNode;
20
+ } & Omit<HTMLAttributes<HTMLElement>, 'children' | 'title'>;
21
+ /**
22
+ * ImageViewer component for inspecting one or more images of the same subject.
23
+ *
24
+ * Each view is a tab, so a subject photographed from several angles reads as one
25
+ * piece instead of a gallery. Zoom and rotation reset when the view changes: a
26
+ * transform that made sense for one angle rarely makes sense for the next.
27
+ *
28
+ * Renders `emptyState` while `views` is empty, and disables the toolbar with it,
29
+ * so the frame keeps its size instead of collapsing.
30
+ *
31
+ * @component
32
+ * @example
33
+ * <ImageViewer
34
+ * title="Identity document"
35
+ * views={[
36
+ * { id: 'front', label: 'Front', src: '/front.jpg', alt: 'Document front' },
37
+ * { id: 'back', label: 'Back', src: '/back.jpg', alt: 'Document back' },
38
+ * ]}
39
+ * labels={{
40
+ * zoomIn: 'Zoom in',
41
+ * zoomOut: 'Zoom out',
42
+ * rotate: 'Rotate',
43
+ * enterFullscreen: 'Full screen',
44
+ * exitFullscreen: 'Exit full screen',
45
+ * }}
46
+ * emptyState={<p>Nothing uploaded yet</p>}
47
+ * />
48
+ *
49
+ * @param {Object} props - The properties of the ImageViewer component.
50
+ * @param {TImageView[]} props.views - Images to display. More than one renders a tab per view.
51
+ * @param {TImageViewerLabels} props.labels - Accessible names of the toolbar actions.
52
+ * @param {ReactNode} [props.title] - Optional heading rendered on the left of the toolbar.
53
+ * @param {ReactNode} [props.emptyState] - Content rendered when there is no image.
54
+ * @param {HTMLAttributes<HTMLElement>} [props.restProps] - Additional attributes for the root element.
55
+ *
56
+ * @returns {ReactNode} The rendered ImageViewer component.
57
+ */
58
+ export declare function ImageViewer({ views, labels, title, emptyState, ...restProps }: ImageViewerProps): ReactNode;
59
+ export {};
60
+ //# sourceMappingURL=ImageViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageViewer.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/ImageViewer/ImageViewer.tsx"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAaf,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC;AAW5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,MAAM,EACN,KAAK,EACL,UAAU,EACV,GAAG,SAAS,EACb,EAAE,gBAAgB,GAAG,SAAS,CA6K9B"}
@@ -0,0 +1,144 @@
1
+ 'use client';
2
+ import clsx from 'clsx';
3
+ import { useEffect, useId, useState, } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { MdFullscreen, MdFullscreenExit, MdRotateRight, MdZoomIn, MdZoomOut, } from 'react-icons/md';
6
+ import { Each, Show } from '../../00-base';
7
+ import { List, Paragraph } from '../../01-atoms';
8
+ import { TabItem } from '../Tab/TabItem';
9
+ const MIN_ZOOM = 1;
10
+ const MAX_ZOOM = 3;
11
+ const ZOOM_STEP = 0.5;
12
+ const QUARTER_TURN = 90;
13
+ const FULL_TURN = 360;
14
+ const TOOL_CLASSNAME = 'hover:bg-gray-5 focus-visible:ring-lavender disabled:text-gray-3 dark:disabled:text-gray-1 dark:hover:bg-gray-1 flex h-8 w-8 cursor-pointer items-center justify-center rounded-sm text-lg text-black focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:hover:bg-transparent dark:text-white';
15
+ /**
16
+ * ImageViewer component for inspecting one or more images of the same subject.
17
+ *
18
+ * Each view is a tab, so a subject photographed from several angles reads as one
19
+ * piece instead of a gallery. Zoom and rotation reset when the view changes: a
20
+ * transform that made sense for one angle rarely makes sense for the next.
21
+ *
22
+ * Renders `emptyState` while `views` is empty, and disables the toolbar with it,
23
+ * so the frame keeps its size instead of collapsing.
24
+ *
25
+ * @component
26
+ * @example
27
+ * <ImageViewer
28
+ * title="Identity document"
29
+ * views={[
30
+ * { id: 'front', label: 'Front', src: '/front.jpg', alt: 'Document front' },
31
+ * { id: 'back', label: 'Back', src: '/back.jpg', alt: 'Document back' },
32
+ * ]}
33
+ * labels={{
34
+ * zoomIn: 'Zoom in',
35
+ * zoomOut: 'Zoom out',
36
+ * rotate: 'Rotate',
37
+ * enterFullscreen: 'Full screen',
38
+ * exitFullscreen: 'Exit full screen',
39
+ * }}
40
+ * emptyState={<p>Nothing uploaded yet</p>}
41
+ * />
42
+ *
43
+ * @param {Object} props - The properties of the ImageViewer component.
44
+ * @param {TImageView[]} props.views - Images to display. More than one renders a tab per view.
45
+ * @param {TImageViewerLabels} props.labels - Accessible names of the toolbar actions.
46
+ * @param {ReactNode} [props.title] - Optional heading rendered on the left of the toolbar.
47
+ * @param {ReactNode} [props.emptyState] - Content rendered when there is no image.
48
+ * @param {HTMLAttributes<HTMLElement>} [props.restProps] - Additional attributes for the root element.
49
+ *
50
+ * @returns {ReactNode} The rendered ImageViewer component.
51
+ */
52
+ export function ImageViewer({ views, labels, title, emptyState, ...restProps }) {
53
+ const [activeIndex, setActiveIndex] = useState(0);
54
+ const [zoom, setZoom] = useState(MIN_ZOOM);
55
+ const [rotation, setRotation] = useState(0);
56
+ const [isFullscreen, setIsFullscreen] = useState(false);
57
+ const generatedId = useId();
58
+ /**
59
+ * Se sigue la firma de las vistas y no el arreglo: un consumidor que lo
60
+ * declare en linea entrega una referencia nueva en cada render y el visor se
61
+ * reiniciaria mientras el usuario lo esta usando.
62
+ */
63
+ const viewsSignature = views.map(({ id }) => id).join('|');
64
+ useEffect(() => {
65
+ setActiveIndex(0);
66
+ setZoom(MIN_ZOOM);
67
+ setRotation(0);
68
+ }, [viewsSignature]);
69
+ useEffect(() => {
70
+ if (!isFullscreen)
71
+ return;
72
+ const handleKeyDown = (event) => {
73
+ if (event.key === 'Escape')
74
+ setIsFullscreen(false);
75
+ };
76
+ document.addEventListener('keydown', handleKeyDown);
77
+ return () => document.removeEventListener('keydown', handleKeyDown);
78
+ }, [isFullscreen]);
79
+ const activeView = views[activeIndex] ?? views[0];
80
+ const hasImage = Boolean(activeView);
81
+ const selectView = (index) => {
82
+ setActiveIndex(index);
83
+ setZoom(MIN_ZOOM);
84
+ setRotation(0);
85
+ };
86
+ const frame = (<section {...restProps} className={clsx('border-gray-5 dark:border-gray-1 flex min-h-0 flex-col overflow-hidden rounded-lg border', isFullscreen ? 'h-full w-full bg-white dark:bg-black' : undefined, !isFullscreen ? restProps.className : undefined)}>
87
+ <div className="border-gray-5 bg-gray-6 dark:border-gray-1 dark:bg-black-1 flex flex-none items-center justify-between gap-4 border-b px-4 py-3">
88
+ <Show>
89
+ <Show.When isTrue={Boolean(title)}>
90
+ <Paragraph size="subtitle-1" className="dark:text-white">
91
+ {title}
92
+ </Paragraph>
93
+ </Show.When>
94
+ </Show>
95
+ <div className="ml-auto flex items-center gap-1.5">
96
+ <button type="button" aria-label={labels.zoomOut} disabled={!hasImage || zoom <= MIN_ZOOM} onClick={() => setZoom((current) => current - ZOOM_STEP)} className={TOOL_CLASSNAME}>
97
+ <MdZoomOut aria-hidden="true"/>
98
+ </button>
99
+ <button type="button" aria-label={labels.zoomIn} disabled={!hasImage || zoom >= MAX_ZOOM} onClick={() => setZoom((current) => current + ZOOM_STEP)} className={TOOL_CLASSNAME}>
100
+ <MdZoomIn aria-hidden="true"/>
101
+ </button>
102
+ <button type="button" aria-label={labels.rotate} disabled={!hasImage} onClick={() => setRotation((current) => (current + QUARTER_TURN) % FULL_TURN)} className={TOOL_CLASSNAME}>
103
+ <MdRotateRight aria-hidden="true"/>
104
+ </button>
105
+ <button type="button" aria-label={isFullscreen ? labels.exitFullscreen : labels.enterFullscreen} aria-pressed={isFullscreen} disabled={!hasImage} onClick={() => setIsFullscreen((current) => !current)} className={TOOL_CLASSNAME}>
106
+ {isFullscreen ? (<MdFullscreenExit aria-hidden="true"/>) : (<MdFullscreen aria-hidden="true"/>)}
107
+ </button>
108
+ </div>
109
+ </div>
110
+
111
+ <Show>
112
+ <Show.When isTrue={views.length > 1}>
113
+ <List role="tablist" aria-orientation="horizontal" orientation="horizontal" className="border-gray-5 dark:border-gray-1 flex-none items-end gap-6 border-b px-4">
114
+ <Each of={views} render={({ id, label }, index) => (<li key={id} role="presentation">
115
+ <TabItem id={`${generatedId}-tab-${index}`} aria-controls={`${generatedId}-panel`} aria-selected={index === activeIndex} tabIndex={index === activeIndex ? 0 : -1} isActive={index === activeIndex} onClick={() => selectView(index)} className="pb-2">
116
+ {label}
117
+ </TabItem>
118
+ </li>)}/>
119
+ </List>
120
+ </Show.When>
121
+ </Show>
122
+
123
+ <div id={`${generatedId}-panel`} role={views.length > 1 ? 'tabpanel' : undefined} aria-labelledby={views.length > 1 ? `${generatedId}-tab-${activeIndex}` : undefined} className="bg-gray-7 dark:bg-black-2 flex min-h-0 flex-1 items-center justify-center overflow-hidden p-5">
124
+ <Show>
125
+ <Show.When isTrue={hasImage}>
126
+ {/**
127
+ * `next/image` exigiria que cada consumidor declare el dominio de
128
+ * sus imagenes en `remotePatterns`. Estas son cargas de usuario en
129
+ * almacenamiento externo, asi que la lista no se puede conocer.
130
+ */}
131
+ {/* eslint-disable-next-line @next/next/no-img-element */}
132
+ <img src={activeView?.src} alt={activeView?.alt} style={{ transform: `rotate(${rotation}deg) scale(${zoom})` }} className="max-h-full max-w-full object-contain transition-transform duration-200 ease-out"/>
133
+ </Show.When>
134
+ <Show.Else>{emptyState}</Show.Else>
135
+ </Show>
136
+ </div>
137
+ </section>);
138
+ if (!isFullscreen)
139
+ return frame;
140
+ return createPortal(<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-6">
141
+ <div className="h-full w-full max-w-5xl">{frame}</div>
142
+ </div>, document.body);
143
+ }
144
+ //# sourceMappingURL=ImageViewer.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageViewer.jsx","sourceRoot":"","sources":["../../../../src/components/02-molecules/ImageViewer/ImageViewer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './ImageViewer';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/ImageViewer/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './ImageViewer';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/02-molecules/ImageViewer/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { type HTMLAttributes, type ReactNode } from 'react';
2
+ export type TSideNavItem = {
3
+ id: string;
4
+ label: ReactNode;
5
+ indicator?: ReactNode;
6
+ isMuted?: boolean;
7
+ disabled?: boolean;
8
+ };
9
+ export type TSideNavGroup = {
10
+ id: string;
11
+ items: TSideNavItem[];
12
+ };
13
+ type SideNavProps = {
14
+ groups: TSideNavGroup[];
15
+ activeId?: string;
16
+ onSelect: (id: string) => void;
17
+ navigationLabel?: string;
18
+ } & Omit<HTMLAttributes<HTMLElement>, 'children' | 'onSelect'>;
19
+ /**
20
+ * SideNav component for navigating between the sections of a single page.
21
+ *
22
+ * Unlike `Menu`, it does not read the route: the consumer owns which section is
23
+ * active. That makes it usable for panels that live inside one page, where
24
+ * moving between sections must not push a history entry.
25
+ *
26
+ * Groups are separated by a rule and carry no heading, so a group of one row
27
+ * still reads as its own thing without inventing a label for it.
28
+ *
29
+ * @component
30
+ * @example
31
+ * <SideNav
32
+ * navigationLabel="Sections"
33
+ * activeId="profile"
34
+ * onSelect={setSection}
35
+ * groups={[
36
+ * { id: 'general', items: [{ id: 'profile', label: 'Profile data' }] },
37
+ * {
38
+ * id: 'documents',
39
+ * items: [
40
+ * { id: 'id-card', label: 'Identity document', indicator: <MdCheck /> },
41
+ * { id: 'license', label: 'Driver license', isMuted: true },
42
+ * ],
43
+ * },
44
+ * ]}
45
+ * />
46
+ *
47
+ * @param {Object} props - The properties of the SideNav component.
48
+ * @param {TSideNavGroup[]} props.groups - Groups of rows, rendered in order and separated by a rule.
49
+ * @param {string} [props.activeId] - Id of the current row.
50
+ * @param {Function} props.onSelect - Callback triggered with the id of the selected row.
51
+ * @param {string} [props.navigationLabel] - Accessible name of the navigation region.
52
+ * @param {HTMLAttributes<HTMLElement>} [props.restProps] - Additional attributes for the nav element.
53
+ *
54
+ * @returns {ReactNode} The rendered SideNav component.
55
+ */
56
+ export declare function SideNav({ groups, activeId, onSelect, navigationLabel, ...restProps }: SideNavProps): ReactNode;
57
+ export {};
58
+ //# sourceMappingURL=SideNav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SideNav.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/SideNav/SideNav.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,OAAO,CAAC,EACtB,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,GAAG,SAAS,EACb,EAAE,YAAY,GAAG,SAAS,CAqC1B"}
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+ import clsx from 'clsx';
3
+ import { Fragment } from 'react';
4
+ import { Each } from '../../00-base';
5
+ import { SideNavItem } from './SideNavItem';
6
+ /**
7
+ * SideNav component for navigating between the sections of a single page.
8
+ *
9
+ * Unlike `Menu`, it does not read the route: the consumer owns which section is
10
+ * active. That makes it usable for panels that live inside one page, where
11
+ * moving between sections must not push a history entry.
12
+ *
13
+ * Groups are separated by a rule and carry no heading, so a group of one row
14
+ * still reads as its own thing without inventing a label for it.
15
+ *
16
+ * @component
17
+ * @example
18
+ * <SideNav
19
+ * navigationLabel="Sections"
20
+ * activeId="profile"
21
+ * onSelect={setSection}
22
+ * groups={[
23
+ * { id: 'general', items: [{ id: 'profile', label: 'Profile data' }] },
24
+ * {
25
+ * id: 'documents',
26
+ * items: [
27
+ * { id: 'id-card', label: 'Identity document', indicator: <MdCheck /> },
28
+ * { id: 'license', label: 'Driver license', isMuted: true },
29
+ * ],
30
+ * },
31
+ * ]}
32
+ * />
33
+ *
34
+ * @param {Object} props - The properties of the SideNav component.
35
+ * @param {TSideNavGroup[]} props.groups - Groups of rows, rendered in order and separated by a rule.
36
+ * @param {string} [props.activeId] - Id of the current row.
37
+ * @param {Function} props.onSelect - Callback triggered with the id of the selected row.
38
+ * @param {string} [props.navigationLabel] - Accessible name of the navigation region.
39
+ * @param {HTMLAttributes<HTMLElement>} [props.restProps] - Additional attributes for the nav element.
40
+ *
41
+ * @returns {ReactNode} The rendered SideNav component.
42
+ */
43
+ export function SideNav({ groups, activeId, onSelect, navigationLabel, ...restProps }) {
44
+ return (<nav {...restProps} aria-label={navigationLabel ?? restProps['aria-label']} className={clsx('bg-gray-6 dark:bg-black-1 flex flex-col py-4', restProps.className)}>
45
+ <Each of={groups} render={({ id, items }, groupIndex) => (<Fragment key={id}>
46
+ {groupIndex > 0 && (<hr className="border-gray-5 dark:border-gray-1 mx-4 my-2"/>)}
47
+ <Each of={items} render={(item) => (<SideNavItem key={item.id} isActive={item.id === activeId} isMuted={item.isMuted} indicator={item.indicator} disabled={item.disabled} onClick={() => onSelect(item.id)}>
48
+ {item.label}
49
+ </SideNavItem>)}/>
50
+ </Fragment>)}/>
51
+ </nav>);
52
+ }
53
+ //# sourceMappingURL=SideNav.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SideNav.jsx","sourceRoot":"","sources":["../../../../src/components/02-molecules/SideNav/SideNav.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAuC,MAAM,OAAO,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAsB5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,OAAO,CAAC,EACtB,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,GAAG,SAAS,EACC;IACb,OAAO,CACL,CAAC,GAAG,CACF,IAAI,SAAS,CAAC,CACd,UAAU,CAAC,CAAC,eAAe,IAAI,SAAS,CAAC,YAAY,CAAC,CAAC,CACvD,SAAS,CAAC,CAAC,IAAI,CACb,8CAA8C,EAC9C,SAAS,CAAC,SAAS,CACpB,CAAC,CAEF;MAAA,CAAC,IAAI,CACH,EAAE,CAAC,CAAC,MAAM,CAAC,CACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,CACrC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAChB;YAAA,CAAC,UAAU,GAAG,CAAC,IAAI,CACjB,CAAC,EAAE,CAAC,SAAS,CAAC,4CAA4C,EAAG,CAC9D,CACD;YAAA,CAAC,IAAI,CACH,EAAE,CAAC,CAAC,KAAK,CAAC,CACV,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAChB,CAAC,WAAW,CACV,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACb,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAC/B,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CACtB,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAC1B,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CACxB,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAEjC;kBAAA,CAAC,IAAI,CAAC,KAAK,CACb;gBAAA,EAAE,WAAW,CAAC,CACf,CAAC,EAEN;UAAA,EAAE,QAAQ,CAAC,CACZ,CAAC,EAEN;IAAA,EAAE,GAAG,CAAC,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ export type SideNavItemProps = {
3
+ isActive?: boolean;
4
+ isMuted?: boolean;
5
+ indicator?: ReactNode;
6
+ } & ButtonHTMLAttributes<HTMLButtonElement>;
7
+ /**
8
+ * SideNavItem component for rendering a row of a vertical navigation rail.
9
+ *
10
+ * The active row is marked with a bar on the leading edge, the vertical
11
+ * counterpart of the bottom bar the horizontal navigation uses.
12
+ *
13
+ * `isMuted` is for rows that exist but have nothing behind them yet. It reads
14
+ * as unavailable without disabling the row, so it stays reachable.
15
+ *
16
+ * @component
17
+ * @example
18
+ * <SideNavItem
19
+ * isActive
20
+ * indicator={<MdCheck />}
21
+ * onClick={() => select('profile')}
22
+ * >
23
+ * Profile data
24
+ * </SideNavItem>
25
+ *
26
+ * @param {Object} props - The properties of the SideNavItem component.
27
+ * @param {boolean} [props.isActive] - Whether the row is the current one.
28
+ * @param {boolean} [props.isMuted] - Renders the row as available but empty.
29
+ * @param {ReactNode} [props.indicator] - Optional element aligned to the trailing edge.
30
+ * @param {ReactNode} props.children - The label of the row.
31
+ * @param {ButtonHTMLAttributes<HTMLButtonElement>} [props.restProps] - Additional attributes for the button.
32
+ *
33
+ * @returns {ReactNode} The rendered SideNavItem component.
34
+ */
35
+ export declare function SideNavItem({ children, isActive, isMuted, indicator, ...restProps }: SideNavItemProps): ReactNode;
36
+ //# sourceMappingURL=SideNavItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SideNavItem.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/SideNav/SideNavItem.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,EACT,GAAG,SAAS,EACb,EAAE,gBAAgB,GAAG,SAAS,CAoB9B"}
@@ -0,0 +1,38 @@
1
+ import clsx from 'clsx';
2
+ /**
3
+ * SideNavItem component for rendering a row of a vertical navigation rail.
4
+ *
5
+ * The active row is marked with a bar on the leading edge, the vertical
6
+ * counterpart of the bottom bar the horizontal navigation uses.
7
+ *
8
+ * `isMuted` is for rows that exist but have nothing behind them yet. It reads
9
+ * as unavailable without disabling the row, so it stays reachable.
10
+ *
11
+ * @component
12
+ * @example
13
+ * <SideNavItem
14
+ * isActive
15
+ * indicator={<MdCheck />}
16
+ * onClick={() => select('profile')}
17
+ * >
18
+ * Profile data
19
+ * </SideNavItem>
20
+ *
21
+ * @param {Object} props - The properties of the SideNavItem component.
22
+ * @param {boolean} [props.isActive] - Whether the row is the current one.
23
+ * @param {boolean} [props.isMuted] - Renders the row as available but empty.
24
+ * @param {ReactNode} [props.indicator] - Optional element aligned to the trailing edge.
25
+ * @param {ReactNode} props.children - The label of the row.
26
+ * @param {ButtonHTMLAttributes<HTMLButtonElement>} [props.restProps] - Additional attributes for the button.
27
+ *
28
+ * @returns {ReactNode} The rendered SideNavItem component.
29
+ */
30
+ export function SideNavItem({ children, isActive, isMuted, indicator, ...restProps }) {
31
+ return (<button type="button" {...restProps} aria-current={isActive || undefined} className={clsx('focus-visible:ring-lavender disabled:text-gray-3 dark:disabled:text-gray-1 flex min-h-[52px] w-full cursor-pointer items-center justify-between gap-2 border-l-2 px-4 py-2 text-left text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed', isActive
32
+ ? 'border-lavender font-medium text-black dark:text-white'
33
+ : 'hover:text-lavender dark:hover:text-lavender-dark border-transparent font-normal', !isActive && isMuted && 'text-gray-3 italic', !isActive && !isMuted && 'text-gray-1 dark:text-gray-2', restProps.className)}>
34
+ <span className="flex-1 leading-snug">{children}</span>
35
+ {!!indicator && <span className="flex-none">{indicator}</span>}
36
+ </button>);
37
+ }
38
+ //# sourceMappingURL=SideNavItem.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SideNavItem.jsx","sourceRoot":"","sources":["../../../../src/components/02-molecules/SideNav/SideNavItem.tsx"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AASxB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,EACT,GAAG,SAAS,EACK;IACjB,OAAO,CACL,CAAC,MAAM,CACL,IAAI,CAAC,QAAQ,CACb,IAAI,SAAS,CAAC,CACd,YAAY,CAAC,CAAC,QAAQ,IAAI,SAAS,CAAC,CACpC,SAAS,CAAC,CAAC,IAAI,CACb,0QAA0Q,EAC1Q,QAAQ;YACN,CAAC,CAAC,wDAAwD;YAC1D,CAAC,CAAC,kFAAkF,EACtF,CAAC,QAAQ,IAAI,OAAO,IAAI,oBAAoB,EAC5C,CAAC,QAAQ,IAAI,CAAC,OAAO,IAAI,8BAA8B,EACvD,SAAS,CAAC,SAAS,CACpB,CAAC,CAEF;MAAA,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CACtD;MAAA,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAChE;IAAA,EAAE,MAAM,CAAC,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './SideNav';
2
+ export * from './SideNavItem';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/SideNav/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './SideNav';
2
+ export * from './SideNavItem';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/02-molecules/SideNav/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC"}
@@ -2,6 +2,7 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
2
2
  export type TabItemProps = {
3
3
  icon?: ReactNode | string;
4
4
  isActive?: boolean;
5
+ count?: number | string;
5
6
  } & ButtonHTMLAttributes<HTMLButtonElement>;
6
7
  /**
7
8
  * TabItem component for rendering a tab item with optional icon and active state.
@@ -12,6 +13,7 @@ export type TabItemProps = {
12
13
  * <TabItem
13
14
  * icon={<SomeIcon />}
14
15
  * isActive={true}
16
+ * count={12}
15
17
  * className="custom-class"
16
18
  * >
17
19
  * Tab Content
@@ -20,10 +22,11 @@ export type TabItemProps = {
20
22
  * @param {Object} props - The properties of the TabItem component.
21
23
  * @param {ReactNode|string} [props.icon] - Optional icon to display alongside the tab content.
22
24
  * @param {boolean} [props.isActive] - Whether the tab item is currently active.
25
+ * @param {number|string} [props.count] - Optional counter rendered as plain text after the label.
23
26
  * @param {ReactNode} props.children - The content to be displayed inside the tab item.
24
27
  * @param {Object} [props.restProps] - Additional HTML attributes for the container div.
25
28
  *
26
29
  * @returns {ReactNode} The rendered TabItem component.
27
30
  */
28
- export declare function TabItem({ children, icon, isActive, ...restProps }: TabItemProps): ReactNode;
31
+ export declare function TabItem({ children, icon, isActive, count, ...restProps }: TabItemProps): ReactNode;
29
32
  //# sourceMappingURL=TabItem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TabItem.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/Tab/TabItem.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,GAAG,SAAS,EACb,EAAE,YAAY,GAAG,SAAS,CAiB1B"}
1
+ {"version":3,"file":"TabItem.d.ts","sourceRoot":"","sources":["../../../../src/components/02-molecules/Tab/TabItem.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7D,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB,GAAG,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,GAAG,SAAS,EACb,EAAE,YAAY,GAAG,SAAS,CA6B1B"}