@proef/common-ui 2.6.5 → 2.6.7

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 (38) hide show
  1. package/dist/esm/index.js +42 -33
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/legacy/components/layout/Spinner/Spinner.js +1 -15
  4. package/dist/esm/legacy/components/layout/Spinner/Spinner.js.map +1 -1
  5. package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.js +1 -1
  6. package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.js +1 -1
  7. package/dist/esm/legacy/components/organisms/LazyLoader/LazyLoader.js +1 -1
  8. package/dist/esm/legacy/components/organisms/Selector/Selector.js +1 -1
  9. package/dist/esm/v1/components/data/MonthCalendar/MonthCalendar.js +83 -0
  10. package/dist/esm/v1/components/data/MonthCalendar/MonthCalendar.js.map +1 -0
  11. package/dist/esm/v1/components/overlays/DropdownMenu/DropdownMenu.js +222 -0
  12. package/dist/esm/v1/components/overlays/DropdownMenu/DropdownMenu.js.map +1 -0
  13. package/dist/esm/v1/foundations/icons/icons.js +8 -0
  14. package/dist/esm/v1/foundations/icons/icons.js.map +1 -1
  15. package/dist/esm/v1/primitives/display/Badge/Badge.js +3 -2
  16. package/dist/esm/v1/primitives/display/Badge/Badge.js.map +1 -1
  17. package/dist/esm/v1/primitives/display/DescriptionList/DescriptionList.js +31 -0
  18. package/dist/esm/v1/primitives/display/DescriptionList/DescriptionList.js.map +1 -0
  19. package/dist/esm/v1/primitives/feedback/Spinner/Spinner.js +24 -0
  20. package/dist/esm/v1/primitives/feedback/Spinner/Spinner.js.map +1 -0
  21. package/dist/esm/v1/primitives/forms/DateInput/DateInput.js +3 -1
  22. package/dist/esm/v1/primitives/forms/DateInput/DateInput.js.map +1 -1
  23. package/dist/esm/v1/primitives/navigation/Steps/Steps.js +68 -0
  24. package/dist/esm/v1/primitives/navigation/Steps/Steps.js.map +1 -0
  25. package/dist/style.components.css +1 -1
  26. package/dist/style.css +1 -1
  27. package/dist/style.layered.css +1 -1
  28. package/dist/types/legacy/components/layout/Spinner/Spinner.d.ts +1 -4
  29. package/dist/types/v1/components/data/MonthCalendar/MonthCalendar.d.ts +37 -0
  30. package/dist/types/v1/components/overlays/DropdownMenu/DropdownMenu.d.ts +92 -0
  31. package/dist/types/v1/foundations/icons/icons.d.ts +2 -0
  32. package/dist/types/v1/index.d.ts +11 -1
  33. package/dist/types/v1/primitives/display/Badge/Badge.d.ts +2 -0
  34. package/dist/types/v1/primitives/display/DescriptionList/DescriptionList.d.ts +29 -0
  35. package/dist/types/v1/primitives/feedback/Spinner/Spinner.d.ts +20 -0
  36. package/dist/types/v1/primitives/navigation/Steps/Steps.d.ts +49 -0
  37. package/package.json +1 -1
  38. package/dist/esm/legacy/components/layout/Spinner/Spinner.css +0 -10
package/dist/esm/index.js CHANGED
@@ -19,6 +19,7 @@ import { default as default19, timestampDeData } from "./legacy/components/molec
19
19
  import { DateInput } from "./v1/primitives/forms/DateInput/DateInput.js";
20
20
  import { default as default20, useDialogContainer } from "./v1/components/overlays/Dialog/Dialog.js";
21
21
  import { default as default21, accepts } from "./v1/components/documents/DocumentDropzone/DocumentDropzone.js";
22
+ import { DEFAULT_DROPDOWN_MENU_LABELS, DropdownMenu } from "./v1/components/overlays/DropdownMenu/DropdownMenu.js";
22
23
  import { default as default22 } from "./legacy/components/layout/Error/Error.js";
23
24
  import { default as default23 } from "./legacy/pages/Error/ErrorPage.js";
24
25
  import { default as default24 } from "./legacy/components/ui/Escolha.js";
@@ -70,22 +71,22 @@ import { default as default66 } from "./legacy/components/molecules/SeletorPesqu
70
71
  import { default as default67 } from "./legacy/components/ui/Separadores.js";
71
72
  import { default as default68 } from "./v1/components/layout/SideNav/SideNav.js";
72
73
  import { default as default69 } from "./legacy/components/molecules/Sidebar/Sidebar.js";
73
- import { default as default70 } from "./legacy/components/layout/Spinner/Spinner.js";
74
- import { default as default71 } from "./legacy/components/atoms/Stack/Stack.js";
75
- import { default as default72 } from "./legacy/components/organisms/StatusMatrix/StatusMatrix.js";
76
- import { default as default73 } from "./v1/primitives/forms/Switch/Switch.js";
77
- import { default as default74 } from "./legacy/components/atoms/Button/variants/TabButton.js";
78
- import { default as default75 } from "./v1/components/tables/Table/Table.js";
79
- import { default as default76 } from "./v1/primitives/navigation/Tabs/Tabs.js";
80
- import { default as default77 } from "./v1/primitives/forms/TextArea/TextArea.js";
81
- import { default as default78 } from "./v1/primitives/forms/TextInput/TextInput.js";
82
- import { default as default79 } from "./legacy/components/ui/TextoAuto.js";
83
- import { default as default80, useToast } from "./v1/components/feedback/Toast/Toast.js";
84
- import { default as default81 } from "./v1/components/overlays/Tooltip/Tooltip.js";
85
- import { default as default82 } from "./v1/components/overlays/Tooltip/TooltipLayer.js";
86
- import { default as default83 } from "./legacy/components/layout/TopBar/TopBar.js";
87
- import { default as default84 } from "./legacy/components/layout/TopNavLayout/TopNavLayout.js";
88
- import { default as default85 } from "./legacy/components/molecules/UserAvatar/UserAvatar.js";
74
+ import { default as default70 } from "./legacy/components/atoms/Stack/Stack.js";
75
+ import { default as default71 } from "./legacy/components/organisms/StatusMatrix/StatusMatrix.js";
76
+ import { DEFAULT_STEPS_LABELS, Steps } from "./v1/primitives/navigation/Steps/Steps.js";
77
+ import { default as default72 } from "./v1/primitives/forms/Switch/Switch.js";
78
+ import { default as default73 } from "./legacy/components/atoms/Button/variants/TabButton.js";
79
+ import { default as default74 } from "./v1/components/tables/Table/Table.js";
80
+ import { default as default75 } from "./v1/primitives/navigation/Tabs/Tabs.js";
81
+ import { default as default76 } from "./v1/primitives/forms/TextArea/TextArea.js";
82
+ import { default as default77 } from "./v1/primitives/forms/TextInput/TextInput.js";
83
+ import { default as default78 } from "./legacy/components/ui/TextoAuto.js";
84
+ import { default as default79, useToast } from "./v1/components/feedback/Toast/Toast.js";
85
+ import { default as default80 } from "./v1/components/overlays/Tooltip/Tooltip.js";
86
+ import { default as default81 } from "./v1/components/overlays/Tooltip/TooltipLayer.js";
87
+ import { default as default82 } from "./legacy/components/layout/TopBar/TopBar.js";
88
+ import { default as default83 } from "./legacy/components/layout/TopNavLayout/TopNavLayout.js";
89
+ import { default as default84 } from "./legacy/components/molecules/UserAvatar/UserAvatar.js";
89
90
  import { Anel, Barras, BarrasEmpilhadas, BarrasHorizontais, Bolhas, Linha, Linhas, PALETA, cor } from "./legacy/components/charts/Charts.js";
90
91
  import { ApiProvider } from "./legacy/contexts/apiContext.js";
91
92
  import { AuthProvider, useAuth } from "./legacy/contexts/authContext.js";
@@ -95,15 +96,17 @@ import { AvisosProvider, useAvisos } from "./legacy/components/ui/Avisos.js";
95
96
  import { Badge } from "./v1/primitives/display/Badge/Badge.js";
96
97
  import { BadgeSla, BarraFiltros, Btn, BtnGroup, Campo, CampoInterruptor, CampoPesquisa, CampoSelect, ChipEstadoInterno, Estado, LinhaAlerta, ListaCampos, Modal, SectionGroup } from "./legacy/components/ui/Ui.js";
97
98
  import { BarChart, DonutChart, LineChart, shortDateLabel, useChartPalette } from "./v1/components/data/Charts/Charts.js";
98
- import { BoldIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, CloseIcon, ErrorIcon, ExternalLinkIcon, FilterIcon, GlobeIcon, ImageIcon, InfoIcon, ItalicIcon, LayersIcon, ListBulletIcon, ListIcon, ListNumberedIcon, LockIcon, LogOutIcon, MaximizeIcon, MenuIcon, MinimizeIcon, MonitorIcon, MoonIcon, PanelOpenIcon, PrefilledIcon, QrCodeIcon, QuoteIcon, SearchIcon, SlidersIcon, SparklesIcon, StrikethroughIcon, SunIcon, UnderlineIcon, WarningIcon } from "./v1/foundations/icons/icons.js";
99
+ import { BoldIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, CloseIcon, ErrorIcon, ExternalLinkIcon, FilterIcon, GlobeIcon, ImageIcon, InfoIcon, ItalicIcon, LayersIcon, ListBulletIcon, ListIcon, ListNumberedIcon, LockIcon, LogOutIcon, MaximizeIcon, MenuIcon, MinimizeIcon, MonitorIcon, MoonIcon, MoreIcon, PanelOpenIcon, PrefilledIcon, QrCodeIcon, QuoteIcon, SearchIcon, SlidersIcon, SparklesIcon, StrikethroughIcon, SunIcon, UnderlineIcon, WarningIcon } from "./v1/foundations/icons/icons.js";
99
100
  import { Card } from "./v1/primitives/display/Card/Card.js";
100
101
  import { DEFAULT_LIST_FINDER_LABELS } from "./v1/components/lists/ListFinder/labels.js";
101
102
  import { DEFAULT_TABLE_LABELS } from "./v1/components/tables/Table/labels.js";
103
+ import { DescriptionList } from "./v1/primitives/display/DescriptionList/DescriptionList.js";
102
104
  import { FilterChips } from "./v1/primitives/display/FilterChips/FilterChips.js";
103
105
  import { FontWeight } from "./legacy/constants/FontWeight.js";
104
106
  import { InputData, InputHora } from "./legacy/components/atoms/InputData/InputData.js";
105
107
  import { Kpi } from "./v1/primitives/display/Kpi/Kpi.js";
106
108
  import { LazyLoader } from "./legacy/components/organisms/LazyLoader/LazyLoader.js";
109
+ import { MonthCalendar } from "./v1/components/data/MonthCalendar/MonthCalendar.js";
107
110
  import { Notice } from "./v1/primitives/display/Notice/Notice.js";
108
111
  import { Pagination } from "./v1/components/data/Pagination/Pagination.js";
109
112
  import { PeopleFilter } from "./v1/primitives/display/PeopleFilter/PeopleFilter.js";
@@ -166,13 +169,17 @@ export {
166
169
  ConfirmProvider,
167
170
  default17 as Confirmacao,
168
171
  default18 as CustomSwitch,
172
+ DEFAULT_DROPDOWN_MENU_LABELS,
169
173
  DEFAULT_LIST_FINDER_LABELS,
174
+ DEFAULT_STEPS_LABELS,
170
175
  DEFAULT_TABLE_LABELS,
171
176
  default19 as DataTable,
172
177
  DateInput,
178
+ DescriptionList,
173
179
  default20 as Dialog,
174
180
  default21 as DocumentDropzone,
175
181
  DonutChart,
182
+ DropdownMenu,
176
183
  default22 as Error,
177
184
  ErrorIcon,
178
185
  default23 as ErrorPage,
@@ -234,7 +241,9 @@ export {
234
241
  MinimizeIcon,
235
242
  default50 as Modal,
236
243
  MonitorIcon,
244
+ MonthCalendar,
237
245
  MoonIcon,
246
+ MoreIcon,
238
247
  default51 as MultiTagPicker,
239
248
  default52 as NotFoundPage,
240
249
  Notice,
@@ -274,26 +283,26 @@ export {
274
283
  SlidersIcon,
275
284
  SocketProvider,
276
285
  SparklesIcon,
277
- default70 as Spinner,
278
- default71 as Stack,
279
- default72 as StatusMatrix,
286
+ default70 as Stack,
287
+ default71 as StatusMatrix,
288
+ Steps,
280
289
  StrikethroughIcon,
281
290
  SunIcon,
282
- default73 as Switch,
291
+ default72 as Switch,
283
292
  THEME_STORAGE_KEY,
284
- default74 as TabButton,
285
- default75 as Table,
286
- default76 as Tabs,
287
- default77 as TextArea,
288
- default78 as TextInput,
289
- default79 as TextoAuto,
290
- default80 as ToastProvider,
291
- default81 as Tooltip,
292
- default82 as TooltipLayer,
293
- default83 as TopBar,
294
- default84 as TopNavLayout,
293
+ default73 as TabButton,
294
+ default74 as Table,
295
+ default75 as Tabs,
296
+ default76 as TextArea,
297
+ default77 as TextInput,
298
+ default78 as TextoAuto,
299
+ default79 as ToastProvider,
300
+ default80 as Tooltip,
301
+ default81 as TooltipLayer,
302
+ default82 as TopBar,
303
+ default83 as TopNavLayout,
295
304
  UnderlineIcon,
296
- default85 as UserAvatar,
305
+ default84 as UserAvatar,
297
306
  WarningIcon,
298
307
  applyColumnLayout,
299
308
  applyTheme,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,18 +1,4 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { faSpinner } from "@fortawesome/free-solid-svg-icons";
4
- import './Spinner.css';/* empty css */
5
- const Spinner = () => {
6
- return /* @__PURE__ */ jsx("div", { className: "cui-spinner", children: /* @__PURE__ */ jsx(
7
- FontAwesomeIcon,
8
- {
9
- icon: faSpinner,
10
- className: "cui-icon",
11
- spin: true,
12
- "aria-hidden": "true"
13
- }
14
- ) });
15
- };
1
+ import { Spinner } from "../../../../v1/primitives/feedback/Spinner/Spinner.js";
16
2
  export {
17
3
  Spinner as default
18
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Spinner.js","sources":["../../../../../../src/legacy/components/layout/Spinner/Spinner.tsx"],"sourcesContent":["import React from 'react'\r\n\r\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\r\nimport { faSpinner } from '@fortawesome/free-solid-svg-icons'\r\nimport './Spinner.scss'\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nconst Spinner: React.FC = () => {\r\n return (\r\n <div className=\"cui-spinner\">\r\n <FontAwesomeIcon\r\n icon={faSpinner}\r\n className={'cui-icon'}\r\n spin\r\n aria-hidden=\"true\"\r\n />\r\n </div>\r\n )\r\n}\r\n\r\nexport default Spinner\r\n"],"names":[],"mappings":";;;;AAOA,MAAM,UAAoB,MAAM;AAC9B,SACE,oBAAC,OAAA,EAAI,WAAU,eACb,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM;AAAA,MACN,WAAW;AAAA,MACX,MAAI;AAAA,MACJ,eAAY;AAAA,IAAA;AAAA,EAAA,GAEhB;AAEJ;"}
1
+ {"version":3,"file":"Spinner.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -2,7 +2,7 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { faCheck } from "@fortawesome/free-solid-svg-icons";
3
3
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
4
  import Modal from "../Modal/Modal.js";
5
- import Spinner from "../../layout/Spinner/Spinner.js";
5
+ import { Spinner } from "../../../../v1/primitives/feedback/Spinner/Spinner.js";
6
6
  import { ActionButtonThemeStyled } from "../../../styles/themes/ButtonThemes.js";
7
7
  import './ConfirmModal.css';/* empty css */
8
8
  import ActionButton from "../../atoms/Button/variants/ActionButton.js";
@@ -5,7 +5,7 @@ import { faCircleMinus, faMagnifyingGlass } from "@fortawesome/free-solid-svg-ic
5
5
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
6
6
  import { Color } from "../../../constants/Colors.js";
7
7
  import Select from "../../atoms/MultiTagPickerTag/MultiTagPickerTag.js";
8
- import Spinner from "../../layout/Spinner/Spinner.js";
8
+ import { Spinner } from "../../../../v1/primitives/feedback/Spinner/Spinner.js";
9
9
  import './MultiTagPicker.css';/* empty css */
10
10
  const MultiTagPicker = (props) => {
11
11
  const {
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { useState, useRef, useEffect } from "react";
3
3
  import InfiniteScroll from "react-infinite-scroll-component";
4
4
  import { useApi } from "../../../hooks/useApi.js";
5
- import Spinner from "../../layout/Spinner/Spinner.js";
5
+ import { Spinner } from "../../../../v1/primitives/feedback/Spinner/Spinner.js";
6
6
  const LazyLoader = ({
7
7
  endpoint,
8
8
  params = {},
@@ -6,7 +6,7 @@ import Modal from "../../molecules/Modal/Modal.js";
6
6
  import SearchInput from "../../../../v1/primitives/forms/SearchInput/SearchInput.js";
7
7
  import { LazyLoader } from "../LazyLoader/LazyLoader.js";
8
8
  import './Selector.css';/* empty css */
9
- import Spinner from "../../layout/Spinner/Spinner.js";
9
+ import { Spinner } from "../../../../v1/primitives/feedback/Spinner/Spinner.js";
10
10
  import Tooltip from "../../../../v1/components/overlays/Tooltip/Tooltip.js";
11
11
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
12
12
  import ActionButton from "../../atoms/Button/variants/ActionButton.js";
@@ -0,0 +1,83 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { BADGE_TONE_CLASSES } from "../../../primitives/display/Badge/Badge.js";
4
+ const pad = (n) => String(n).padStart(2, "0");
5
+ const hoje = () => {
6
+ const d = /* @__PURE__ */ new Date();
7
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
8
+ };
9
+ function MonthCalendar({
10
+ year,
11
+ month,
12
+ days = [],
13
+ title,
14
+ locale = "pt-PT",
15
+ weekStartsOn = 1,
16
+ today,
17
+ className = "",
18
+ ...props
19
+ }) {
20
+ const prefixo = `${year}-${pad(month)}-`;
21
+ const marcados = React.useMemo(() => {
22
+ const m = /* @__PURE__ */ new Map();
23
+ for (const d of days) {
24
+ if (!d.date.startsWith(prefixo)) continue;
25
+ const atual = m.get(d.date);
26
+ if (atual) {
27
+ if (d.label) atual.labels.push(d.label);
28
+ } else {
29
+ m.set(d.date, { tone: d.tone, labels: d.label ? [d.label] : [] });
30
+ }
31
+ }
32
+ return m;
33
+ }, [days, prefixo]);
34
+ const nomes = React.useMemo(() => {
35
+ const utc = { timeZone: "UTC" };
36
+ const primeiro = weekStartsOn === 1 ? 5 : 4;
37
+ const semana = new Intl.DateTimeFormat(locale, { ...utc, weekday: "narrow" });
38
+ return {
39
+ mes: new Intl.DateTimeFormat(locale, { ...utc, month: "long" }).format(new Date(Date.UTC(year, month - 1, 1))),
40
+ semana: Array.from({ length: 7 }, (_, i) => semana.format(new Date(Date.UTC(2026, 0, primeiro + i)))),
41
+ longo: new Intl.DateTimeFormat(locale, { ...utc, day: "numeric", month: "long" })
42
+ };
43
+ }, [locale, weekStartsOn, year, month]);
44
+ const total = new Date(Date.UTC(year, month, 0)).getUTCDate();
45
+ const inicio = (new Date(Date.UTC(year, month - 1, 1)).getUTCDay() - weekStartsOn + 7) % 7;
46
+ const diaHoje = today === void 0 ? hoje() : today;
47
+ const cabecalho = title === void 0 ? nomes.mes : title;
48
+ return /* @__PURE__ */ jsxs("section", { ...props, "aria-label": `${nomes.mes} ${year}`, className: `min-w-[16rem] rounded-lg border border-border bg-card p-3 ${className}`, children: [
49
+ cabecalho !== null && /* @__PURE__ */ jsx("h3", { className: "mb-2 text-center text-sm font-semibold capitalize text-foreground", children: cabecalho }),
50
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[repeat(7,minmax(2rem,1fr))] gap-0.5 text-center text-xs tabular-nums", children: [
51
+ nomes.semana.map((s, i) => /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "pb-1 font-medium uppercase text-muted-foreground", children: s }, i)),
52
+ Array.from({ length: inicio }, (_, i) => /* @__PURE__ */ jsx("span", { "aria-hidden": true }, `v${i}`)),
53
+ Array.from({ length: total }, (_, i) => {
54
+ const dia = i + 1;
55
+ const data = `${prefixo}${pad(dia)}`;
56
+ const coluna = (inicio + i) % 7;
57
+ const fimDeSemana = weekStartsOn === 1 ? coluna >= 5 : coluna === 0 || coluna === 6;
58
+ const marca = marcados.get(data);
59
+ const texto = marca?.labels.join(" · ");
60
+ return /* @__PURE__ */ jsx(
61
+ "span",
62
+ {
63
+ "data-date": data,
64
+ "data-tip": texto || void 0,
65
+ "aria-label": texto ? `${nomes.longo.format(new Date(Date.UTC(year, month - 1, dia)))}: ${texto}` : void 0,
66
+ className: [
67
+ "flex h-7 items-center justify-center rounded-md",
68
+ marca ? `${BADGE_TONE_CLASSES[marca.tone]} font-semibold` : fimDeSemana ? "text-muted-foreground" : "text-foreground",
69
+ data === diaHoje ? "ring-1 ring-primary dark:ring-sky-300" : "",
70
+ texto ? "cursor-help" : ""
71
+ ].join(" "),
72
+ children: dia
73
+ },
74
+ data
75
+ );
76
+ })
77
+ ] })
78
+ ] });
79
+ }
80
+ export {
81
+ MonthCalendar
82
+ };
83
+ //# sourceMappingURL=MonthCalendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MonthCalendar.js","sources":["../../../../../../src/v1/components/data/MonthCalendar/MonthCalendar.tsx"],"sourcesContent":["import React from 'react'\r\nimport { BADGE_TONE_CLASSES, type BadgeTone } from '../../../primitives/display/Badge/Badge'\r\n\r\n/** Os mesmos tons do `Badge`: estados (`ok`, `risco`, `incumprido`, `neutral`, `accent`) e classificações. */\r\nexport type MonthCalendarTone = BadgeTone\r\n\r\nexport interface MonthCalendarDay {\r\n /** `YYYY-MM-DD`. Os dias de outros meses são ignorados, por isso pode passar-se a lista do ano inteiro. */\r\n date: string\r\n tone: MonthCalendarTone\r\n /** O que o dia tem (ex: \"Férias · Autorizado\"); vai para o `data-tip` e para os leitores de ecrã. */\r\n label?: string\r\n}\r\n\r\nexport interface MonthCalendarProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children' | 'title'> {\r\n year: number\r\n /** 1 = janeiro … 12 = dezembro. */\r\n month: number\r\n /** Dias marcados. Vários na mesma data: fica a cor do primeiro e juntam-se as etiquetas. */\r\n days?: MonthCalendarDay[]\r\n /** Título por cima dos dias. Por omissão o nome do mês; `null` para nenhum. */\r\n title?: React.ReactNode | null\r\n /** Nomes do mês e dos dias da semana. */\r\n locale?: string\r\n /** 1 = segunda (Portugal), 0 = domingo. */\r\n weekStartsOn?: 0 | 1\r\n /** Dia com contorno. Por omissão o de hoje; `null` para nenhum. */\r\n today?: string | null\r\n}\r\n\r\nconst pad = (n: number) => String(n).padStart(2, '0')\r\nconst hoje = () => {\r\n const d = new Date()\r\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`\r\n}\r\n\r\n/**\r\n * Um mês, com os dias pintados por tom (ex: férias, feriados, ausências).\r\n * Para o ano inteiro, um por mês numa grelha (ver a story \"Ano inteiro\").\r\n *\r\n * Genérico: não sabe o que são férias. A app decide a cor e a etiqueta de cada\r\n * dia; o calendário só desenha. A legenda faz-se com `Badge`, nos mesmos tons.\r\n *\r\n * **Aceita os atributos HTML do `<section>`** (ex: `id`, `className`).\r\n */\r\nexport default function MonthCalendar({\r\n year,\r\n month,\r\n days = [],\r\n title,\r\n locale = 'pt-PT',\r\n weekStartsOn = 1,\r\n today,\r\n className = '',\r\n ...props\r\n}: MonthCalendarProps) {\r\n const prefixo = `${year}-${pad(month)}-`\r\n\r\n const marcados = React.useMemo(() => {\r\n const m = new Map<string, { tone: MonthCalendarTone; labels: string[] }>()\r\n for (const d of days) {\r\n if (!d.date.startsWith(prefixo)) continue\r\n const atual = m.get(d.date)\r\n if (atual) {\r\n if (d.label) atual.labels.push(d.label)\r\n } else {\r\n m.set(d.date, { tone: d.tone, labels: d.label ? [d.label] : [] })\r\n }\r\n }\r\n return m\r\n }, [days, prefixo])\r\n\r\n const nomes = React.useMemo(() => {\r\n const utc = { timeZone: 'UTC' } as const\r\n // 05/01/2026 é segunda; 04/01/2026 domingo.\r\n const primeiro = weekStartsOn === 1 ? 5 : 4\r\n const semana = new Intl.DateTimeFormat(locale, { ...utc, weekday: 'narrow' })\r\n return {\r\n mes: new Intl.DateTimeFormat(locale, { ...utc, month: 'long' }).format(new Date(Date.UTC(year, month - 1, 1))),\r\n semana: Array.from({ length: 7 }, (_, i) => semana.format(new Date(Date.UTC(2026, 0, primeiro + i)))),\r\n longo: new Intl.DateTimeFormat(locale, { ...utc, day: 'numeric', month: 'long' }),\r\n }\r\n }, [locale, weekStartsOn, year, month])\r\n\r\n const total = new Date(Date.UTC(year, month, 0)).getUTCDate()\r\n const inicio = (new Date(Date.UTC(year, month - 1, 1)).getUTCDay() - weekStartsOn + 7) % 7\r\n const diaHoje = today === undefined ? hoje() : today\r\n const cabecalho = title === undefined ? nomes.mes : title\r\n\r\n // Mínimo de 7 dias × 2rem: num sítio estreito o mês não encolhe ao ponto de os números se sobreporem.\r\n return (\r\n <section {...props} aria-label={`${nomes.mes} ${year}`} className={`min-w-[16rem] rounded-lg border border-border bg-card p-3 ${className}`}>\r\n {cabecalho !== null && (\r\n <h3 className=\"mb-2 text-center text-sm font-semibold capitalize text-foreground\">{cabecalho}</h3>\r\n )}\r\n <div className=\"grid grid-cols-[repeat(7,minmax(2rem,1fr))] gap-0.5 text-center text-xs tabular-nums\">\r\n {nomes.semana.map((s, i) => (\r\n <span key={i} aria-hidden className=\"pb-1 font-medium uppercase text-muted-foreground\">{s}</span>\r\n ))}\r\n {Array.from({ length: inicio }, (_, i) => <span key={`v${i}`} aria-hidden />)}\r\n {Array.from({ length: total }, (_, i) => {\r\n const dia = i + 1\r\n const data = `${prefixo}${pad(dia)}`\r\n const coluna = (inicio + i) % 7\r\n const fimDeSemana = weekStartsOn === 1 ? coluna >= 5 : coluna === 0 || coluna === 6\r\n const marca = marcados.get(data)\r\n const texto = marca?.labels.join(' · ')\r\n return (\r\n <span\r\n key={data}\r\n data-date={data}\r\n data-tip={texto || undefined}\r\n aria-label={texto ? `${nomes.longo.format(new Date(Date.UTC(year, month - 1, dia)))}: ${texto}` : undefined}\r\n className={[\r\n 'flex h-7 items-center justify-center rounded-md',\r\n marca ? `${BADGE_TONE_CLASSES[marca.tone]} font-semibold` : fimDeSemana ? 'text-muted-foreground' : 'text-foreground',\r\n data === diaHoje ? 'ring-1 ring-primary dark:ring-sky-300' : '',\r\n texto ? 'cursor-help' : '',\r\n ].join(' ')}\r\n >\r\n {dia}\r\n </span>\r\n )\r\n })}\r\n </div>\r\n </section>\r\n )\r\n}\r\n\r\nexport { MonthCalendar }\r\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import React, { useId, useRef, useMemo, useState, useEffect } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import Button from "../../../primitives/buttons/Button/Button.js";
5
+ import { ChevronDownIcon, MoreIcon, CheckIcon } from "../../../foundations/icons/icons.js";
6
+ import { useFloatingPanel, PANEL_CLASS, itemClass } from "../../../capabilities/overlay/useFloatingPanel.js";
7
+ import { useDialogContainer } from "../Dialog/Dialog.js";
8
+ const DEFAULT_DROPDOWN_MENU_LABELS = {
9
+ more: "More actions",
10
+ search: "Search…",
11
+ noResults: "No results",
12
+ empty: "Nothing here yet"
13
+ };
14
+ const textOf = (item) => item.searchText ?? (typeof item.label === "string" || typeof item.label === "number" ? String(item.label) : "");
15
+ const isTyping = (el) => el instanceof HTMLElement && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable);
16
+ function DropdownMenu({
17
+ items,
18
+ label,
19
+ icon,
20
+ "aria-label": ariaLabel,
21
+ variant,
22
+ size = "md",
23
+ active,
24
+ align,
25
+ disabled = false,
26
+ header,
27
+ searchable,
28
+ labels,
29
+ panelClassName = "",
30
+ className = "",
31
+ onOpenChange
32
+ }) {
33
+ const L = { ...DEFAULT_DROPDOWN_MENU_LABELS, ...labels };
34
+ const hasText = label !== void 0 && label !== null && label !== false;
35
+ const p = useFloatingPanel(align ?? (hasText ? "left" : "right"));
36
+ const dialogContainer = useDialogContainer();
37
+ const menuId = useId();
38
+ const searchRef = useRef(null);
39
+ const list = useMemo(() => items.filter((i) => !!i), [items]);
40
+ const withSearch = searchable ?? list.length > 10;
41
+ const [query, setQuery] = useState("");
42
+ const visible = useMemo(() => {
43
+ const q = query.trim().toLowerCase();
44
+ return q ? list.filter((i) => textOf(i).toLowerCase().includes(q)) : list;
45
+ }, [list, query]);
46
+ const enabled = visible.map((i, n) => i.disabled ? -1 : n).filter((n) => n >= 0);
47
+ const [activeIndex, setActiveIndex] = useState(-1);
48
+ const changed = useRef(onOpenChange);
49
+ changed.current = onOpenChange;
50
+ const first = useRef(true);
51
+ useEffect(() => {
52
+ if (first.current) {
53
+ first.current = false;
54
+ return;
55
+ }
56
+ changed.current?.(p.open);
57
+ }, [p.open]);
58
+ useEffect(() => {
59
+ if (!p.open) return;
60
+ setQuery("");
61
+ setActiveIndex(-1);
62
+ if (withSearch) setTimeout(() => searchRef.current?.focus(), 0);
63
+ }, [p.open]);
64
+ useEffect(() => {
65
+ if (p.open && activeIndex >= 0) p.panelRef.current?.querySelector(`[data-index="${activeIndex}"]`)?.scrollIntoView?.({ block: "nearest" });
66
+ }, [activeIndex, p.open, p.panelRef]);
67
+ if (list.length === 0 && !header) return null;
68
+ const close = () => {
69
+ p.close();
70
+ p.triggerRef.current?.focus();
71
+ };
72
+ const choose = (item) => {
73
+ if (item.disabled) return;
74
+ if (item.checked === void 0) close();
75
+ item.onSelect?.();
76
+ };
77
+ const move = (step) => {
78
+ if (enabled.length === 0) return;
79
+ const at = enabled.indexOf(activeIndex);
80
+ const next = step === "first" ? enabled[0] : step === "last" ? enabled[enabled.length - 1] : at === -1 ? step === 1 ? enabled[0] : enabled[enabled.length - 1] : enabled[Math.min(enabled.length - 1, Math.max(0, at + step))];
81
+ setActiveIndex(next);
82
+ };
83
+ const keys = (e) => {
84
+ if (!p.open) {
85
+ if (["ArrowDown", "ArrowUp"].includes(e.key)) {
86
+ e.preventDefault();
87
+ p.setOpen(true);
88
+ }
89
+ return;
90
+ }
91
+ if (e.key === "Escape") {
92
+ e.preventDefault();
93
+ e.stopPropagation();
94
+ close();
95
+ return;
96
+ }
97
+ if (e.key === "Tab") {
98
+ p.close();
99
+ return;
100
+ }
101
+ if (isTyping(e.target) && e.target !== searchRef.current) return;
102
+ if (e.key === "ArrowDown") {
103
+ e.preventDefault();
104
+ move(1);
105
+ } else if (e.key === "ArrowUp") {
106
+ e.preventDefault();
107
+ move(-1);
108
+ } else if (e.key === "Home" && e.target !== searchRef.current) {
109
+ e.preventDefault();
110
+ move("first");
111
+ } else if (e.key === "End" && e.target !== searchRef.current) {
112
+ e.preventDefault();
113
+ move("last");
114
+ } else if (e.key === "Enter" || e.key === " " && e.target !== searchRef.current) {
115
+ const item = visible[activeIndex];
116
+ if (item) {
117
+ e.preventDefault();
118
+ choose(item);
119
+ }
120
+ }
121
+ };
122
+ const name = ariaLabel ?? (hasText ? void 0 : L.more);
123
+ return /* @__PURE__ */ jsxs("div", { className: `relative inline-flex ${className}`, onKeyDown: keys, children: [
124
+ /* @__PURE__ */ jsx(
125
+ Button,
126
+ {
127
+ ref: p.triggerRef,
128
+ variant: variant ?? (hasText ? "secondary" : "ghost"),
129
+ size,
130
+ active,
131
+ disabled,
132
+ icon: hasText ? icon : icon ?? /* @__PURE__ */ jsx(MoreIcon, { size: size === "sm" ? 14 : 16 }),
133
+ "aria-label": name,
134
+ "data-tip": hasText ? void 0 : name,
135
+ "aria-haspopup": "menu",
136
+ "aria-expanded": p.open,
137
+ "aria-controls": p.open ? menuId : void 0,
138
+ onClick: (e) => {
139
+ e.stopPropagation();
140
+ p.setOpen((o) => !o);
141
+ },
142
+ children: hasText ? /* @__PURE__ */ jsxs(Fragment, { children: [
143
+ label,
144
+ /* @__PURE__ */ jsx(ChevronDownIcon, { size: 12, className: `shrink-0 transition-transform ${p.open ? "rotate-180" : ""}` })
145
+ ] }) : void 0
146
+ }
147
+ ),
148
+ p.open && createPortal(
149
+ /* @__PURE__ */ jsxs("div", { ref: p.panelRef, style: p.style, className: `${PANEL_CLASS} ${panelClassName}`, onClick: (e) => e.stopPropagation(), children: [
150
+ header && /* @__PURE__ */ jsx("div", { className: "border-b border-slate-100 px-2 pt-1 pb-2 dark:border-slate-800", children: header }),
151
+ withSearch && /* @__PURE__ */ jsx("div", { className: "px-2 pt-1 pb-1", children: /* @__PURE__ */ jsx(
152
+ "input",
153
+ {
154
+ ref: searchRef,
155
+ type: "search",
156
+ value: query,
157
+ onChange: (e) => {
158
+ setQuery(e.target.value);
159
+ setActiveIndex(-1);
160
+ },
161
+ placeholder: L.search,
162
+ "aria-label": L.search,
163
+ className: "h-8 w-full rounded-lg border border-slate-300 bg-white px-2.5 text-sm outline-none focus:border-proef-blue dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100"
164
+ }
165
+ ) }),
166
+ /* @__PURE__ */ jsxs("div", { id: menuId, role: "menu", "aria-label": typeof label === "string" ? label : name, children: [
167
+ visible.length === 0 && /* @__PURE__ */ jsx("p", { className: "m-0 px-3 py-2 text-slate-400", children: list.length === 0 ? L.empty : L.noResults }),
168
+ visible.map((item, i) => {
169
+ const checkable = item.checked !== void 0;
170
+ const mark = checkable ? item.checked : item.selected;
171
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
172
+ item.separator && i > 0 && /* @__PURE__ */ jsx("div", { role: "separator", className: "my-1 border-t border-slate-100 dark:border-slate-800" }),
173
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", "data-index": i, children: [
174
+ /* @__PURE__ */ jsxs(
175
+ "button",
176
+ {
177
+ type: "button",
178
+ role: checkable ? "menuitemcheckbox" : item.selected !== void 0 ? "menuitemradio" : "menuitem",
179
+ "aria-checked": checkable ? item.checked : item.selected !== void 0 ? item.selected : void 0,
180
+ disabled: item.disabled,
181
+ tabIndex: -1,
182
+ onMouseEnter: () => !item.disabled && setActiveIndex(i),
183
+ onClick: () => choose(item),
184
+ className: `min-w-0 flex-1 appearance-none border-0 ${itemClass(i === activeIndex, item.danger)} disabled:cursor-not-allowed disabled:opacity-40`,
185
+ children: [
186
+ item.icon && /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: item.icon }),
187
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
188
+ /* @__PURE__ */ jsx("span", { className: `block truncate ${mark ? "font-semibold" : ""}`, children: item.label }),
189
+ item.description && /* @__PURE__ */ jsx("span", { className: "block truncate text-[11px] text-slate-500 dark:text-slate-400", children: item.description })
190
+ ] }),
191
+ (checkable || item.selected !== void 0) && /* @__PURE__ */ jsx("span", { className: "w-4 shrink-0", children: mark && /* @__PURE__ */ jsx(CheckIcon, { size: 16, strokeWidth: 2.5, className: "text-proef-blue dark:text-sky-300" }) })
192
+ ]
193
+ }
194
+ ),
195
+ item.action && /* @__PURE__ */ jsx(
196
+ Button,
197
+ {
198
+ variant: "ghost",
199
+ size: "sm",
200
+ icon: item.action.icon,
201
+ "aria-label": item.action.label,
202
+ "data-tip": item.action.label,
203
+ tabIndex: -1,
204
+ onClick: item.action.onSelect,
205
+ className: "mr-1 shrink-0"
206
+ }
207
+ )
208
+ ] })
209
+ ] }, i);
210
+ })
211
+ ] })
212
+ ] }),
213
+ dialogContainer ?? document.body
214
+ )
215
+ ] });
216
+ }
217
+ export {
218
+ DEFAULT_DROPDOWN_MENU_LABELS,
219
+ DropdownMenu,
220
+ DropdownMenu as default
221
+ };
222
+ //# sourceMappingURL=DropdownMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropdownMenu.js","sources":["../../../../../../src/v1/components/overlays/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["import React, { useEffect, useId, useMemo, useRef, useState } from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport Button, { type ButtonProps } from '../../../primitives/buttons/Button/Button'\r\nimport { CheckIcon, ChevronDownIcon, MoreIcon } from '../../../foundations/icons/icons'\r\nimport { PANEL_CLASS, itemClass, useFloatingPanel } from '../../../capabilities/overlay/useFloatingPanel'\r\nimport { useDialogContainer } from '../Dialog/Dialog'\r\n\r\nexport interface DropdownMenuItemAction {\r\n /** Ícone do botão pequeno (ex: um caixote do lixo). */\r\n icon: React.ReactNode\r\n /** Nome do botão, para o tooltip e leitores de ecrã (ex: \"Apagar vista\"). */\r\n label: string\r\n onSelect: () => void\r\n}\r\n\r\nexport interface DropdownMenuItem {\r\n /** Normalmente texto; aceita JSX. */\r\n label: React.ReactNode\r\n /** Linha pequena por baixo do texto. */\r\n description?: React.ReactNode\r\n /** Ícone à esquerda (ex: um ícone do lucide com `size={14}`). */\r\n icon?: React.ReactNode\r\n /** O que o item faz. Um item normal fecha o menu ao ser escolhido. */\r\n onSelect?: () => void\r\n /**\r\n * Item de marcar (✓ quando `true`). O menu fica aberto para se marcarem\r\n * vários (ex: filtrar por tipos). `false` = por marcar, `undefined` = não é de marcar.\r\n */\r\n checked?: boolean\r\n /** A opção atual numa escolha de uma só (ex: a vista aplicada): ✓, e escolher fecha o menu como uma ação. */\r\n selected?: boolean\r\n /** Apaga ou não se pode desfazer: a vermelho. */\r\n danger?: boolean\r\n disabled?: boolean\r\n /** Linha a separar este item do anterior. */\r\n separator?: boolean\r\n /** Texto para a pesquisa quando o `label` não é texto. */\r\n searchText?: string\r\n /** Botão pequeno à direita do item (ex: apagar uma vista guardada). Não fecha o menu. */\r\n action?: DropdownMenuItemAction\r\n}\r\n\r\nexport interface DropdownMenuLabels {\r\n /** Nome do botão sem texto (⋯). */\r\n more: string\r\n /** Texto da caixa de pesquisa. */\r\n search: string\r\n /** Pesquisa sem resultados. */\r\n noResults: string\r\n /** DropdownMenu sem itens (só aparece com `header`; sem `header` o menu nem se mostra). */\r\n empty: string\r\n}\r\n\r\nexport const DEFAULT_DROPDOWN_MENU_LABELS: DropdownMenuLabels = {\r\n more: 'More actions',\r\n search: 'Search…',\r\n noResults: 'No results',\r\n empty: 'Nothing here yet',\r\n}\r\n\r\nexport interface DropdownMenuProps {\r\n /** Os itens; `false`, `null` e `undefined` são ignorados (para `cond && {...}`). */\r\n items: (DropdownMenuItem | false | null | undefined)[]\r\n /** Texto do botão (com uma seta). Sem texto, o botão é o ⋯ (ações de uma linha ou de um cartão). */\r\n label?: React.ReactNode\r\n /** Ícone antes do texto do botão. Sem `label`, substitui o ⋯. */\r\n icon?: React.ReactNode\r\n /** Nome do botão para leitores de ecrã e tooltip. Sem `label`, por omissão `labels.more`. */\r\n 'aria-label'?: string\r\n /** Aspeto do botão, como no Button. Por omissão `secondary` (com texto) ou `ghost` (⋯). */\r\n variant?: ButtonProps['variant']\r\n size?: ButtonProps['size']\r\n /** Botão ligado (ex: há uma vista ou filtro aplicado). */\r\n active?: boolean\r\n /** Para que lado abre a lista, a partir do botão. Por omissão `left` (com texto) ou `right` (⋯). */\r\n align?: 'left' | 'right'\r\n disabled?: boolean\r\n /** Conteúdo no topo da lista, antes dos itens (ex: campo \"Guardar vista atual\", filtros). */\r\n header?: React.ReactNode\r\n /** Caixa de pesquisa no topo da lista. Por omissão, só com mais de 10 itens. */\r\n searchable?: boolean\r\n labels?: Partial<DropdownMenuLabels>\r\n /** Classes da lista (ex: a largura, `w-64`). */\r\n panelClassName?: string\r\n /** Classes da caixa à volta do botão. */\r\n className?: string\r\n /** Avisa quando a lista abre ou fecha (ex: para limpar um campo do `header`). */\r\n onOpenChange?: (open: boolean) => void\r\n}\r\n\r\nconst textOf = (item: DropdownMenuItem) => item.searchText ?? (typeof item.label === 'string' || typeof item.label === 'number' ? String(item.label) : '')\r\n\r\nconst isTyping = (el: EventTarget | null) =>\r\n el instanceof HTMLElement && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable)\r\n\r\n/**\r\n * Botão que abre uma lista de ações ou opções: o ⋯ de uma linha, \"Ações em massa\", \"Vistas guardadas\",\r\n * um filtro de escolha múltipla. A mesma lista do [SelectInput](?path=/docs/v1-primitives-forms-selectinput--docs).\r\n *\r\n * - **Ações**: escolher executa e fecha.\r\n * - **Marcar** (`checked`): o item leva ✓ e a lista fica aberta para marcar vários.\r\n * - **Escolher um** (`selected`): ✓ na opção atual; escolher fecha.\r\n * - **Ação no item** (`action`): um botão pequeno à direita (ex: apagar uma vista).\r\n * - **Topo livre** (`header`): ex: um campo para guardar a vista atual.\r\n *\r\n * Teclado: ↑/↓, Home/End, Enter escolhe, Esc fecha. Abre por cima de um\r\n * [Dialog](?path=/docs/v1-components-overlays-dialog--docs) e os cliques não chegam a uma linha clicável por baixo.\r\n *\r\n * Substitui o `MenuAcoes` legacy.\r\n */\r\nexport default function DropdownMenu({\r\n items,\r\n label,\r\n icon,\r\n 'aria-label': ariaLabel,\r\n variant,\r\n size = 'md',\r\n active,\r\n align,\r\n disabled = false,\r\n header,\r\n searchable,\r\n labels,\r\n panelClassName = '',\r\n className = '',\r\n onOpenChange,\r\n}: DropdownMenuProps) {\r\n const L = { ...DEFAULT_DROPDOWN_MENU_LABELS, ...labels }\r\n const hasText = label !== undefined && label !== null && label !== false\r\n const p = useFloatingPanel(align ?? (hasText ? 'left' : 'right'))\r\n // Dentro de um Dialog a lista abre na janela (como no SelectInput): no body ficava por baixo dela.\r\n const dialogContainer = useDialogContainer()\r\n const menuId = useId()\r\n const searchRef = useRef<HTMLInputElement>(null)\r\n\r\n const list = useMemo(() => items.filter((i): i is DropdownMenuItem => !!i), [items])\r\n const withSearch = searchable ?? list.length > 10\r\n const [query, setQuery] = useState('')\r\n const visible = useMemo(() => {\r\n const q = query.trim().toLowerCase()\r\n return q ? list.filter((i) => textOf(i).toLowerCase().includes(q)) : list\r\n }, [list, query])\r\n const enabled = visible.map((i, n) => (i.disabled ? -1 : n)).filter((n) => n >= 0)\r\n const [activeIndex, setActiveIndex] = useState(-1)\r\n\r\n const changed = useRef(onOpenChange)\r\n changed.current = onOpenChange\r\n const first = useRef(true)\r\n useEffect(() => {\r\n if (first.current) {\r\n first.current = false\r\n return\r\n }\r\n changed.current?.(p.open)\r\n }, [p.open])\r\n\r\n // Ao abrir: limpa a pesquisa e põe o teclado no primeiro item.\r\n useEffect(() => {\r\n if (!p.open) return\r\n setQuery('')\r\n setActiveIndex(-1)\r\n if (withSearch) setTimeout(() => searchRef.current?.focus(), 0)\r\n }, [p.open]) // eslint-disable-line react-hooks/exhaustive-deps\r\n\r\n useEffect(() => {\r\n if (p.open && activeIndex >= 0) p.panelRef.current?.querySelector<HTMLElement>(`[data-index=\"${activeIndex}\"]`)?.scrollIntoView?.({ block: 'nearest' })\r\n }, [activeIndex, p.open, p.panelRef])\r\n\r\n if (list.length === 0 && !header) return null\r\n\r\n const close = () => {\r\n p.close()\r\n p.triggerRef.current?.focus()\r\n }\r\n\r\n const choose = (item: DropdownMenuItem) => {\r\n if (item.disabled) return\r\n // De marcar: fica aberto para se marcarem vários.\r\n if (item.checked === undefined) close()\r\n item.onSelect?.()\r\n }\r\n\r\n const move = (step: 1 | -1 | 'first' | 'last') => {\r\n if (enabled.length === 0) return\r\n const at = enabled.indexOf(activeIndex)\r\n const next =\r\n step === 'first' ? enabled[0]\r\n : step === 'last' ? enabled[enabled.length - 1]\r\n : at === -1 ? (step === 1 ? enabled[0] : enabled[enabled.length - 1])\r\n : enabled[Math.min(enabled.length - 1, Math.max(0, at + step))]\r\n setActiveIndex(next)\r\n }\r\n\r\n // Um só sítio para o teclado: os eventos da lista (portal) também sobem até aqui.\r\n const keys = (e: React.KeyboardEvent) => {\r\n if (!p.open) {\r\n if (['ArrowDown', 'ArrowUp'].includes(e.key)) {\r\n e.preventDefault()\r\n p.setOpen(true)\r\n }\r\n return\r\n }\r\n if (e.key === 'Escape') {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n close()\r\n return\r\n }\r\n if (e.key === 'Tab') {\r\n p.close()\r\n return\r\n }\r\n // A escrever num campo do topo: as teclas são do campo (a pesquisa deixa passar as setas e o Enter).\r\n if (isTyping(e.target) && e.target !== searchRef.current) return\r\n if (e.key === 'ArrowDown') {\r\n e.preventDefault()\r\n move(1)\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault()\r\n move(-1)\r\n } else if (e.key === 'Home' && e.target !== searchRef.current) {\r\n e.preventDefault()\r\n move('first')\r\n } else if (e.key === 'End' && e.target !== searchRef.current) {\r\n e.preventDefault()\r\n move('last')\r\n } else if (e.key === 'Enter' || (e.key === ' ' && e.target !== searchRef.current)) {\r\n const item = visible[activeIndex]\r\n if (item) {\r\n e.preventDefault()\r\n choose(item)\r\n }\r\n }\r\n }\r\n\r\n const name = ariaLabel ?? (hasText ? undefined : L.more)\r\n\r\n return (\r\n <div className={`relative inline-flex ${className}`} onKeyDown={keys}>\r\n <Button\r\n ref={p.triggerRef}\r\n variant={variant ?? (hasText ? 'secondary' : 'ghost')}\r\n size={size}\r\n active={active}\r\n disabled={disabled}\r\n icon={hasText ? icon : (icon ?? <MoreIcon size={size === 'sm' ? 14 : 16} />)}\r\n aria-label={name}\r\n data-tip={hasText ? undefined : name}\r\n aria-haspopup=\"menu\"\r\n aria-expanded={p.open}\r\n aria-controls={p.open ? menuId : undefined}\r\n onClick={(e) => {\r\n // Dentro de uma linha clicável (tabelas, cartões), abrir o menu não abre a linha.\r\n e.stopPropagation()\r\n p.setOpen((o) => !o)\r\n }}\r\n >\r\n {hasText ? (\r\n <>\r\n {label}\r\n <ChevronDownIcon size={12} className={`shrink-0 transition-transform ${p.open ? 'rotate-180' : ''}`} />\r\n </>\r\n ) : undefined}\r\n </Button>\r\n {p.open &&\r\n createPortal(\r\n <div ref={p.panelRef} style={p.style} className={`${PANEL_CLASS} ${panelClassName}`} onClick={(e) => e.stopPropagation()}>\r\n {header && <div className=\"border-b border-slate-100 px-2 pt-1 pb-2 dark:border-slate-800\">{header}</div>}\r\n {withSearch && (\r\n <div className=\"px-2 pt-1 pb-1\">\r\n <input\r\n ref={searchRef}\r\n type=\"search\"\r\n value={query}\r\n onChange={(e) => {\r\n setQuery(e.target.value)\r\n setActiveIndex(-1)\r\n }}\r\n placeholder={L.search}\r\n aria-label={L.search}\r\n className=\"h-8 w-full rounded-lg border border-slate-300 bg-white px-2.5 text-sm outline-none focus:border-proef-blue dark:border-slate-600 dark:bg-slate-800 dark:text-slate-100\"\r\n />\r\n </div>\r\n )}\r\n <div id={menuId} role=\"menu\" aria-label={typeof label === 'string' ? label : name}>\r\n {visible.length === 0 && (\r\n <p className=\"m-0 px-3 py-2 text-slate-400\">{list.length === 0 ? L.empty : L.noResults}</p>\r\n )}\r\n {visible.map((item, i) => {\r\n const checkable = item.checked !== undefined\r\n const mark = checkable ? item.checked : item.selected\r\n return (\r\n <React.Fragment key={i}>\r\n {item.separator && i > 0 && <div role=\"separator\" className=\"my-1 border-t border-slate-100 dark:border-slate-800\" />}\r\n <div className=\"flex items-center\" data-index={i}>\r\n <button\r\n type=\"button\"\r\n role={checkable ? 'menuitemcheckbox' : item.selected !== undefined ? 'menuitemradio' : 'menuitem'}\r\n aria-checked={checkable ? item.checked : item.selected !== undefined ? item.selected : undefined}\r\n disabled={item.disabled}\r\n tabIndex={-1}\r\n onMouseEnter={() => !item.disabled && setActiveIndex(i)}\r\n onClick={() => choose(item)}\r\n className={`min-w-0 flex-1 appearance-none border-0 ${itemClass(i === activeIndex, item.danger)} disabled:cursor-not-allowed disabled:opacity-40`}\r\n >\r\n {item.icon && <span className=\"flex shrink-0 items-center\">{item.icon}</span>}\r\n <span className=\"min-w-0 flex-1\">\r\n <span className={`block truncate ${mark ? 'font-semibold' : ''}`}>{item.label}</span>\r\n {item.description && <span className=\"block truncate text-[11px] text-slate-500 dark:text-slate-400\">{item.description}</span>}\r\n </span>\r\n {(checkable || item.selected !== undefined) && (\r\n <span className=\"w-4 shrink-0\">\r\n {mark && <CheckIcon size={16} strokeWidth={2.5} className=\"text-proef-blue dark:text-sky-300\" />}\r\n </span>\r\n )}\r\n </button>\r\n {item.action && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n icon={item.action.icon}\r\n aria-label={item.action.label}\r\n data-tip={item.action.label}\r\n tabIndex={-1}\r\n onClick={item.action.onSelect}\r\n className=\"mr-1 shrink-0\"\r\n />\r\n )}\r\n </div>\r\n </React.Fragment>\r\n )\r\n })}\r\n </div>\r\n </div>,\r\n dialogContainer ?? document.body,\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\nexport { DropdownMenu }\r\n"],"names":[],"mappings":"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"}
@@ -185,6 +185,13 @@ function SlidersIcon(props) {
185
185
  /* @__PURE__ */ jsx("circle", { cx: "8", cy: "17", r: "2" })
186
186
  ] });
187
187
  }
188
+ function MoreIcon(props) {
189
+ return /* @__PURE__ */ jsxs(Svg, { ...props, fill: "currentColor", children: [
190
+ /* @__PURE__ */ jsx("circle", { cx: "5", cy: "12", r: "1.8" }),
191
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.8" }),
192
+ /* @__PURE__ */ jsx("circle", { cx: "19", cy: "12", r: "1.8" })
193
+ ] });
194
+ }
188
195
  export {
189
196
  BoldIcon,
190
197
  CheckIcon,
@@ -212,6 +219,7 @@ export {
212
219
  MinimizeIcon,
213
220
  MonitorIcon,
214
221
  MoonIcon,
222
+ MoreIcon,
215
223
  PanelOpenIcon,
216
224
  PrefilledIcon,
217
225
  QrCodeIcon,