@innovui/react-native 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -2
- package/dist/components/_shared/anchored-overlay.js +74 -0
- package/dist/components/_shared/anchored-overlay.js.map +1 -0
- package/dist/components/_shared/backdrop.js +25 -0
- package/dist/components/_shared/backdrop.js.map +1 -0
- package/dist/components/_shared/overlay-anchor.js +31 -0
- package/dist/components/_shared/overlay-anchor.js.map +1 -0
- package/dist/components/_shared/overlay-state.js +31 -0
- package/dist/components/_shared/overlay-state.js.map +1 -0
- package/dist/components/_shared/status.d.ts +5 -0
- package/dist/components/_shared/status.js +98 -0
- package/dist/components/_shared/status.js.map +1 -0
- package/dist/components/_shared/use-token.js +15 -0
- package/dist/components/_shared/use-token.js.map +1 -0
- package/dist/components/accordion/accordion.d.ts +43 -0
- package/dist/components/accordion/accordion.js +90 -0
- package/dist/components/accordion/accordion.js.map +1 -0
- package/dist/components/accordion/index.d.ts +2 -0
- package/dist/components/alert/alert.d.ts +25 -0
- package/dist/components/alert/alert.js +61 -0
- package/dist/components/alert/alert.js.map +1 -0
- package/dist/components/alert/index.d.ts +2 -0
- package/dist/components/avatar/avatar.d.ts +17 -0
- package/dist/components/avatar/avatar.js +51 -0
- package/dist/components/avatar/avatar.js.map +1 -0
- package/dist/components/avatar/index.d.ts +2 -0
- package/dist/components/badge/badge.d.ts +16 -0
- package/dist/components/badge/badge.js +57 -0
- package/dist/components/badge/badge.js.map +1 -0
- package/dist/components/badge/index.d.ts +2 -0
- package/dist/components/banner/banner.d.ts +23 -0
- package/dist/components/banner/banner.js +50 -0
- package/dist/components/banner/banner.js.map +1 -0
- package/dist/components/banner/index.d.ts +2 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts +22 -0
- package/dist/components/breadcrumb/breadcrumb.js +54 -0
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/components/breadcrumb/index.d.ts +2 -0
- package/dist/components/button/button.d.ts +21 -0
- package/dist/components/button/button.js +89 -0
- package/dist/components/button/button.js.map +1 -0
- package/dist/components/button/index.d.ts +2 -0
- package/dist/components/card/card.d.ts +17 -0
- package/dist/components/card/card.js +28 -0
- package/dist/components/card/card.js.map +1 -0
- package/dist/components/card/index.d.ts +2 -0
- package/dist/components/checkbox/checkbox.d.ts +18 -0
- package/dist/components/checkbox/checkbox.js +64 -0
- package/dist/components/checkbox/checkbox.js.map +1 -0
- package/dist/components/checkbox/index.d.ts +2 -0
- package/dist/components/combobox/combobox.d.ts +40 -0
- package/dist/components/combobox/combobox.js +101 -0
- package/dist/components/combobox/combobox.js.map +1 -0
- package/dist/components/combobox/index.d.ts +2 -0
- package/dist/components/container/container.d.ts +17 -0
- package/dist/components/container/container.js +42 -0
- package/dist/components/container/container.js.map +1 -0
- package/dist/components/container/index.d.ts +2 -0
- package/dist/components/date-picker/calendar.d.ts +38 -0
- package/dist/components/date-picker/calendar.js +137 -0
- package/dist/components/date-picker/calendar.js.map +1 -0
- package/dist/components/date-picker/date-picker.d.ts +24 -0
- package/dist/components/date-picker/date-picker.js +52 -0
- package/dist/components/date-picker/date-picker.js.map +1 -0
- package/dist/components/date-picker/date-presets.d.ts +15 -0
- package/dist/components/date-picker/date-presets.js +52 -0
- package/dist/components/date-picker/date-presets.js.map +1 -0
- package/dist/components/date-picker/date-range-picker.d.ts +24 -0
- package/dist/components/date-picker/date-range-picker.js +78 -0
- package/dist/components/date-picker/date-range-picker.js.map +1 -0
- package/dist/components/date-picker/date-sheet.js +33 -0
- package/dist/components/date-picker/date-sheet.js.map +1 -0
- package/dist/components/date-picker/date-trigger.js +14 -0
- package/dist/components/date-picker/date-trigger.js.map +1 -0
- package/dist/components/date-picker/dates.d.ts +8 -0
- package/dist/components/date-picker/dates.js +40 -0
- package/dist/components/date-picker/dates.js.map +1 -0
- package/dist/components/date-picker/index.d.ts +6 -0
- package/dist/components/divider/divider.d.ts +11 -0
- package/dist/components/divider/divider.js +16 -0
- package/dist/components/divider/divider.js.map +1 -0
- package/dist/components/divider/index.d.ts +2 -0
- package/dist/components/drawer/drawer.d.ts +34 -0
- package/dist/components/drawer/drawer.js +144 -0
- package/dist/components/drawer/drawer.js.map +1 -0
- package/dist/components/drawer/index.d.ts +2 -0
- package/dist/components/field/field.d.ts +31 -0
- package/dist/components/field/field.js +52 -0
- package/dist/components/field/field.js.map +1 -0
- package/dist/components/field/index.d.ts +2 -0
- package/dist/components/grid/grid.d.ts +35 -0
- package/dist/components/grid/grid.js +64 -0
- package/dist/components/grid/grid.js.map +1 -0
- package/dist/components/grid/index.d.ts +2 -0
- package/dist/components/icon-button/icon-button.d.ts +23 -0
- package/dist/components/icon-button/icon-button.js +48 -0
- package/dist/components/icon-button/icon-button.js.map +1 -0
- package/dist/components/icon-button/index.d.ts +2 -0
- package/dist/components/input/index.d.ts +2 -0
- package/dist/components/input/input.d.ts +22 -0
- package/dist/components/input/input.js +86 -0
- package/dist/components/input/input.js.map +1 -0
- package/dist/components/link/index.d.ts +2 -0
- package/dist/components/link/link.d.ts +13 -0
- package/dist/components/link/link.js +20 -0
- package/dist/components/link/link.js.map +1 -0
- package/dist/components/list/index.d.ts +2 -0
- package/dist/components/list/list.d.ts +26 -0
- package/dist/components/list/list.js +45 -0
- package/dist/components/list/list.js.map +1 -0
- package/dist/components/menu/index.d.ts +2 -0
- package/dist/components/menu/menu.d.ts +47 -0
- package/dist/components/menu/menu.js +81 -0
- package/dist/components/menu/menu.js.map +1 -0
- package/dist/components/modal/index.d.ts +2 -0
- package/dist/components/modal/modal.d.ts +41 -0
- package/dist/components/modal/modal.js +92 -0
- package/dist/components/modal/modal.js.map +1 -0
- package/dist/components/pagination/index.d.ts +2 -0
- package/dist/components/pagination/pagination.d.ts +21 -0
- package/dist/components/pagination/pagination.js +99 -0
- package/dist/components/pagination/pagination.js.map +1 -0
- package/dist/components/popover/index.d.ts +2 -0
- package/dist/components/popover/popover.d.ts +29 -0
- package/dist/components/popover/popover.js +54 -0
- package/dist/components/popover/popover.js.map +1 -0
- package/dist/components/radio/index.d.ts +2 -0
- package/dist/components/radio/radio.d.ts +16 -0
- package/dist/components/radio/radio.js +37 -0
- package/dist/components/radio/radio.js.map +1 -0
- package/dist/components/segmented-control/index.d.ts +2 -0
- package/dist/components/segmented-control/segmented-control.d.ts +23 -0
- package/dist/components/segmented-control/segmented-control.js +53 -0
- package/dist/components/segmented-control/segmented-control.js.map +1 -0
- package/dist/components/select/field-trigger.d.ts +6 -0
- package/dist/components/select/field-trigger.js +90 -0
- package/dist/components/select/field-trigger.js.map +1 -0
- package/dist/components/select/index.d.ts +2 -0
- package/dist/components/select/select.d.ts +35 -0
- package/dist/components/select/select.js +98 -0
- package/dist/components/select/select.js.map +1 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/skeleton.d.ts +17 -0
- package/dist/components/skeleton/skeleton.js +53 -0
- package/dist/components/skeleton/skeleton.js.map +1 -0
- package/dist/components/slider/index.d.ts +2 -0
- package/dist/components/slider/slider.d.ts +26 -0
- package/dist/components/slider/slider.js +102 -0
- package/dist/components/slider/slider.js.map +1 -0
- package/dist/components/spinner/index.d.ts +2 -0
- package/dist/components/spinner/spinner.d.ts +13 -0
- package/dist/components/spinner/spinner.js +47 -0
- package/dist/components/spinner/spinner.js.map +1 -0
- package/dist/components/stack/index.d.ts +2 -0
- package/dist/components/stack/stack.d.ts +17 -0
- package/dist/components/stack/stack.js +60 -0
- package/dist/components/stack/stack.js.map +1 -0
- package/dist/components/stepper/index.d.ts +2 -0
- package/dist/components/stepper/stepper.d.ts +20 -0
- package/dist/components/stepper/stepper.js +61 -0
- package/dist/components/stepper/stepper.js.map +1 -0
- package/dist/components/switch/index.d.ts +2 -0
- package/dist/components/switch/switch.d.ts +15 -0
- package/dist/components/switch/switch.js +51 -0
- package/dist/components/switch/switch.js.map +1 -0
- package/dist/components/table/index.d.ts +2 -0
- package/dist/components/table/table.d.ts +42 -0
- package/dist/components/table/table.js +83 -0
- package/dist/components/table/table.js.map +1 -0
- package/dist/components/tabs/index.d.ts +2 -0
- package/dist/components/tabs/tabs.d.ts +35 -0
- package/dist/components/tabs/tabs.js +82 -0
- package/dist/components/tabs/tabs.js.map +1 -0
- package/dist/components/tag/index.d.ts +2 -0
- package/dist/components/tag/tag.d.ts +18 -0
- package/dist/components/tag/tag.js +37 -0
- package/dist/components/tag/tag.js.map +1 -0
- package/dist/components/textarea/index.d.ts +2 -0
- package/dist/components/textarea/textarea.d.ts +16 -0
- package/dist/components/textarea/textarea.js +46 -0
- package/dist/components/textarea/textarea.js.map +1 -0
- package/dist/components/toast/index.d.ts +2 -0
- package/dist/components/toast/toast.d.ts +41 -0
- package/dist/components/toast/toast.js +167 -0
- package/dist/components/toast/toast.js.map +1 -0
- package/dist/components/tooltip/index.d.ts +2 -0
- package/dist/components/tooltip/tooltip.d.ts +28 -0
- package/dist/components/tooltip/tooltip.js +62 -0
- package/dist/components/tooltip/tooltip.js.map +1 -0
- package/dist/components/upload/index.d.ts +2 -0
- package/dist/components/upload/upload.d.ts +65 -0
- package/dist/components/upload/upload.js +130 -0
- package/dist/components/upload/upload.js.map +1 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +46 -0
- package/dist/tokens/generated.d.ts +371 -0
- package/dist/tokens/generated.js +373 -0
- package/dist/tokens/generated.js.map +1 -0
- package/dist/utils/cn.d.ts +6 -0
- package/dist/utils/cn.js +11 -0
- package/dist/utils/cn.js.map +1 -0
- package/package.json +61 -3
- package/theme.css +191 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { addDays, startOfDay } from "./dates.js";
|
|
2
|
+
//#region src/components/date-picker/date-presets.ts
|
|
3
|
+
/**
|
|
4
|
+
* Atalhos comuns de período, contados a partir de `today`: hoje, últimos 7 e 30 dias (incluindo
|
|
5
|
+
* hoje), este mês e mês passado. Passe só os que quiser, ou monte os seus, em `presets`.
|
|
6
|
+
*/
|
|
7
|
+
function getDatePresets(today = /* @__PURE__ */ new Date()) {
|
|
8
|
+
const day = startOfDay(today);
|
|
9
|
+
const year = day.getFullYear();
|
|
10
|
+
const month = day.getMonth();
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
label: "Hoje",
|
|
14
|
+
range: {
|
|
15
|
+
from: day,
|
|
16
|
+
to: day
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
label: "Últimos 7 dias",
|
|
21
|
+
range: {
|
|
22
|
+
from: addDays(day, -6),
|
|
23
|
+
to: day
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Últimos 30 dias",
|
|
28
|
+
range: {
|
|
29
|
+
from: addDays(day, -29),
|
|
30
|
+
to: day
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: "Este mês",
|
|
35
|
+
range: {
|
|
36
|
+
from: new Date(year, month, 1),
|
|
37
|
+
to: new Date(year, month + 1, 0)
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "Mês passado",
|
|
42
|
+
range: {
|
|
43
|
+
from: new Date(year, month - 1, 1),
|
|
44
|
+
to: new Date(year, month, 0)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { getDatePresets };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=date-presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-presets.js","names":[],"sources":["../../../src/components/date-picker/date-presets.ts"],"sourcesContent":["import type { DateRange } from './dates'\nimport { addDays, startOfDay } from './dates'\n\nexport type DatePreset = {\n /** Texto do atalho. */\n label: string\n /** Período que o atalho escolhe, com início e fim. */\n range: Required<DateRange>\n}\n\n/**\n * Atalhos comuns de período, contados a partir de `today`: hoje, últimos 7 e 30 dias (incluindo\n * hoje), este mês e mês passado. Passe só os que quiser, ou monte os seus, em `presets`.\n */\nexport function getDatePresets(today: Date = new Date()): DatePreset[] {\n const day = startOfDay(today)\n const year = day.getFullYear()\n const month = day.getMonth()\n return [\n { label: 'Hoje', range: { from: day, to: day } },\n { label: 'Últimos 7 dias', range: { from: addDays(day, -6), to: day } },\n { label: 'Últimos 30 dias', range: { from: addDays(day, -29), to: day } },\n {\n label: 'Este mês',\n range: { from: new Date(year, month, 1), to: new Date(year, month + 1, 0) },\n },\n {\n label: 'Mês passado',\n range: { from: new Date(year, month - 1, 1), to: new Date(year, month, 0) },\n },\n ]\n}\n"],"mappings":";;;;;;AAcA,SAAgB,eAAe,wBAAc,IAAI,KAAK,GAAiB;CACrE,MAAM,MAAM,WAAW,KAAK;CAC5B,MAAM,OAAO,IAAI,YAAY;CAC7B,MAAM,QAAQ,IAAI,SAAS;CAC3B,OAAO;EACL;GAAE,OAAO;GAAQ,OAAO;IAAE,MAAM;IAAK,IAAI;GAAI;EAAE;EAC/C;GAAE,OAAO;GAAkB,OAAO;IAAE,MAAM,QAAQ,KAAK,EAAE;IAAG,IAAI;GAAI;EAAE;EACtE;GAAE,OAAO;GAAmB,OAAO;IAAE,MAAM,QAAQ,KAAK,GAAG;IAAG,IAAI;GAAI;EAAE;EACxE;GACE,OAAO;GACP,OAAO;IAAE,MAAM,IAAI,KAAK,MAAM,OAAO,CAAC;IAAG,IAAI,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;GAAE;EAC5E;EACA;GACE,OAAO;GACP,OAAO;IAAE,MAAM,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;IAAG,IAAI,IAAI,KAAK,MAAM,OAAO,CAAC;GAAE;EAC5E;CACF;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DateRange } from "./dates.js";
|
|
2
|
+
import { DatePickerProps } from "./date-picker.js";
|
|
3
|
+
import { DatePreset } from "./date-presets.js";
|
|
4
|
+
//#region src/components/date-picker/date-range-picker.d.ts
|
|
5
|
+
export type DateRangePickerProps = Omit<DatePickerProps, 'value' | 'defaultValue' | 'onValueChange' | 'placeholder'> & {
|
|
6
|
+
/** Período escolhido (modo controlado). Sem `to`, só o início foi escolhido. */
|
|
7
|
+
value?: DateRange;
|
|
8
|
+
defaultValue?: DateRange;
|
|
9
|
+
/** Chamado a cada toque: primeiro com só o início, depois com o período completo. */
|
|
10
|
+
onValueChange?: (range: DateRange) => void;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Atalhos de período acima do calendário ("Últimos 7 dias"). `true` usa os padrão de
|
|
14
|
+
* `getDatePresets()`; ou passe a sua lista. Atalhos fora de `minDate`/`maxDate` ficam desabilitados.
|
|
15
|
+
*/
|
|
16
|
+
presets?: boolean | DatePreset[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Campo de período: um gatilho que mostra o início e o fim e abre um Calendar para escolher os dois
|
|
20
|
+
* dias. No native os atalhos ficam numa linha quebrável acima do calendário (sem coluna lateral).
|
|
21
|
+
*/
|
|
22
|
+
export declare function DateRangePicker({ value, defaultValue, onValueChange, minDate, maxDate, locale, weekStartsOn, placeholder, presets, size, error, disabled, readOnly, className, accessibilityLabel, accessibilityHint, testID }: DateRangePickerProps): import("react").JSX.Element;
|
|
23
|
+
//#endregion
|
|
24
|
+
//# sourceMappingURL=date-range-picker.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Button } from "../button/button.js";
|
|
2
|
+
import { isSameDay, startOfDay } from "./dates.js";
|
|
3
|
+
import { Calendar } from "./calendar.js";
|
|
4
|
+
import { DateSheet } from "./date-sheet.js";
|
|
5
|
+
import { DateTrigger } from "./date-trigger.js";
|
|
6
|
+
import { getDatePresets } from "./date-presets.js";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { View } from "react-native";
|
|
9
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
//#region src/components/date-picker/date-range-picker.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Campo de período: um gatilho que mostra o início e o fim e abre um Calendar para escolher os dois
|
|
13
|
+
* dias. No native os atalhos ficam numa linha quebrável acima do calendário (sem coluna lateral).
|
|
14
|
+
*/
|
|
15
|
+
function DateRangePicker({ value, defaultValue, onValueChange, minDate, maxDate, locale = "pt-BR", weekStartsOn, placeholder = "dd/mm/aaaa – dd/mm/aaaa", presets, size, error, disabled, readOnly, className, accessibilityLabel, accessibilityHint, testID }) {
|
|
16
|
+
const [inner, setInner] = useState(defaultValue);
|
|
17
|
+
const [open, setOpen] = useState(false);
|
|
18
|
+
const range = value ?? inner;
|
|
19
|
+
const format = new Intl.DateTimeFormat(locale, {
|
|
20
|
+
day: "2-digit",
|
|
21
|
+
month: "2-digit",
|
|
22
|
+
year: "numeric"
|
|
23
|
+
});
|
|
24
|
+
const text = range?.from ? `${format.format(range.from)} – ${range.to ? format.format(range.to) : "…"}` : void 0;
|
|
25
|
+
const visible = open && !readOnly && !disabled;
|
|
26
|
+
const shortcuts = presets === true ? getDatePresets() : presets || [];
|
|
27
|
+
const outOfBounds = ({ range: r }) => minDate !== void 0 && r.from < startOfDay(minDate) || maxDate !== void 0 && r.to > startOfDay(maxDate);
|
|
28
|
+
const isActive = ({ range: r }) => range?.from !== void 0 && range.to !== void 0 && isSameDay(range.from, r.from) && isSameDay(range.to, r.to);
|
|
29
|
+
function apply(next, close) {
|
|
30
|
+
if (value === void 0) setInner(next);
|
|
31
|
+
onValueChange?.(next);
|
|
32
|
+
if (close) setOpen(false);
|
|
33
|
+
}
|
|
34
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DateTrigger, {
|
|
35
|
+
text,
|
|
36
|
+
placeholder,
|
|
37
|
+
size,
|
|
38
|
+
error,
|
|
39
|
+
open: visible,
|
|
40
|
+
disabled,
|
|
41
|
+
readOnly,
|
|
42
|
+
onPress: () => setOpen(true),
|
|
43
|
+
className,
|
|
44
|
+
accessibilityLabel,
|
|
45
|
+
accessibilityHint,
|
|
46
|
+
testID
|
|
47
|
+
}), /* @__PURE__ */ jsx(DateSheet, {
|
|
48
|
+
visible,
|
|
49
|
+
onRequestClose: () => setOpen(false),
|
|
50
|
+
testID: testID ? `${testID}-sheet` : void 0,
|
|
51
|
+
children: /* @__PURE__ */ jsxs(View, {
|
|
52
|
+
className: "gap-3",
|
|
53
|
+
children: [shortcuts.length > 0 && /* @__PURE__ */ jsx(View, {
|
|
54
|
+
className: "w-70 flex-row flex-wrap gap-1",
|
|
55
|
+
children: shortcuts.map((preset) => /* @__PURE__ */ jsx(Button, {
|
|
56
|
+
size: "sm",
|
|
57
|
+
variant: isActive(preset) ? "secondary" : "ghost",
|
|
58
|
+
accessibilityState: { selected: isActive(preset) },
|
|
59
|
+
disabled: outOfBounds(preset),
|
|
60
|
+
onPress: () => apply(preset.range, true),
|
|
61
|
+
children: preset.label
|
|
62
|
+
}, preset.label))
|
|
63
|
+
}), /* @__PURE__ */ jsx(Calendar, {
|
|
64
|
+
mode: "range",
|
|
65
|
+
value: range,
|
|
66
|
+
minDate,
|
|
67
|
+
maxDate,
|
|
68
|
+
locale,
|
|
69
|
+
weekStartsOn,
|
|
70
|
+
onValueChange: (next) => apply(next, next.to !== void 0)
|
|
71
|
+
})]
|
|
72
|
+
})
|
|
73
|
+
})] });
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { DateRangePicker };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=date-range-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range-picker.js","names":[],"sources":["../../../src/components/date-picker/date-range-picker.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { useState } from 'react'\nimport { View } from 'react-native'\nimport { Button } from '../button/button'\nimport { Calendar } from './calendar'\nimport type { DatePickerProps } from './date-picker'\nimport { type DatePreset, getDatePresets } from './date-presets'\nimport { DateSheet } from './date-sheet'\nimport { DateTrigger } from './date-trigger'\nimport { type DateRange, isSameDay, startOfDay } from './dates'\n\nexport type DateRangePickerProps = Omit<\n DatePickerProps,\n 'value' | 'defaultValue' | 'onValueChange' | 'placeholder'\n> & {\n /** Período escolhido (modo controlado). Sem `to`, só o início foi escolhido. */\n value?: DateRange\n defaultValue?: DateRange\n /** Chamado a cada toque: primeiro com só o início, depois com o período completo. */\n onValueChange?: (range: DateRange) => void\n placeholder?: string\n /**\n * Atalhos de período acima do calendário (\"Últimos 7 dias\"). `true` usa os padrão de\n * `getDatePresets()`; ou passe a sua lista. Atalhos fora de `minDate`/`maxDate` ficam desabilitados.\n */\n presets?: boolean | DatePreset[]\n}\n\n/**\n * Campo de período: um gatilho que mostra o início e o fim e abre um Calendar para escolher os dois\n * dias. No native os atalhos ficam numa linha quebrável acima do calendário (sem coluna lateral).\n */\nexport function DateRangePicker({\n value,\n defaultValue,\n onValueChange,\n minDate,\n maxDate,\n locale = 'pt-BR',\n weekStartsOn,\n placeholder = 'dd/mm/aaaa – dd/mm/aaaa',\n presets,\n size,\n error,\n disabled,\n readOnly,\n className,\n accessibilityLabel,\n accessibilityHint,\n testID,\n}: DateRangePickerProps) {\n const [inner, setInner] = useState(defaultValue)\n const [open, setOpen] = useState(false)\n const range = value ?? inner\n const format = new Intl.DateTimeFormat(locale, {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n })\n const text = range?.from\n ? `${format.format(range.from)} – ${range.to ? format.format(range.to) : '…'}`\n : undefined\n const visible = open && !readOnly && !disabled\n\n const shortcuts = presets === true ? getDatePresets() : presets || []\n const outOfBounds = ({ range: r }: DatePreset) =>\n (minDate !== undefined && r.from < startOfDay(minDate)) ||\n (maxDate !== undefined && r.to > startOfDay(maxDate))\n const isActive = ({ range: r }: DatePreset) =>\n range?.from !== undefined &&\n range.to !== undefined &&\n isSameDay(range.from, r.from) &&\n isSameDay(range.to, r.to)\n\n function apply(next: DateRange, close: boolean) {\n if (value === undefined) setInner(next)\n onValueChange?.(next)\n if (close) setOpen(false)\n }\n\n return (\n <>\n <DateTrigger\n text={text}\n placeholder={placeholder}\n size={size}\n error={error}\n open={visible}\n disabled={disabled}\n readOnly={readOnly}\n onPress={() => setOpen(true)}\n className={className}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={accessibilityHint}\n testID={testID}\n />\n <DateSheet\n visible={visible}\n onRequestClose={() => setOpen(false)}\n testID={testID ? `${testID}-sheet` : undefined}\n >\n <View className=\"gap-3\">\n {shortcuts.length > 0 && (\n <View className=\"w-70 flex-row flex-wrap gap-1\">\n {shortcuts.map((preset) => (\n <Button\n key={preset.label}\n size=\"sm\"\n variant={isActive(preset) ? 'secondary' : 'ghost'}\n accessibilityState={{ selected: isActive(preset) }}\n disabled={outOfBounds(preset)}\n onPress={() => apply(preset.range, true)}\n >\n {preset.label}\n </Button>\n ))}\n </View>\n )}\n <Calendar\n mode=\"range\"\n value={range}\n minDate={minDate}\n maxDate={maxDate}\n locale={locale}\n weekStartsOn={weekStartsOn}\n onValueChange={(next) => apply(next, next.to !== undefined)}\n />\n </View>\n </DateSheet>\n </>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,SAAgB,gBAAgB,EAC9B,OACA,cACA,eACA,SACA,SACA,SAAS,SACT,cACA,cAAc,2BACd,SACA,MACA,OACA,UACA,UACA,WACA,oBACA,mBACA,UACuB;CACvB,MAAM,CAAC,OAAO,YAAY,SAAS,YAAY;CAC/C,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,QAAQ,SAAS;CACvB,MAAM,SAAS,IAAI,KAAK,eAAe,QAAQ;EAC7C,KAAK;EACL,OAAO;EACP,MAAM;CACR,CAAC;CACD,MAAM,OAAO,OAAO,OAChB,GAAG,OAAO,OAAO,MAAM,IAAI,EAAE,KAAK,MAAM,KAAK,OAAO,OAAO,MAAM,EAAE,IAAI,QACvE,KAAA;CACJ,MAAM,UAAU,QAAQ,CAAC,YAAY,CAAC;CAEtC,MAAM,YAAY,YAAY,OAAO,eAAe,IAAI,WAAW,CAAC;CACpE,MAAM,eAAe,EAAE,OAAO,QAC3B,YAAY,KAAA,KAAa,EAAE,OAAO,WAAW,OAAO,KACpD,YAAY,KAAA,KAAa,EAAE,KAAK,WAAW,OAAO;CACrD,MAAM,YAAY,EAAE,OAAO,QACzB,OAAO,SAAS,KAAA,KAChB,MAAM,OAAO,KAAA,KACb,UAAU,MAAM,MAAM,EAAE,IAAI,KAC5B,UAAU,MAAM,IAAI,EAAE,EAAE;CAE1B,SAAS,MAAM,MAAiB,OAAgB;EAC9C,IAAI,UAAU,KAAA,GAAW,SAAS,IAAI;EACtC,gBAAgB,IAAI;EACpB,IAAI,OAAO,QAAQ,KAAK;CAC1B;CAEA,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,aAAD;EACQ;EACO;EACP;EACC;EACP,MAAM;EACI;EACA;EACV,eAAe,QAAQ,IAAI;EAChB;EACS;EACD;EACX;CACT,CAAA,GACD,oBAAC,WAAD;EACW;EACT,sBAAsB,QAAQ,KAAK;EACnC,QAAQ,SAAS,GAAG,OAAO,UAAU,KAAA;EAErC,UAAA,qBAAC,MAAD;GAAM,WAAU;GAAhB,UAAA,CACG,UAAU,SAAS,KAClB,oBAAC,MAAD;IAAM,WAAU;IACb,UAAA,UAAU,KAAK,WACd,oBAAC,QAAD;KAEE,MAAK;KACL,SAAS,SAAS,MAAM,IAAI,cAAc;KAC1C,oBAAoB,EAAE,UAAU,SAAS,MAAM,EAAE;KACjD,UAAU,YAAY,MAAM;KAC5B,eAAe,MAAM,OAAO,OAAO,IAAI;KAEtC,UAAA,OAAO;IACF,GARD,OAAO,KAQN,CACT;GACG,CAAA,GAER,oBAAC,UAAD;IACE,MAAK;IACL,OAAO;IACE;IACA;IACD;IACM;IACd,gBAAgB,SAAS,MAAM,MAAM,KAAK,OAAO,KAAA,CAAS;GAC3D,CAAA,CACG;;CACG,CAAA,CACX,EAAA,CAAA;AAEN"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Backdrop } from "../_shared/backdrop.js";
|
|
2
|
+
import { Modal, View } from "react-native";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/date-picker/date-sheet.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Painel do calendário (uso interno). Decisão: Modal nativo centralizado com Backdrop em vez de
|
|
7
|
+
* camada ancorada, porque o calendário (280 de largura, mais os atalhos no período) não cabe de
|
|
8
|
+
* forma previsível junto a gatilhos estreitos ou perto da borda da tela.
|
|
9
|
+
*/
|
|
10
|
+
function DateSheet({ visible, onRequestClose, children, testID }) {
|
|
11
|
+
return /* @__PURE__ */ jsx(Modal, {
|
|
12
|
+
transparent: true,
|
|
13
|
+
visible,
|
|
14
|
+
animationType: "fade",
|
|
15
|
+
onRequestClose,
|
|
16
|
+
statusBarTranslucent: true,
|
|
17
|
+
children: /* @__PURE__ */ jsxs(View, {
|
|
18
|
+
className: "flex-1 items-center justify-center p-4",
|
|
19
|
+
children: [/* @__PURE__ */ jsx(Backdrop, {
|
|
20
|
+
onPress: onRequestClose,
|
|
21
|
+
testID: testID ? `${testID}-backdrop` : void 0
|
|
22
|
+
}), /* @__PURE__ */ jsx(View, {
|
|
23
|
+
testID,
|
|
24
|
+
className: "max-w-full rounded-lg border border-border-default bg-surface-elevated p-3 shadow-md",
|
|
25
|
+
children
|
|
26
|
+
})]
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { DateSheet };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=date-sheet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-sheet.js","names":[],"sources":["../../../src/components/date-picker/date-sheet.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport type { ReactNode } from 'react'\nimport { Modal, View } from 'react-native'\nimport { Backdrop } from '../_shared/backdrop'\n\n/**\n * Painel do calendário (uso interno). Decisão: Modal nativo centralizado com Backdrop em vez de\n * camada ancorada, porque o calendário (280 de largura, mais os atalhos no período) não cabe de\n * forma previsível junto a gatilhos estreitos ou perto da borda da tela.\n */\nexport function DateSheet({\n visible,\n onRequestClose,\n children,\n testID,\n}: {\n visible: boolean\n onRequestClose: () => void\n children: ReactNode\n testID?: string\n}) {\n return (\n <Modal\n transparent\n visible={visible}\n animationType=\"fade\"\n onRequestClose={onRequestClose}\n statusBarTranslucent\n >\n <View className=\"flex-1 items-center justify-center p-4\">\n <Backdrop onPress={onRequestClose} testID={testID ? `${testID}-backdrop` : undefined} />\n <View\n testID={testID}\n className=\"max-w-full rounded-lg border border-border-default bg-surface-elevated p-3 shadow-md\"\n >\n {children}\n </View>\n </View>\n </Modal>\n )\n}\n"],"mappings":";;;;;;;;;AAWA,SAAgB,UAAU,EACxB,SACA,gBACA,UACA,UAMC;CACD,OACE,oBAAC,OAAD;EACE,aAAA;EACS;EACT,eAAc;EACE;EAChB,sBAAA;EAEA,UAAA,qBAAC,MAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,UAAD;IAAU,SAAS;IAAgB,QAAQ,SAAS,GAAG,OAAO,aAAa,KAAA;GAAY,CAAA,GACvF,oBAAC,MAAD;IACU;IACR,WAAU;IAET;GACG,CAAA,CACF;;CACD,CAAA;AAEX"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FieldTrigger } from "../select/field-trigger.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/date-picker/date-trigger.tsx
|
|
4
|
+
/** Gatilho no estilo do Input que mostra a data e abre o calendário (não faz parte da API pública). */
|
|
5
|
+
function DateTrigger(props) {
|
|
6
|
+
return /* @__PURE__ */ jsx(FieldTrigger, {
|
|
7
|
+
...props,
|
|
8
|
+
icon: "calendar"
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DateTrigger };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=date-trigger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-trigger.js","names":[],"sources":["../../../src/components/date-picker/date-trigger.tsx"],"sourcesContent":["import { FieldTrigger, type FieldTriggerProps } from '../select/field-trigger'\n\nexport type DateTriggerProps = Omit<FieldTriggerProps, 'icon'>\n\n/** Gatilho no estilo do Input que mostra a data e abre o calendário (não faz parte da API pública). */\nexport function DateTrigger(props: DateTriggerProps) {\n return <FieldTrigger {...props} icon=\"calendar\" />\n}\n"],"mappings":";;;;AAKA,SAAgB,YAAY,OAAyB;CACnD,OAAO,oBAAC,cAAD;EAAc,GAAI;EAAO,MAAK;CAAY,CAAA;AACnD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/components/date-picker/dates.ts
|
|
2
|
+
/** Funções de data locais (sem fuso): o calendário trabalha com o dia civil, não com instantes. */
|
|
3
|
+
const startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
4
|
+
const addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
|
|
5
|
+
/** Soma meses mantendo o dia, ou o último dia do mês se ele não existir (31/jan + 1 mês = 28/fev). */
|
|
6
|
+
function addMonths(d, n) {
|
|
7
|
+
const last = new Date(d.getFullYear(), d.getMonth() + n + 1, 0).getDate();
|
|
8
|
+
return new Date(d.getFullYear(), d.getMonth() + n, Math.min(d.getDate(), last));
|
|
9
|
+
}
|
|
10
|
+
const isSameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
11
|
+
const clamp = (d, min, max) => {
|
|
12
|
+
if (min && d < startOfDay(min)) return startOfDay(min);
|
|
13
|
+
if (max && d > startOfDay(max)) return startOfDay(max);
|
|
14
|
+
return d;
|
|
15
|
+
};
|
|
16
|
+
/** `2026-10-19`, o formato de `<input type="date">` e de formulários. */
|
|
17
|
+
const toISODate = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
18
|
+
/** Semanas do mês (linhas de 7 dias, começando em `weekStartsOn`); dias de fora são `null`. */
|
|
19
|
+
function monthWeeks(year, month, weekStartsOn) {
|
|
20
|
+
const lead = (new Date(year, month, 1).getDay() - weekStartsOn + 7) % 7;
|
|
21
|
+
const days = new Date(year, month + 1, 0).getDate();
|
|
22
|
+
const cells = [...Array.from({ length: lead }, () => null), ...Array.from({ length: days }, (_, i) => new Date(year, month, i + 1))];
|
|
23
|
+
while (cells.length % 7 !== 0) cells.push(null);
|
|
24
|
+
return Array.from({ length: cells.length / 7 }, (_, w) => cells.slice(w * 7, w * 7 + 7));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Próximo período depois de um clique em `day`: o primeiro clique define o início; o segundo, o fim
|
|
28
|
+
* (um dia anterior ao início recomeça o período); com o período completo, um novo clique recomeça.
|
|
29
|
+
*/
|
|
30
|
+
function pickRange(range, day) {
|
|
31
|
+
if (range?.from && !range.to && day >= range.from) return {
|
|
32
|
+
from: range.from,
|
|
33
|
+
to: day
|
|
34
|
+
};
|
|
35
|
+
return { from: day };
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { addDays, addMonths, clamp, isSameDay, monthWeeks, pickRange, startOfDay, toISODate };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=dates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dates.js","names":[],"sources":["../../../src/components/date-picker/dates.ts"],"sourcesContent":["/** Funções de data locais (sem fuso): o calendário trabalha com o dia civil, não com instantes. */\n\nexport const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate())\n\nexport const addDays = (d: Date, n: number) =>\n new Date(d.getFullYear(), d.getMonth(), d.getDate() + n)\n\n/** Soma meses mantendo o dia, ou o último dia do mês se ele não existir (31/jan + 1 mês = 28/fev). */\nexport function addMonths(d: Date, n: number) {\n const last = new Date(d.getFullYear(), d.getMonth() + n + 1, 0).getDate()\n return new Date(d.getFullYear(), d.getMonth() + n, Math.min(d.getDate(), last))\n}\n\nexport const isSameDay = (a: Date, b: Date) =>\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n\nexport const clamp = (d: Date, min?: Date, max?: Date) => {\n if (min && d < startOfDay(min)) return startOfDay(min)\n if (max && d > startOfDay(max)) return startOfDay(max)\n return d\n}\n\n/** `2026-10-19`, o formato de `<input type=\"date\">` e de formulários. */\nexport const toISODate = (d: Date) =>\n `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n\n/** Semanas do mês (linhas de 7 dias, começando em `weekStartsOn`); dias de fora são `null`. */\nexport function monthWeeks(year: number, month: number, weekStartsOn: number) {\n const lead = (new Date(year, month, 1).getDay() - weekStartsOn + 7) % 7\n const days = new Date(year, month + 1, 0).getDate()\n const cells: (Date | null)[] = [\n ...Array.from({ length: lead }, () => null),\n ...Array.from({ length: days }, (_, i) => new Date(year, month, i + 1)),\n ]\n while (cells.length % 7 !== 0) cells.push(null)\n return Array.from({ length: cells.length / 7 }, (_, w) => cells.slice(w * 7, w * 7 + 7))\n}\n\n/** Um período: `to` ausente significa que só o início foi escolhido. */\nexport type DateRange = { from?: Date; to?: Date }\n\n/**\n * Próximo período depois de um clique em `day`: o primeiro clique define o início; o segundo, o fim\n * (um dia anterior ao início recomeça o período); com o período completo, um novo clique recomeça.\n */\nexport function pickRange(range: DateRange | undefined, day: Date): DateRange {\n if (range?.from && !range.to && day >= range.from) return { from: range.from, to: day }\n return { from: day }\n}\n"],"mappings":";;AAEA,MAAa,cAAc,MAAY,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC;AAE1F,MAAa,WAAW,GAAS,MAC/B,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,IAAI,CAAC;;AAGzD,SAAgB,UAAU,GAAS,GAAW;CAC5C,MAAM,OAAO,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ;CACxE,OAAO,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,IAAI,GAAG,KAAK,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;AAChF;AAEA,MAAa,aAAa,GAAS,MACjC,EAAE,YAAY,MAAM,EAAE,YAAY,KAClC,EAAE,SAAS,MAAM,EAAE,SAAS,KAC5B,EAAE,QAAQ,MAAM,EAAE,QAAQ;AAE5B,MAAa,SAAS,GAAS,KAAY,QAAe;CACxD,IAAI,OAAO,IAAI,WAAW,GAAG,GAAG,OAAO,WAAW,GAAG;CACrD,IAAI,OAAO,IAAI,WAAW,GAAG,GAAG,OAAO,WAAW,GAAG;CACrD,OAAO;AACT;;AAGA,MAAa,aAAa,MACxB,GAAG,EAAE,YAAY,EAAE,GAAG,OAAO,EAAE,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;;AAGxG,SAAgB,WAAW,MAAc,OAAe,cAAsB;CAC5E,MAAM,QAAQ,IAAI,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,OAAO,IAAI,eAAe,KAAK;CACtE,MAAM,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,QAAQ;CAClD,MAAM,QAAyB,CAC7B,GAAG,MAAM,KAAK,EAAE,QAAQ,KAAK,SAAS,IAAI,GAC1C,GAAG,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,CACxE;CACA,OAAO,MAAM,SAAS,MAAM,GAAG,MAAM,KAAK,IAAI;CAC9C,OAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,SAAS,EAAE,IAAI,GAAG,MAAM,MAAM,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC;AACzF;;;;;AASA,SAAgB,UAAU,OAA8B,KAAsB;CAC5E,IAAI,OAAO,QAAQ,CAAC,MAAM,MAAM,OAAO,MAAM,MAAM,OAAO;EAAE,MAAM,MAAM;EAAM,IAAI;CAAI;CACtF,OAAO,EAAE,MAAM,IAAI;AACrB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DateRange } from "./dates.js";
|
|
2
|
+
import { Calendar, CalendarProps } from "./calendar.js";
|
|
3
|
+
import { DatePicker, DatePickerProps } from "./date-picker.js";
|
|
4
|
+
import { DatePreset, getDatePresets } from "./date-presets.js";
|
|
5
|
+
import { DateRangePicker, DateRangePickerProps } from "./date-range-picker.js";
|
|
6
|
+
export { Calendar, type CalendarProps, DatePicker, type DatePickerProps, type DatePreset, type DateRange, DateRangePicker, type DateRangePickerProps, getDatePresets };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ViewProps } from "react-native";
|
|
2
|
+
//#region src/components/divider/divider.d.ts
|
|
3
|
+
export type DividerProps = Omit<ViewProps, 'children'> & {
|
|
4
|
+
className?: string;
|
|
5
|
+
/** `vertical` precisa de um pai em linha (ou altura definida) para ocupar a altura. */
|
|
6
|
+
orientation?: 'horizontal' | 'vertical';
|
|
7
|
+
};
|
|
8
|
+
/** Linha fina que separa blocos de conteúdo. Usa `border/default`. */
|
|
9
|
+
export declare function Divider({ className, orientation, ...props }: DividerProps): import("react").JSX.Element;
|
|
10
|
+
//#endregion
|
|
11
|
+
//# sourceMappingURL=divider.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/divider/divider.tsx
|
|
5
|
+
/** Linha fina que separa blocos de conteúdo. Usa `border/default`. */
|
|
6
|
+
function Divider({ className, orientation = "horizontal", ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(View, {
|
|
8
|
+
importantForAccessibility: "no",
|
|
9
|
+
className: cn("shrink-0 bg-border-default", orientation === "horizontal" ? "h-px w-full" : "w-px self-stretch", className),
|
|
10
|
+
...props
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Divider };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=divider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"divider.js","names":[],"sources":["../../../src/components/divider/divider.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\n\nexport type DividerProps = Omit<ViewProps, 'children'> & {\n className?: string\n /** `vertical` precisa de um pai em linha (ou altura definida) para ocupar a altura. */\n orientation?: 'horizontal' | 'vertical'\n}\n\n/** Linha fina que separa blocos de conteúdo. Usa `border/default`. */\nexport function Divider({ className, orientation = 'horizontal', ...props }: DividerProps) {\n return (\n // decorativo: o RN não tem role \"separator\", então some da árvore de acessibilidade\n <View\n importantForAccessibility=\"no\"\n className={cn(\n 'shrink-0 bg-border-default',\n orientation === 'horizontal' ? 'h-px w-full' : 'w-px self-stretch',\n className,\n )}\n {...props}\n />\n )\n}\n"],"mappings":";;;;;AAYA,SAAgB,QAAQ,EAAE,WAAW,cAAc,cAAc,GAAG,SAAuB;CACzF,OAEE,oBAAC,MAAD;EACE,2BAA0B;EAC1B,WAAW,GACT,8BACA,gBAAgB,eAAe,gBAAgB,qBAC/C,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
|
+
//#region src/components/drawer/drawer.d.ts
|
|
4
|
+
declare const drawerVariants: (props?: ({
|
|
5
|
+
side?: "bottom" | "left" | "right" | "top" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
export type DrawerProps = VariantProps<typeof drawerVariants> & {
|
|
8
|
+
open?: boolean;
|
|
9
|
+
defaultOpen?: boolean;
|
|
10
|
+
onOpenChange?: (open: boolean) => void;
|
|
11
|
+
/** Título do painel; também é o nome acessível dele. */
|
|
12
|
+
title: ReactNode;
|
|
13
|
+
/** O corpo do painel. */
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
/** Ações do rodapé (primária à direita). Use `DrawerClose` para fechar. Sem ele, não há rodapé. */
|
|
16
|
+
footer?: ReactNode;
|
|
17
|
+
/** Elemento que abre o painel (ex.: um `<Button>`); recebe `onPress`. Dispensável no modo controlado. */
|
|
18
|
+
trigger?: ReactNode;
|
|
19
|
+
/** Nome acessível do botão de fechar (×). */
|
|
20
|
+
closeLabel?: string;
|
|
21
|
+
className?: string;
|
|
22
|
+
testID?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Painel colado a uma borda da tela, que desliza ao abrir e fechar. Usa o `Modal` nativo (botão
|
|
26
|
+
* voltar do Android e toque no fundo fecham). Não há foco preso nem `Esc`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function Drawer({ open, defaultOpen, onOpenChange, title, children, footer, trigger, closeLabel, side, className, testID }: DrawerProps): import("react").JSX.Element;
|
|
29
|
+
/** Fecha o painel ao pressionar. Envolva um `<Button>` do rodapé (injeta `onPress`, como o `asChild`). */
|
|
30
|
+
export declare function DrawerClose({ children }: {
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}): import("react").JSX.Element;
|
|
33
|
+
//#endregion
|
|
34
|
+
//# sourceMappingURL=drawer.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { CloseIcon } from "../_shared/status.js";
|
|
3
|
+
import { useToken } from "../_shared/use-token.js";
|
|
4
|
+
import { Backdrop } from "../_shared/backdrop.js";
|
|
5
|
+
import { injectHandler, useOpenState } from "../_shared/overlay-state.js";
|
|
6
|
+
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
|
7
|
+
import { Animated, Modal, Pressable, ScrollView, Text, View, useWindowDimensions } from "react-native";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { cva } from "class-variance-authority";
|
|
10
|
+
//#region src/components/drawer/drawer.tsx
|
|
11
|
+
const drawerVariants = cva("overflow-hidden border-border-default bg-surface-elevated shadow-2xl", {
|
|
12
|
+
variants: { side: {
|
|
13
|
+
right: "border-l",
|
|
14
|
+
left: "border-r",
|
|
15
|
+
top: "border-b",
|
|
16
|
+
bottom: "border-t"
|
|
17
|
+
} },
|
|
18
|
+
defaultVariants: { side: "right" }
|
|
19
|
+
});
|
|
20
|
+
const containerVariants = cva("flex-1", {
|
|
21
|
+
variants: { side: {
|
|
22
|
+
right: "flex-row justify-end",
|
|
23
|
+
left: "flex-row justify-start",
|
|
24
|
+
top: "justify-start",
|
|
25
|
+
bottom: "justify-end"
|
|
26
|
+
} },
|
|
27
|
+
defaultVariants: { side: "right" }
|
|
28
|
+
});
|
|
29
|
+
const CloseContext = createContext(null);
|
|
30
|
+
/**
|
|
31
|
+
* Painel colado a uma borda da tela, que desliza ao abrir e fechar. Usa o `Modal` nativo (botão
|
|
32
|
+
* voltar do Android e toque no fundo fecham). Não há foco preso nem `Esc`.
|
|
33
|
+
*/
|
|
34
|
+
function Drawer({ open, defaultOpen, onOpenChange, title, children, footer, trigger, closeLabel = "Fechar", side, className, testID }) {
|
|
35
|
+
const [isOpen, setOpen] = useOpenState({
|
|
36
|
+
open,
|
|
37
|
+
defaultOpen,
|
|
38
|
+
onOpenChange
|
|
39
|
+
});
|
|
40
|
+
const close = () => setOpen(false);
|
|
41
|
+
const iconColor = useToken("--color-text-primary");
|
|
42
|
+
const { width, height } = useWindowDimensions();
|
|
43
|
+
const placement = side ?? "right";
|
|
44
|
+
const horizontal = placement === "left" || placement === "right";
|
|
45
|
+
const [mounted, setMounted] = useState(isOpen);
|
|
46
|
+
const progress = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (isOpen) setMounted(true);
|
|
49
|
+
const animation = Animated.timing(progress, {
|
|
50
|
+
toValue: isOpen ? 1 : 0,
|
|
51
|
+
duration: 250,
|
|
52
|
+
useNativeDriver: true
|
|
53
|
+
});
|
|
54
|
+
animation.start(({ finished }) => {
|
|
55
|
+
if (finished && !isOpen) setMounted(false);
|
|
56
|
+
});
|
|
57
|
+
return () => animation.stop();
|
|
58
|
+
}, [isOpen, progress]);
|
|
59
|
+
const offset = horizontal ? width : height;
|
|
60
|
+
const sign = placement === "right" || placement === "bottom" ? 1 : -1;
|
|
61
|
+
const translate = progress.interpolate({
|
|
62
|
+
inputRange: [0, 1],
|
|
63
|
+
outputRange: [sign * offset, 0]
|
|
64
|
+
});
|
|
65
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [injectHandler(trigger, "onPress", () => setOpen(true)), /* @__PURE__ */ jsx(Modal, {
|
|
66
|
+
transparent: true,
|
|
67
|
+
statusBarTranslucent: true,
|
|
68
|
+
animationType: "none",
|
|
69
|
+
visible: mounted,
|
|
70
|
+
onRequestClose: close,
|
|
71
|
+
children: /* @__PURE__ */ jsx(CloseContext.Provider, {
|
|
72
|
+
value: close,
|
|
73
|
+
children: /* @__PURE__ */ jsxs(View, {
|
|
74
|
+
className: containerVariants({ side }),
|
|
75
|
+
accessibilityViewIsModal: true,
|
|
76
|
+
children: [/* @__PURE__ */ jsx(Animated.View, {
|
|
77
|
+
pointerEvents: "box-none",
|
|
78
|
+
style: {
|
|
79
|
+
position: "absolute",
|
|
80
|
+
top: 0,
|
|
81
|
+
left: 0,
|
|
82
|
+
right: 0,
|
|
83
|
+
bottom: 0,
|
|
84
|
+
opacity: progress
|
|
85
|
+
},
|
|
86
|
+
children: /* @__PURE__ */ jsx(Backdrop, {
|
|
87
|
+
onPress: close,
|
|
88
|
+
testID: testID ? `${testID}-backdrop` : void 0
|
|
89
|
+
})
|
|
90
|
+
}), /* @__PURE__ */ jsx(Animated.View, {
|
|
91
|
+
style: [horizontal ? {
|
|
92
|
+
width: "85%",
|
|
93
|
+
maxWidth: 400
|
|
94
|
+
} : {
|
|
95
|
+
width: "100%",
|
|
96
|
+
maxHeight: "80%"
|
|
97
|
+
}, { transform: [horizontal ? { translateX: translate } : { translateY: translate }] }],
|
|
98
|
+
children: /* @__PURE__ */ jsxs(View, {
|
|
99
|
+
testID,
|
|
100
|
+
className: cn(drawerVariants({ side }), horizontal && "flex-1", className),
|
|
101
|
+
children: [
|
|
102
|
+
/* @__PURE__ */ jsxs(View, {
|
|
103
|
+
className: "flex-row items-center gap-4 border-b border-border-default px-6 py-4",
|
|
104
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
105
|
+
accessibilityRole: "header",
|
|
106
|
+
className: "flex-1 text-lg font-semibold leading-6 text-text-primary",
|
|
107
|
+
children: title
|
|
108
|
+
}), /* @__PURE__ */ jsx(Pressable, {
|
|
109
|
+
accessibilityRole: "button",
|
|
110
|
+
accessibilityLabel: closeLabel,
|
|
111
|
+
hitSlop: 8,
|
|
112
|
+
onPress: close,
|
|
113
|
+
className: "size-8 items-center justify-center rounded-full active:bg-button-secondary-bg-hover",
|
|
114
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { color: iconColor })
|
|
115
|
+
})]
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ jsx(ScrollView, {
|
|
118
|
+
className: "shrink",
|
|
119
|
+
contentContainerClassName: "gap-4 p-6",
|
|
120
|
+
children: typeof children === "string" ? /* @__PURE__ */ jsx(Text, {
|
|
121
|
+
className: "text-sm leading-5 text-text-secondary",
|
|
122
|
+
children
|
|
123
|
+
}) : children
|
|
124
|
+
}),
|
|
125
|
+
footer && /* @__PURE__ */ jsx(View, {
|
|
126
|
+
className: "flex-row items-center justify-end gap-2 border-t border-border-default px-6 py-4",
|
|
127
|
+
children: footer
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
})
|
|
131
|
+
})]
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
})] });
|
|
135
|
+
}
|
|
136
|
+
/** Fecha o painel ao pressionar. Envolva um `<Button>` do rodapé (injeta `onPress`, como o `asChild`). */
|
|
137
|
+
function DrawerClose({ children }) {
|
|
138
|
+
const close = useContext(CloseContext);
|
|
139
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: injectHandler(children, "onPress", () => close?.()) });
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { Drawer, DrawerClose };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=drawer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drawer.js","names":["RNModal"],"sources":["../../../src/components/drawer/drawer.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { createContext, type ReactNode, useContext, useEffect, useRef, useState } from 'react'\nimport {\n Animated,\n Pressable,\n Modal as RNModal,\n ScrollView,\n Text,\n useWindowDimensions,\n View,\n} from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { Backdrop } from '../_shared/backdrop'\nimport { injectHandler, useOpenState } from '../_shared/overlay-state'\nimport { CloseIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\n\nconst drawerVariants = cva('overflow-hidden border-border-default bg-surface-elevated shadow-2xl', {\n variants: {\n side: {\n right: 'border-l',\n left: 'border-r',\n top: 'border-b',\n bottom: 'border-t',\n },\n },\n defaultVariants: { side: 'right' },\n})\n\nconst containerVariants = cva('flex-1', {\n variants: {\n side: {\n right: 'flex-row justify-end',\n left: 'flex-row justify-start',\n top: 'justify-start',\n bottom: 'justify-end',\n },\n },\n defaultVariants: { side: 'right' },\n})\n\nconst CloseContext = createContext<(() => void) | null>(null)\n\ntype Side = 'right' | 'left' | 'top' | 'bottom'\n\nexport type DrawerProps = VariantProps<typeof drawerVariants> & {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Título do painel; também é o nome acessível dele. */\n title: ReactNode\n /** O corpo do painel. */\n children: ReactNode\n /** Ações do rodapé (primária à direita). Use `DrawerClose` para fechar. Sem ele, não há rodapé. */\n footer?: ReactNode\n /** Elemento que abre o painel (ex.: um `<Button>`); recebe `onPress`. Dispensável no modo controlado. */\n trigger?: ReactNode\n /** Nome acessível do botão de fechar (×). */\n closeLabel?: string\n className?: string\n testID?: string\n}\n\n/**\n * Painel colado a uma borda da tela, que desliza ao abrir e fechar. Usa o `Modal` nativo (botão\n * voltar do Android e toque no fundo fecham). Não há foco preso nem `Esc`.\n */\nexport function Drawer({\n open,\n defaultOpen,\n onOpenChange,\n title,\n children,\n footer,\n trigger,\n closeLabel = 'Fechar',\n side,\n className,\n testID,\n}: DrawerProps) {\n const [isOpen, setOpen] = useOpenState({ open, defaultOpen, onOpenChange })\n const close = () => setOpen(false)\n const iconColor = useToken('--color-text-primary')\n const { width, height } = useWindowDimensions()\n const placement: Side = side ?? 'right'\n const horizontal = placement === 'left' || placement === 'right'\n\n // O Modal fica montado durante a animação de saída.\n const [mounted, setMounted] = useState(isOpen)\n const progress = useRef(new Animated.Value(isOpen ? 1 : 0)).current\n\n useEffect(() => {\n if (isOpen) setMounted(true)\n const animation = Animated.timing(progress, {\n toValue: isOpen ? 1 : 0,\n duration: 250,\n useNativeDriver: true,\n })\n animation.start(({ finished }) => {\n if (finished && !isOpen) setMounted(false)\n })\n return () => animation.stop()\n }, [isOpen, progress])\n\n const offset = horizontal ? width : height\n const sign = placement === 'right' || placement === 'bottom' ? 1 : -1\n const translate = progress.interpolate({ inputRange: [0, 1], outputRange: [sign * offset, 0] })\n\n return (\n <>\n {injectHandler(trigger, 'onPress', () => setOpen(true))}\n <RNModal\n transparent\n statusBarTranslucent\n animationType=\"none\"\n visible={mounted}\n onRequestClose={close}\n >\n <CloseContext.Provider value={close}>\n <View className={containerVariants({ side })} accessibilityViewIsModal>\n <Animated.View\n pointerEvents=\"box-none\"\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n opacity: progress,\n }}\n >\n <Backdrop onPress={close} testID={testID ? `${testID}-backdrop` : undefined} />\n </Animated.View>\n <Animated.View\n style={[\n horizontal ? { width: '85%', maxWidth: 400 } : { width: '100%', maxHeight: '80%' },\n { transform: [horizontal ? { translateX: translate } : { translateY: translate }] },\n ]}\n >\n <View\n testID={testID}\n className={cn(drawerVariants({ side }), horizontal && 'flex-1', className)}\n >\n <View className=\"flex-row items-center gap-4 border-b border-border-default px-6 py-4\">\n <Text\n accessibilityRole=\"header\"\n className=\"flex-1 text-lg font-semibold leading-6 text-text-primary\"\n >\n {title}\n </Text>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n hitSlop={8}\n onPress={close}\n className=\"size-8 items-center justify-center rounded-full active:bg-button-secondary-bg-hover\"\n >\n <CloseIcon color={iconColor} />\n </Pressable>\n </View>\n <ScrollView className=\"shrink\" contentContainerClassName=\"gap-4 p-6\">\n {typeof children === 'string' ? (\n <Text className=\"text-sm leading-5 text-text-secondary\">{children}</Text>\n ) : (\n children\n )}\n </ScrollView>\n {footer && (\n <View className=\"flex-row items-center justify-end gap-2 border-t border-border-default px-6 py-4\">\n {footer}\n </View>\n )}\n </View>\n </Animated.View>\n </View>\n </CloseContext.Provider>\n </RNModal>\n </>\n )\n}\n\n/** Fecha o painel ao pressionar. Envolva um `<Button>` do rodapé (injeta `onPress`, como o `asChild`). */\nexport function DrawerClose({ children }: { children: ReactNode }) {\n const close = useContext(CloseContext)\n return <>{injectHandler(children, 'onPress', () => close?.())}</>\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from "react";
|
|
2
|
+
//#region src/components/field/field.d.ts
|
|
3
|
+
export type FieldProps = {
|
|
4
|
+
/** Texto do rótulo. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Exibe um asterisco vermelho após o rótulo. */
|
|
7
|
+
required?: boolean;
|
|
8
|
+
/** Propaga vermelho para rótulo, borda do controle e texto de apoio. */
|
|
9
|
+
error?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
/** Propaga somente leitura ao controle. */
|
|
12
|
+
readOnly?: boolean;
|
|
13
|
+
/** Tamanho do rótulo e do controle (o `sm` usa rótulo de 12px). */
|
|
14
|
+
size?: 'sm' | 'md' | 'lg';
|
|
15
|
+
/** Instrução (estado normal) ou mensagem de erro (estado error). */
|
|
16
|
+
supportingText?: ReactNode;
|
|
17
|
+
/** O controle: Input, Textarea ou Select. Recebe disabled/error/size e o nome acessível. */
|
|
18
|
+
children: ReactElement<{
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
size?: 'sm' | 'md' | 'lg';
|
|
22
|
+
error?: boolean;
|
|
23
|
+
accessibilityLabel?: string;
|
|
24
|
+
accessibilityHint?: string;
|
|
25
|
+
}>;
|
|
26
|
+
className?: string;
|
|
27
|
+
};
|
|
28
|
+
/** Combina rótulo + controle + texto de apoio — o jeito certo de montar um campo de formulário. */
|
|
29
|
+
export declare function Field({ label, required, error, disabled, readOnly, size, supportingText, children, className }: FieldProps): import("react").JSX.Element;
|
|
30
|
+
//#endregion
|
|
31
|
+
//# sourceMappingURL=field.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { StatusIcon } from "../_shared/status.js";
|
|
3
|
+
import { useToken } from "../_shared/use-token.js";
|
|
4
|
+
import { cloneElement } from "react";
|
|
5
|
+
import { Text, View } from "react-native";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/field/field.tsx
|
|
8
|
+
/** Combina rótulo + controle + texto de apoio — o jeito certo de montar um campo de formulário. */
|
|
9
|
+
function Field({ label, required, error, disabled, readOnly, size, supportingText, children, className }) {
|
|
10
|
+
const errorColor = useToken("--color-status-error-text");
|
|
11
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
12
|
+
className: cn("gap-1", className),
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
15
|
+
className: cn("font-medium text-field-text-label", size === "sm" ? "text-xs leading-4" : "text-sm", error && "text-status-error-text"),
|
|
16
|
+
children: [label, required ? /* @__PURE__ */ jsx(Text, {
|
|
17
|
+
className: "text-status-error-text",
|
|
18
|
+
accessibilityElementsHidden: true,
|
|
19
|
+
importantForAccessibility: "no",
|
|
20
|
+
children: " *"
|
|
21
|
+
}) : null]
|
|
22
|
+
}),
|
|
23
|
+
cloneElement(children, {
|
|
24
|
+
disabled: disabled ?? children.props.disabled,
|
|
25
|
+
readOnly: readOnly ?? children.props.readOnly,
|
|
26
|
+
size: size ?? children.props.size,
|
|
27
|
+
error: error ?? children.props.error,
|
|
28
|
+
accessibilityLabel: children.props.accessibilityLabel ?? label,
|
|
29
|
+
accessibilityHint: children.props.accessibilityHint ?? (typeof supportingText === "string" ? supportingText : void 0)
|
|
30
|
+
}),
|
|
31
|
+
supportingText ? /* @__PURE__ */ jsxs(View, {
|
|
32
|
+
className: "flex-row items-center gap-1",
|
|
33
|
+
accessibilityLiveRegion: error ? "polite" : void 0,
|
|
34
|
+
children: [error ? /* @__PURE__ */ jsx(StatusIcon, {
|
|
35
|
+
status: "error",
|
|
36
|
+
color: errorColor,
|
|
37
|
+
style: {
|
|
38
|
+
width: 16,
|
|
39
|
+
height: 16
|
|
40
|
+
}
|
|
41
|
+
}) : null, typeof supportingText === "string" ? /* @__PURE__ */ jsx(Text, {
|
|
42
|
+
className: cn("shrink text-xs leading-4 text-field-text-label", error && "text-status-error-text"),
|
|
43
|
+
children: supportingText
|
|
44
|
+
}) : supportingText]
|
|
45
|
+
}) : null
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Field };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=field.js.map
|