@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,90 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { ChevronRightIcon } from "../_shared/status.js";
|
|
3
|
+
import { useToken } from "../_shared/use-token.js";
|
|
4
|
+
import { Pressable, Text, View } from "react-native";
|
|
5
|
+
import Svg, { Path } from "react-native-svg";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { cva } from "class-variance-authority";
|
|
8
|
+
//#region src/components/select/field-trigger.tsx
|
|
9
|
+
const triggerVariants = cva("w-full flex-row items-center justify-between gap-2 rounded-md border", {
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
lg: "h-14 px-4",
|
|
13
|
+
md: "h-12 px-3",
|
|
14
|
+
sm: "h-10 px-2"
|
|
15
|
+
},
|
|
16
|
+
state: {
|
|
17
|
+
idle: "border-field-border bg-field-bg",
|
|
18
|
+
open: "border-field-focus-ring bg-field-bg",
|
|
19
|
+
error: "border-field-border-error bg-field-bg",
|
|
20
|
+
disabled: "border-field-border-disabled bg-field-bg-disabled",
|
|
21
|
+
readOnly: "border-transparent bg-field-bg-disabled"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
size: "md",
|
|
26
|
+
state: "idle"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const textVariants = cva("flex-1 text-base", { variants: { tone: {
|
|
30
|
+
value: "text-field-text",
|
|
31
|
+
placeholder: "text-field-text-placeholder",
|
|
32
|
+
disabled: "text-field-text-disabled"
|
|
33
|
+
} } });
|
|
34
|
+
function CalendarIcon({ color }) {
|
|
35
|
+
return /* @__PURE__ */ jsx(Svg, {
|
|
36
|
+
width: 20,
|
|
37
|
+
height: 20,
|
|
38
|
+
viewBox: "0 0 24 24",
|
|
39
|
+
fill: "none",
|
|
40
|
+
stroke: color,
|
|
41
|
+
strokeWidth: 1.5,
|
|
42
|
+
children: /* @__PURE__ */ jsx(Path, {
|
|
43
|
+
strokeLinecap: "round",
|
|
44
|
+
strokeLinejoin: "round",
|
|
45
|
+
d: "M8 3v3M16 3v3M4 9h16M5 5h14a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z"
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Gatilho com visual de Input para Select, Combobox, DatePicker e DateRangePicker (uso interno,
|
|
51
|
+
* não faz parte da API pública). Sem hover/focus-visible no native: o estado aberto usa a cor do
|
|
52
|
+
* anel de foco na borda.
|
|
53
|
+
*/
|
|
54
|
+
function FieldTrigger({ anchorRef, text, placeholder, icon, size, error, open, disabled, readOnly, onPress, className, accessibilityLabel, accessibilityHint, testID }) {
|
|
55
|
+
const iconColor = useToken("--color-field-text-placeholder");
|
|
56
|
+
const hasText = text !== void 0 && text !== null && text !== "";
|
|
57
|
+
return /* @__PURE__ */ jsx(View, {
|
|
58
|
+
ref: anchorRef,
|
|
59
|
+
collapsable: false,
|
|
60
|
+
className: "w-full",
|
|
61
|
+
children: /* @__PURE__ */ jsxs(Pressable, {
|
|
62
|
+
accessibilityRole: "combobox",
|
|
63
|
+
accessibilityLabel,
|
|
64
|
+
accessibilityHint,
|
|
65
|
+
accessibilityState: {
|
|
66
|
+
expanded: Boolean(open),
|
|
67
|
+
disabled: Boolean(disabled)
|
|
68
|
+
},
|
|
69
|
+
disabled,
|
|
70
|
+
onPress: readOnly ? void 0 : onPress,
|
|
71
|
+
testID,
|
|
72
|
+
className: cn(triggerVariants({
|
|
73
|
+
size,
|
|
74
|
+
state: disabled ? "disabled" : readOnly ? "readOnly" : open ? "open" : error ? "error" : "idle"
|
|
75
|
+
}), className),
|
|
76
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
77
|
+
numberOfLines: 1,
|
|
78
|
+
className: textVariants({ tone: disabled ? "disabled" : hasText ? "value" : "placeholder" }),
|
|
79
|
+
children: hasText ? text : placeholder
|
|
80
|
+
}), icon === "calendar" ? /* @__PURE__ */ jsx(CalendarIcon, { color: iconColor }) : /* @__PURE__ */ jsx(View, {
|
|
81
|
+
className: open ? "-rotate-90" : "rotate-90",
|
|
82
|
+
children: /* @__PURE__ */ jsx(ChevronRightIcon, { color: iconColor })
|
|
83
|
+
})]
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { FieldTrigger };
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=field-trigger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-trigger.js","names":[],"sources":["../../../src/components/select/field-trigger.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva } from 'class-variance-authority'\nimport type { ReactNode, RefObject } from 'react'\nimport { Pressable, Text, View } from 'react-native'\nimport Svg, { Path } from 'react-native-svg'\nimport { cn } from '../../utils/cn'\nimport { ChevronRightIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\n\nexport type FieldSize = 'sm' | 'md' | 'lg'\n\nconst triggerVariants = cva(\n 'w-full flex-row items-center justify-between gap-2 rounded-md border',\n {\n variants: {\n size: {\n lg: 'h-14 px-4',\n md: 'h-12 px-3',\n sm: 'h-10 px-2',\n },\n state: {\n idle: 'border-field-border bg-field-bg',\n open: 'border-field-focus-ring bg-field-bg',\n error: 'border-field-border-error bg-field-bg',\n disabled: 'border-field-border-disabled bg-field-bg-disabled',\n readOnly: 'border-transparent bg-field-bg-disabled',\n },\n },\n defaultVariants: { size: 'md', state: 'idle' },\n },\n)\n\nconst textVariants = cva('flex-1 text-base', {\n variants: {\n tone: {\n value: 'text-field-text',\n placeholder: 'text-field-text-placeholder',\n disabled: 'text-field-text-disabled',\n },\n },\n})\n\nexport type FieldTriggerProps = {\n /** Âncora medida por `useAnchor` (a View que envolve o gatilho). */\n anchorRef?: RefObject<View>\n /** Texto do valor; sem ele, mostra o placeholder. */\n text?: ReactNode\n placeholder: string\n icon: 'chevron' | 'calendar'\n size?: FieldSize\n error?: boolean\n open?: boolean\n disabled?: boolean\n readOnly?: boolean\n onPress?: () => void\n className?: string\n accessibilityLabel?: string\n accessibilityHint?: string\n testID?: string\n}\n\nfunction CalendarIcon({ color }: { color?: string }) {\n return (\n <Svg width={20} height={20} viewBox=\"0 0 24 24\" fill=\"none\" stroke={color} strokeWidth={1.5}>\n <Path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M8 3v3M16 3v3M4 9h16M5 5h14a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z\"\n />\n </Svg>\n )\n}\n\n/**\n * Gatilho com visual de Input para Select, Combobox, DatePicker e DateRangePicker (uso interno,\n * não faz parte da API pública). Sem hover/focus-visible no native: o estado aberto usa a cor do\n * anel de foco na borda.\n */\nexport function FieldTrigger({\n anchorRef,\n text,\n placeholder,\n icon,\n size,\n error,\n open,\n disabled,\n readOnly,\n onPress,\n className,\n accessibilityLabel,\n accessibilityHint,\n testID,\n}: FieldTriggerProps) {\n const iconColor = useToken('--color-field-text-placeholder')\n const hasText = text !== undefined && text !== null && text !== ''\n const state = disabled\n ? 'disabled'\n : readOnly\n ? 'readOnly'\n : open\n ? 'open'\n : error\n ? 'error'\n : 'idle'\n\n return (\n <View ref={anchorRef} collapsable={false} className=\"w-full\">\n <Pressable\n accessibilityRole=\"combobox\"\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={accessibilityHint}\n accessibilityState={{ expanded: Boolean(open), disabled: Boolean(disabled) }}\n disabled={disabled}\n onPress={readOnly ? undefined : onPress}\n testID={testID}\n className={cn(triggerVariants({ size, state }), className)}\n >\n <Text\n numberOfLines={1}\n className={textVariants({\n tone: disabled ? 'disabled' : hasText ? 'value' : 'placeholder',\n })}\n >\n {hasText ? text : placeholder}\n </Text>\n {icon === 'calendar' ? (\n <CalendarIcon color={iconColor} />\n ) : (\n <View className={open ? '-rotate-90' : 'rotate-90'}>\n <ChevronRightIcon color={iconColor} />\n </View>\n )}\n </Pressable>\n </View>\n )\n}\n"],"mappings":";;;;;;;;AAYA,MAAM,kBAAkB,IACtB,wEACA;CACE,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,OAAO;GACL,MAAM;GACN,MAAM;GACN,OAAO;GACP,UAAU;GACV,UAAU;EACZ;CACF;CACA,iBAAiB;EAAE,MAAM;EAAM,OAAO;CAAO;AAC/C,CACF;AAEA,MAAM,eAAe,IAAI,oBAAoB,EAC3C,UAAU,EACR,MAAM;CACJ,OAAO;CACP,aAAa;CACb,UAAU;AACZ,EACF,EACF,CAAC;AAqBD,SAAS,aAAa,EAAE,SAA6B;CACnD,OACE,oBAAC,KAAD;EAAK,OAAO;EAAI,QAAQ;EAAI,SAAQ;EAAY,MAAK;EAAO,QAAQ;EAAO,aAAa;EACtF,UAAA,oBAAC,MAAD;GACE,eAAc;GACd,gBAAe;GACf,GAAE;EACH,CAAA;CACE,CAAA;AAET;;;;;;AAOA,SAAgB,aAAa,EAC3B,WACA,MACA,aACA,MACA,MACA,OACA,MACA,UACA,UACA,SACA,WACA,oBACA,mBACA,UACoB;CACpB,MAAM,YAAY,SAAS,gCAAgC;CAC3D,MAAM,UAAU,SAAS,KAAA,KAAa,SAAS,QAAQ,SAAS;CAWhE,OACE,oBAAC,MAAD;EAAM,KAAK;EAAW,aAAa;EAAO,WAAU;EAClD,UAAA,qBAAC,WAAD;GACE,mBAAkB;GACE;GACD;GACnB,oBAAoB;IAAE,UAAU,QAAQ,IAAI;IAAG,UAAU,QAAQ,QAAQ;GAAE;GACjE;GACV,SAAS,WAAW,KAAA,IAAY;GACxB;GACR,WAAW,GAAG,gBAAgB;IAAE;IAAM,OApB9B,WACV,aACA,WACE,aACA,OACE,SACA,QACE,UACA;GAYwC,CAAC,GAAG,SAAS;GAR3D,UAAA,CAUE,oBAAC,MAAD;IACE,eAAe;IACf,WAAW,aAAa,EACtB,MAAM,WAAW,aAAa,UAAU,UAAU,cACpD,CAAC;IAEA,UAAA,UAAU,OAAO;GACd,CAAA,GACL,SAAS,aACR,oBAAC,cAAD,EAAc,OAAO,UAAY,CAAA,IAEjC,oBAAC,MAAD;IAAM,WAAW,OAAO,eAAe;IACrC,UAAA,oBAAC,kBAAD,EAAkB,OAAO,UAAY,CAAA;GACjC,CAAA,CAEC;;CACP,CAAA;AAEV"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FieldSize } from "./field-trigger.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/components/select/select.d.ts
|
|
4
|
+
export type SelectProps = {
|
|
5
|
+
/** Opções: um ou mais `<SelectOption>`. */
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
onValueChange?: (value: string) => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Mostra o valor mas nunca abre a lista, como um `<TextInput editable={false}>`. */
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
error?: boolean;
|
|
15
|
+
size?: FieldSize;
|
|
16
|
+
className?: string;
|
|
17
|
+
accessibilityLabel?: string;
|
|
18
|
+
accessibilityHint?: string;
|
|
19
|
+
testID?: string;
|
|
20
|
+
};
|
|
21
|
+
export type SelectOptionProps = {
|
|
22
|
+
value: string;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Campo de escolha única. Toque no gatilho abre a lista numa camada ancorada (largura do gatilho,
|
|
28
|
+
* rolável). Diferenças do web: sem navegação por teclado, sem `name`/`required`/`id` (não há
|
|
29
|
+
* formulário nativo).
|
|
30
|
+
*/
|
|
31
|
+
export declare function Select({ children, placeholder, value, defaultValue, onValueChange, size, error, disabled, readOnly, className, accessibilityLabel, accessibilityHint, testID }: SelectProps): import("react").JSX.Element;
|
|
32
|
+
/** Opção da lista do Select. O `children` é o rótulo mostrado na lista e no gatilho. */
|
|
33
|
+
export declare function SelectOption({ value, disabled, children }: SelectOptionProps): import("react").JSX.Element;
|
|
34
|
+
//#endregion
|
|
35
|
+
//# sourceMappingURL=select.d.ts.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { CheckIcon } from "../_shared/status.js";
|
|
3
|
+
import { useToken } from "../_shared/use-token.js";
|
|
4
|
+
import { AnchoredOverlay, useAnchor } from "../_shared/anchored-overlay.js";
|
|
5
|
+
import { FieldTrigger } from "./field-trigger.js";
|
|
6
|
+
import { Children, createContext, isValidElement, useContext, useState } from "react";
|
|
7
|
+
import { Pressable, ScrollView, Text, View } from "react-native";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/components/select/select.tsx
|
|
10
|
+
const SelectContext = createContext({
|
|
11
|
+
value: void 0,
|
|
12
|
+
onSelect: () => {}
|
|
13
|
+
});
|
|
14
|
+
function findLabel(children, value) {
|
|
15
|
+
for (const child of Children.toArray(children)) {
|
|
16
|
+
if (!isValidElement(child)) continue;
|
|
17
|
+
const element = child;
|
|
18
|
+
if (element.type === SelectOption && element.props.value === value) return element.props.children;
|
|
19
|
+
const nested = findLabel(element.props.children, value);
|
|
20
|
+
if (nested !== void 0) return nested;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Campo de escolha única. Toque no gatilho abre a lista numa camada ancorada (largura do gatilho,
|
|
25
|
+
* rolável). Diferenças do web: sem navegação por teclado, sem `name`/`required`/`id` (não há
|
|
26
|
+
* formulário nativo).
|
|
27
|
+
*/
|
|
28
|
+
function Select({ children, placeholder = "Selecione...", value, defaultValue, onValueChange, size, error, disabled, readOnly = false, className, accessibilityLabel, accessibilityHint, testID }) {
|
|
29
|
+
const [inner, setInner] = useState(defaultValue);
|
|
30
|
+
const selected = value ?? inner;
|
|
31
|
+
const anchor = useAnchor();
|
|
32
|
+
const open = anchor.visible && !disabled && !readOnly;
|
|
33
|
+
function onSelect(next) {
|
|
34
|
+
if (value === void 0) setInner(next);
|
|
35
|
+
onValueChange?.(next);
|
|
36
|
+
anchor.close();
|
|
37
|
+
}
|
|
38
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(FieldTrigger, {
|
|
39
|
+
anchorRef: anchor.ref,
|
|
40
|
+
icon: "chevron",
|
|
41
|
+
text: selected === void 0 ? void 0 : findLabel(children, selected),
|
|
42
|
+
placeholder,
|
|
43
|
+
size,
|
|
44
|
+
error,
|
|
45
|
+
open,
|
|
46
|
+
disabled,
|
|
47
|
+
readOnly,
|
|
48
|
+
onPress: anchor.open,
|
|
49
|
+
className,
|
|
50
|
+
accessibilityLabel,
|
|
51
|
+
accessibilityHint,
|
|
52
|
+
testID
|
|
53
|
+
}), /* @__PURE__ */ jsx(AnchoredOverlay, {
|
|
54
|
+
visible: open,
|
|
55
|
+
rect: anchor.rect,
|
|
56
|
+
onRequestClose: anchor.close,
|
|
57
|
+
matchAnchorWidth: true,
|
|
58
|
+
testID: testID ? `${testID}-list` : void 0,
|
|
59
|
+
className: "overflow-hidden rounded-md border border-select-dropdown-border bg-select-dropdown-bg p-1 shadow-md",
|
|
60
|
+
children: /* @__PURE__ */ jsx(SelectContext.Provider, {
|
|
61
|
+
value: {
|
|
62
|
+
value: selected,
|
|
63
|
+
onSelect
|
|
64
|
+
},
|
|
65
|
+
children: /* @__PURE__ */ jsx(ScrollView, {
|
|
66
|
+
className: "max-h-60",
|
|
67
|
+
children
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
})] });
|
|
71
|
+
}
|
|
72
|
+
/** Opção da lista do Select. O `children` é o rótulo mostrado na lista e no gatilho. */
|
|
73
|
+
function SelectOption({ value, disabled, children }) {
|
|
74
|
+
const ctx = useContext(SelectContext);
|
|
75
|
+
const selected = ctx.value === value;
|
|
76
|
+
const checkColor = useToken("--color-select-option-text");
|
|
77
|
+
return /* @__PURE__ */ jsxs(Pressable, {
|
|
78
|
+
role: "option",
|
|
79
|
+
accessibilityState: {
|
|
80
|
+
selected,
|
|
81
|
+
disabled: Boolean(disabled)
|
|
82
|
+
},
|
|
83
|
+
disabled,
|
|
84
|
+
onPress: () => ctx.onSelect(value),
|
|
85
|
+
className: cn("flex-row items-center gap-2 rounded-sm px-3 py-2 active:bg-select-option-bg-hover", selected && "bg-select-option-bg-selected"),
|
|
86
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
87
|
+
className: cn("flex-1 text-base text-select-option-text", disabled && "text-select-option-text-disabled"),
|
|
88
|
+
children
|
|
89
|
+
}), selected ? /* @__PURE__ */ jsx(View, {
|
|
90
|
+
className: "size-4 items-center justify-center",
|
|
91
|
+
children: /* @__PURE__ */ jsx(CheckIcon, { color: checkColor })
|
|
92
|
+
}) : null]
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
export { Select, SelectOption };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select.js","names":[],"sources":["../../../src/components/select/select.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport {\n Children,\n createContext,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useContext,\n useState,\n} from 'react'\nimport { Pressable, ScrollView, Text, View } from 'react-native'\nimport { cn } from '../../utils/cn'\nimport { AnchoredOverlay, useAnchor } from '../_shared/anchored-overlay'\nimport { CheckIcon } from '../_shared/status'\nimport { useToken } from '../_shared/use-token'\nimport { type FieldSize, FieldTrigger } from './field-trigger'\n\ntype SelectContextValue = { value: string | undefined; onSelect: (value: string) => void }\nconst SelectContext = createContext<SelectContextValue>({ value: undefined, onSelect: () => {} })\n\nexport type SelectProps = {\n /** Opções: um ou mais `<SelectOption>`. */\n children: ReactNode\n placeholder?: string\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n disabled?: boolean\n /** Mostra o valor mas nunca abre a lista, como um `<TextInput editable={false}>`. */\n readOnly?: boolean\n error?: boolean\n size?: FieldSize\n className?: string\n accessibilityLabel?: string\n accessibilityHint?: string\n testID?: string\n}\n\nexport type SelectOptionProps = {\n value: string\n disabled?: boolean\n children: ReactNode\n}\n\n// Procura o rótulo da opção escolhida nos filhos (inclusive dentro de Fragments).\nfunction findLabel(children: ReactNode, value: string | undefined): ReactNode | undefined {\n for (const child of Children.toArray(children)) {\n if (!isValidElement(child)) continue\n const element = child as ReactElement<{ value?: string; children?: ReactNode }>\n if (element.type === SelectOption && element.props.value === value)\n return element.props.children\n const nested = findLabel(element.props.children, value)\n if (nested !== undefined) return nested\n }\n return undefined\n}\n\n/**\n * Campo de escolha única. Toque no gatilho abre a lista numa camada ancorada (largura do gatilho,\n * rolável). Diferenças do web: sem navegação por teclado, sem `name`/`required`/`id` (não há\n * formulário nativo).\n */\nexport function Select({\n children,\n placeholder = 'Selecione...',\n value,\n defaultValue,\n onValueChange,\n size,\n error,\n disabled,\n readOnly = false,\n className,\n accessibilityLabel,\n accessibilityHint,\n testID,\n}: SelectProps) {\n const [inner, setInner] = useState(defaultValue)\n const selected = value ?? inner\n const anchor = useAnchor()\n const open = anchor.visible && !disabled && !readOnly\n\n function onSelect(next: string) {\n if (value === undefined) setInner(next)\n onValueChange?.(next)\n anchor.close()\n }\n\n return (\n <>\n <FieldTrigger\n anchorRef={anchor.ref}\n icon=\"chevron\"\n text={selected === undefined ? undefined : findLabel(children, selected)}\n placeholder={placeholder}\n size={size}\n error={error}\n open={open}\n disabled={disabled}\n readOnly={readOnly}\n onPress={anchor.open}\n className={className}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={accessibilityHint}\n testID={testID}\n />\n <AnchoredOverlay\n visible={open}\n rect={anchor.rect}\n onRequestClose={anchor.close}\n matchAnchorWidth\n testID={testID ? `${testID}-list` : undefined}\n className=\"overflow-hidden rounded-md border border-select-dropdown-border bg-select-dropdown-bg p-1 shadow-md\"\n >\n <SelectContext.Provider value={{ value: selected, onSelect }}>\n <ScrollView className=\"max-h-60\">{children}</ScrollView>\n </SelectContext.Provider>\n </AnchoredOverlay>\n </>\n )\n}\n\n/** Opção da lista do Select. O `children` é o rótulo mostrado na lista e no gatilho. */\nexport function SelectOption({ value, disabled, children }: SelectOptionProps) {\n const ctx = useContext(SelectContext)\n const selected = ctx.value === value\n const checkColor = useToken('--color-select-option-text')\n\n return (\n <Pressable\n role=\"option\"\n accessibilityState={{ selected, disabled: Boolean(disabled) }}\n disabled={disabled}\n onPress={() => ctx.onSelect(value)}\n className={cn(\n 'flex-row items-center gap-2 rounded-sm px-3 py-2 active:bg-select-option-bg-hover',\n selected && 'bg-select-option-bg-selected',\n )}\n >\n <Text\n className={cn(\n 'flex-1 text-base text-select-option-text',\n disabled && 'text-select-option-text-disabled',\n )}\n >\n {children}\n </Text>\n {selected ? (\n <View className=\"size-4 items-center justify-center\">\n <CheckIcon color={checkColor} />\n </View>\n ) : null}\n </Pressable>\n )\n}\n"],"mappings":";;;;;;;;;AAmBA,MAAM,gBAAgB,cAAkC;CAAE,OAAO,KAAA;CAAW,gBAAgB,CAAC;AAAE,CAAC;AA2BhG,SAAS,UAAU,UAAqB,OAAkD;CACxF,KAAK,MAAM,SAAS,SAAS,QAAQ,QAAQ,GAAG;EAC9C,IAAI,CAAC,eAAe,KAAK,GAAG;EAC5B,MAAM,UAAU;EAChB,IAAI,QAAQ,SAAS,gBAAgB,QAAQ,MAAM,UAAU,OAC3D,OAAO,QAAQ,MAAM;EACvB,MAAM,SAAS,UAAU,QAAQ,MAAM,UAAU,KAAK;EACtD,IAAI,WAAW,KAAA,GAAW,OAAO;CACnC;AAEF;;;;;;AAOA,SAAgB,OAAO,EACrB,UACA,cAAc,gBACd,OACA,cACA,eACA,MACA,OACA,UACA,WAAW,OACX,WACA,oBACA,mBACA,UACc;CACd,MAAM,CAAC,OAAO,YAAY,SAAS,YAAY;CAC/C,MAAM,WAAW,SAAS;CAC1B,MAAM,SAAS,UAAU;CACzB,MAAM,OAAO,OAAO,WAAW,CAAC,YAAY,CAAC;CAE7C,SAAS,SAAS,MAAc;EAC9B,IAAI,UAAU,KAAA,GAAW,SAAS,IAAI;EACtC,gBAAgB,IAAI;EACpB,OAAO,MAAM;CACf;CAEA,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,cAAD;EACE,WAAW,OAAO;EAClB,MAAK;EACL,MAAM,aAAa,KAAA,IAAY,KAAA,IAAY,UAAU,UAAU,QAAQ;EAC1D;EACP;EACC;EACD;EACI;EACA;EACV,SAAS,OAAO;EACL;EACS;EACD;EACX;CACT,CAAA,GACD,oBAAC,iBAAD;EACE,SAAS;EACT,MAAM,OAAO;EACb,gBAAgB,OAAO;EACvB,kBAAA;EACA,QAAQ,SAAS,GAAG,OAAO,SAAS,KAAA;EACpC,WAAU;EAEV,UAAA,oBAAC,cAAc,UAAf;GAAwB,OAAO;IAAE,OAAO;IAAU;GAAS;GACzD,UAAA,oBAAC,YAAD;IAAY,WAAU;IAAY;GAAqB,CAAA;EACjC,CAAA;CACT,CAAA,CACjB,EAAA,CAAA;AAEN;;AAGA,SAAgB,aAAa,EAAE,OAAO,UAAU,YAA+B;CAC7E,MAAM,MAAM,WAAW,aAAa;CACpC,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,aAAa,SAAS,4BAA4B;CAExD,OACE,qBAAC,WAAD;EACE,MAAK;EACL,oBAAoB;GAAE;GAAU,UAAU,QAAQ,QAAQ;EAAE;EAClD;EACV,eAAe,IAAI,SAAS,KAAK;EACjC,WAAW,GACT,qFACA,YAAY,8BACd;EARF,UAAA,CAUE,oBAAC,MAAD;GACE,WAAW,GACT,4CACA,YAAY,kCACd;GAEC;EACG,CAAA,GACL,WACC,oBAAC,MAAD;GAAM,WAAU;GACd,UAAA,oBAAC,WAAD,EAAW,OAAO,WAAa,CAAA;EAC3B,CAAA,IACJ,IACK;;AAEf"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ViewProps } from "react-native";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
|
+
//#region src/components/skeleton/skeleton.d.ts
|
|
4
|
+
declare const skeletonVariants: (props?: ({
|
|
5
|
+
variant?: "avatar" | "card" | "rectangle" | "text" | "title" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
export type SkeletonProps = Omit<ViewProps, 'children'> & VariantProps<typeof skeletonVariants> & {
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Placeholder de conteúdo em carregamento. É decorativo (escondido do leitor de tela): anuncie o
|
|
12
|
+
* carregamento no contêiner, com `accessibilityState={{ busy: true }}`. Para ações pontuais, use
|
|
13
|
+
* Spinner. Não respeita "reduzir movimento" (a web faz via `motion-reduce`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function Skeleton({ className, variant, ...props }: SkeletonProps): import("react").JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
//# sourceMappingURL=skeleton.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { Animated, Easing, StyleSheet, View } from "react-native";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
//#region src/components/skeleton/skeleton.tsx
|
|
7
|
+
const skeletonVariants = cva("overflow-hidden", {
|
|
8
|
+
variants: { variant: {
|
|
9
|
+
text: "h-3 w-[200px] rounded-sm",
|
|
10
|
+
title: "h-5 w-[160px] rounded-sm",
|
|
11
|
+
avatar: "size-10 rounded-full",
|
|
12
|
+
card: "h-[120px] w-[300px] rounded-md",
|
|
13
|
+
rectangle: "h-6 w-[200px] rounded-md"
|
|
14
|
+
} },
|
|
15
|
+
defaultVariants: { variant: "text" }
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Placeholder de conteúdo em carregamento. É decorativo (escondido do leitor de tela): anuncie o
|
|
19
|
+
* carregamento no contêiner, com `accessibilityState={{ busy: true }}`. Para ações pontuais, use
|
|
20
|
+
* Spinner. Não respeita "reduzir movimento" (a web faz via `motion-reduce`).
|
|
21
|
+
*/
|
|
22
|
+
function Skeleton({ className, variant, ...props }) {
|
|
23
|
+
const opacity = useRef(new Animated.Value(1)).current;
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const loop = Animated.loop(Animated.sequence([Animated.timing(opacity, {
|
|
26
|
+
toValue: .5,
|
|
27
|
+
duration: 1e3,
|
|
28
|
+
easing: Easing.inOut(Easing.ease),
|
|
29
|
+
useNativeDriver: true
|
|
30
|
+
}), Animated.timing(opacity, {
|
|
31
|
+
toValue: 1,
|
|
32
|
+
duration: 1e3,
|
|
33
|
+
easing: Easing.inOut(Easing.ease),
|
|
34
|
+
useNativeDriver: true
|
|
35
|
+
})]));
|
|
36
|
+
loop.start();
|
|
37
|
+
return () => loop.stop();
|
|
38
|
+
}, [opacity]);
|
|
39
|
+
return /* @__PURE__ */ jsx(View, {
|
|
40
|
+
accessibilityElementsHidden: true,
|
|
41
|
+
importantForAccessibility: "no-hide-descendants",
|
|
42
|
+
className: cn(skeletonVariants({ variant }), className),
|
|
43
|
+
...props,
|
|
44
|
+
children: /* @__PURE__ */ jsx(Animated.View, {
|
|
45
|
+
style: [StyleSheet.absoluteFill, { opacity }],
|
|
46
|
+
children: /* @__PURE__ */ jsx(View, { className: "size-full bg-bg-skeleton" })
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { Skeleton };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skeleton.js","names":[],"sources":["../../../src/components/skeleton/skeleton.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { useEffect, useRef } from 'react'\nimport { Animated, Easing, StyleSheet, View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\n\n// Dimensões padrão do Figma; sobrescreva com className (ex.: `w-full`) para casar com o conteúdo real.\nconst skeletonVariants = cva('overflow-hidden', {\n variants: {\n variant: {\n text: 'h-3 w-[200px] rounded-sm',\n title: 'h-5 w-[160px] rounded-sm',\n avatar: 'size-10 rounded-full',\n card: 'h-[120px] w-[300px] rounded-md',\n rectangle: 'h-6 w-[200px] rounded-md',\n },\n },\n defaultVariants: { variant: 'text' },\n})\n\nexport type SkeletonProps = Omit<ViewProps, 'children'> &\n VariantProps<typeof skeletonVariants> & {\n className?: string\n }\n\n/**\n * Placeholder de conteúdo em carregamento. É decorativo (escondido do leitor de tela): anuncie o\n * carregamento no contêiner, com `accessibilityState={{ busy: true }}`. Para ações pontuais, use\n * Spinner. Não respeita \"reduzir movimento\" (a web faz via `motion-reduce`).\n */\nexport function Skeleton({ className, variant, ...props }: SkeletonProps) {\n const opacity = useRef(new Animated.Value(1)).current\n\n useEffect(() => {\n const loop = Animated.loop(\n Animated.sequence([\n Animated.timing(opacity, {\n toValue: 0.5,\n duration: 1000,\n easing: Easing.inOut(Easing.ease),\n useNativeDriver: true,\n }),\n Animated.timing(opacity, {\n toValue: 1,\n duration: 1000,\n easing: Easing.inOut(Easing.ease),\n useNativeDriver: true,\n }),\n ]),\n )\n loop.start()\n return () => loop.stop()\n }, [opacity])\n\n return (\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n className={cn(skeletonVariants({ variant }), className)}\n {...props}\n >\n {/* O Animated não recebe className: só pulsa; o visual fica no View de dentro. */}\n <Animated.View style={[StyleSheet.absoluteFill, { opacity }]}>\n <View className=\"size-full bg-bg-skeleton\" />\n </Animated.View>\n </View>\n )\n}\n"],"mappings":";;;;;;AAQA,MAAM,mBAAmB,IAAI,mBAAmB;CAC9C,UAAU,EACR,SAAS;EACP,MAAM;EACN,OAAO;EACP,QAAQ;EACR,MAAM;EACN,WAAW;CACb,EACF;CACA,iBAAiB,EAAE,SAAS,OAAO;AACrC,CAAC;;;;;;AAYD,SAAgB,SAAS,EAAE,WAAW,SAAS,GAAG,SAAwB;CACxE,MAAM,UAAU,OAAO,IAAI,SAAS,MAAM,CAAC,CAAC,CAAC,CAAC;CAE9C,gBAAgB;EACd,MAAM,OAAO,SAAS,KACpB,SAAS,SAAS,CAChB,SAAS,OAAO,SAAS;GACvB,SAAS;GACT,UAAU;GACV,QAAQ,OAAO,MAAM,OAAO,IAAI;GAChC,iBAAiB;EACnB,CAAC,GACD,SAAS,OAAO,SAAS;GACvB,SAAS;GACT,UAAU;GACV,QAAQ,OAAO,MAAM,OAAO,IAAI;GAChC,iBAAiB;EACnB,CAAC,CACH,CAAC,CACH;EACA,KAAK,MAAM;EACX,aAAa,KAAK,KAAK;CACzB,GAAG,CAAC,OAAO,CAAC;CAEZ,OACE,oBAAC,MAAD;EACE,6BAAA;EACA,2BAA0B;EAC1B,WAAW,GAAG,iBAAiB,EAAE,QAAQ,CAAC,GAAG,SAAS;EACtD,GAAI;EAGJ,UAAA,oBAAC,SAAS,MAAV;GAAe,OAAO,CAAC,WAAW,cAAc,EAAE,QAAQ,CAAC;GACzD,UAAA,oBAAC,MAAD,EAAM,WAAU,2BAA4B,CAAA;EAC/B,CAAA;CACX,CAAA;AAEV"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ViewProps } from "react-native";
|
|
2
|
+
//#region src/components/slider/slider.d.ts
|
|
3
|
+
export type SliderProps = Omit<ViewProps, 'children' | 'style'> & {
|
|
4
|
+
className?: string;
|
|
5
|
+
/** Controlado: `[40]` para um valor, `[20, 80]` para um intervalo. */
|
|
6
|
+
value?: number[];
|
|
7
|
+
defaultValue?: number[];
|
|
8
|
+
min?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
step?: number;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Chamado durante o arraste, a cada mudança de valor. */
|
|
13
|
+
onValueChange?: (value: number[]) => void;
|
|
14
|
+
/** Chamado ao soltar a alça (ou após uma ação de acessibilidade). */
|
|
15
|
+
onValueCommit?: (value: number[]) => void;
|
|
16
|
+
/** Nome acessível de cada alça, na ordem. Padrão: `accessibilityLabel` (com o número, se houver várias). */
|
|
17
|
+
thumbLabels?: string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Escolhe um número (ou um intervalo, com dois valores) arrastando a alça ou tocando na barra.
|
|
21
|
+
* No native cada alça é um elemento "adjustable": o leitor de tela ajusta com deslizar para
|
|
22
|
+
* cima/baixo (ações increment/decrement) em vez das setas do teclado.
|
|
23
|
+
*/
|
|
24
|
+
export declare function Slider({ className, value, defaultValue, min, max, step, disabled, onValueChange, onValueCommit, thumbLabels, accessibilityLabel, accessibilityState, ...props }: SliderProps): import("react").JSX.Element;
|
|
25
|
+
//#endregion
|
|
26
|
+
//# sourceMappingURL=slider.d.ts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { useRef, useState } from "react";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/slider/slider.tsx
|
|
6
|
+
const THUMB = 20;
|
|
7
|
+
const clamp = (n, lo, hi) => Math.min(Math.max(n, lo), hi);
|
|
8
|
+
/**
|
|
9
|
+
* Escolhe um número (ou um intervalo, com dois valores) arrastando a alça ou tocando na barra.
|
|
10
|
+
* No native cada alça é um elemento "adjustable": o leitor de tela ajusta com deslizar para
|
|
11
|
+
* cima/baixo (ações increment/decrement) em vez das setas do teclado.
|
|
12
|
+
*/
|
|
13
|
+
function Slider({ className, value, defaultValue, min = 0, max = 100, step = 1, disabled = false, onValueChange, onValueCommit, thumbLabels, accessibilityLabel, accessibilityState, ...props }) {
|
|
14
|
+
const [uncontrolled, setUncontrolled] = useState(defaultValue ?? [min]);
|
|
15
|
+
const values = value ?? uncontrolled;
|
|
16
|
+
const [width, setWidth] = useState(0);
|
|
17
|
+
const active = useRef(0);
|
|
18
|
+
const latest = useRef(values);
|
|
19
|
+
latest.current = values;
|
|
20
|
+
const count = values.length;
|
|
21
|
+
const usable = Math.max(width - THUMB, 1);
|
|
22
|
+
const range = max - min || 1;
|
|
23
|
+
const toPos = (v) => (clamp(v, min, max) - min) / range * usable;
|
|
24
|
+
const snap = (raw) => {
|
|
25
|
+
const stepped = min + Math.round((raw - min) / step) * step;
|
|
26
|
+
return clamp(Number(stepped.toFixed(10)), min, max);
|
|
27
|
+
};
|
|
28
|
+
const update = (index, raw) => {
|
|
29
|
+
const current = latest.current;
|
|
30
|
+
const lo = (index > 0 ? current[index - 1] : void 0) ?? min;
|
|
31
|
+
const hi = (index < current.length - 1 ? current[index + 1] : void 0) ?? max;
|
|
32
|
+
const next = clamp(snap(raw), lo, hi);
|
|
33
|
+
if (next === current[index]) return;
|
|
34
|
+
const nextValues = current.map((v, i) => i === index ? next : v);
|
|
35
|
+
latest.current = nextValues;
|
|
36
|
+
setUncontrolled(nextValues);
|
|
37
|
+
onValueChange?.(nextValues);
|
|
38
|
+
};
|
|
39
|
+
const valueAt = (x) => min + clamp(x - THUMB / 2, 0, usable) / usable * range;
|
|
40
|
+
const onGrant = (event) => {
|
|
41
|
+
const raw = valueAt(event.nativeEvent.locationX);
|
|
42
|
+
const current = latest.current;
|
|
43
|
+
let nearest = 0;
|
|
44
|
+
current.forEach((v, i) => {
|
|
45
|
+
if (Math.abs(v - raw) <= Math.abs((current[nearest] ?? min) - raw)) nearest = i;
|
|
46
|
+
});
|
|
47
|
+
active.current = nearest;
|
|
48
|
+
update(nearest, raw);
|
|
49
|
+
};
|
|
50
|
+
const onAction = (index) => (event) => {
|
|
51
|
+
const dir = event.nativeEvent.actionName === "increment" ? 1 : -1;
|
|
52
|
+
update(index, (latest.current[index] ?? min) + dir * step);
|
|
53
|
+
onValueCommit?.(latest.current);
|
|
54
|
+
};
|
|
55
|
+
const labelFor = (i) => thumbLabels?.[i] ?? (count > 1 && accessibilityLabel ? `${accessibilityLabel} ${i + 1}` : accessibilityLabel);
|
|
56
|
+
const start = count > 1 ? toPos(values[0] ?? min) : 0;
|
|
57
|
+
const end = toPos(values[count - 1] ?? min);
|
|
58
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
59
|
+
onLayout: (e) => setWidth(e.nativeEvent.layout.width),
|
|
60
|
+
onStartShouldSetResponder: () => !disabled,
|
|
61
|
+
onMoveShouldSetResponder: () => !disabled,
|
|
62
|
+
onResponderTerminationRequest: () => false,
|
|
63
|
+
onResponderGrant: onGrant,
|
|
64
|
+
onResponderMove: (e) => update(active.current, valueAt(e.nativeEvent.locationX)),
|
|
65
|
+
onResponderRelease: () => onValueCommit?.(latest.current),
|
|
66
|
+
className: cn("h-11 w-full justify-center", className),
|
|
67
|
+
...props,
|
|
68
|
+
children: [/* @__PURE__ */ jsx(View, {
|
|
69
|
+
pointerEvents: "none",
|
|
70
|
+
className: "h-1.5 w-full rounded-full bg-border-default",
|
|
71
|
+
children: /* @__PURE__ */ jsx(View, {
|
|
72
|
+
className: cn("absolute h-full rounded-full bg-interactive-primary", disabled && "bg-interactive-disabled"),
|
|
73
|
+
style: {
|
|
74
|
+
left: start + THUMB / 2,
|
|
75
|
+
width: end - start
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}), values.map((v, i) => /* @__PURE__ */ jsx(View, {
|
|
79
|
+
pointerEvents: "none",
|
|
80
|
+
accessible: true,
|
|
81
|
+
accessibilityRole: "adjustable",
|
|
82
|
+
accessibilityLabel: labelFor(i),
|
|
83
|
+
accessibilityValue: {
|
|
84
|
+
min,
|
|
85
|
+
max,
|
|
86
|
+
now: v
|
|
87
|
+
},
|
|
88
|
+
accessibilityState: {
|
|
89
|
+
...accessibilityState,
|
|
90
|
+
disabled
|
|
91
|
+
},
|
|
92
|
+
accessibilityActions: [{ name: "increment" }, { name: "decrement" }],
|
|
93
|
+
onAccessibilityAction: disabled ? void 0 : onAction(i),
|
|
94
|
+
className: cn("absolute top-3 size-5 rounded-full border-2 border-interactive-primary bg-bg-primary shadow-sm", disabled && "border-interactive-disabled"),
|
|
95
|
+
style: { left: toPos(v) }
|
|
96
|
+
}, i))]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { Slider };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=slider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slider.js","names":[],"sources":["../../../src/components/slider/slider.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { useRef, useState } from 'react'\nimport {\n type AccessibilityActionEvent,\n type GestureResponderEvent,\n type LayoutChangeEvent,\n View,\n type ViewProps,\n} from 'react-native'\nimport { cn } from '../../utils/cn'\n\nexport type SliderProps = Omit<ViewProps, 'children' | 'style'> & {\n className?: string\n /** Controlado: `[40]` para um valor, `[20, 80]` para um intervalo. */\n value?: number[]\n defaultValue?: number[]\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n /** Chamado durante o arraste, a cada mudança de valor. */\n onValueChange?: (value: number[]) => void\n /** Chamado ao soltar a alça (ou após uma ação de acessibilidade). */\n onValueCommit?: (value: number[]) => void\n /** Nome acessível de cada alça, na ordem. Padrão: `accessibilityLabel` (com o número, se houver várias). */\n thumbLabels?: string[]\n}\n\nconst THUMB = 20\n\nconst clamp = (n: number, lo: number, hi: number) => Math.min(Math.max(n, lo), hi)\n\n/**\n * Escolhe um número (ou um intervalo, com dois valores) arrastando a alça ou tocando na barra.\n * No native cada alça é um elemento \"adjustable\": o leitor de tela ajusta com deslizar para\n * cima/baixo (ações increment/decrement) em vez das setas do teclado.\n */\nexport function Slider({\n className,\n value,\n defaultValue,\n min = 0,\n max = 100,\n step = 1,\n disabled = false,\n onValueChange,\n onValueCommit,\n thumbLabels,\n accessibilityLabel,\n accessibilityState,\n ...props\n}: SliderProps) {\n const [uncontrolled, setUncontrolled] = useState<number[]>(defaultValue ?? [min])\n const values = value ?? uncontrolled\n const [width, setWidth] = useState(0)\n const active = useRef(0)\n // O arraste lê o valor mais recente sem depender de closures antigas.\n const latest = useRef(values)\n latest.current = values\n\n const count = values.length\n const usable = Math.max(width - THUMB, 1)\n const range = max - min || 1\n const toPos = (v: number) => ((clamp(v, min, max) - min) / range) * usable\n\n const snap = (raw: number) => {\n const stepped = min + Math.round((raw - min) / step) * step\n // Evita lixo de ponto flutuante (0.1 + 0.2).\n return clamp(Number(stepped.toFixed(10)), min, max)\n }\n\n const update = (index: number, raw: number) => {\n const current = latest.current\n // A alça não passa das vizinhas.\n const lo = (index > 0 ? current[index - 1] : undefined) ?? min\n const hi = (index < current.length - 1 ? current[index + 1] : undefined) ?? max\n const next = clamp(snap(raw), lo, hi)\n if (next === current[index]) return\n const nextValues = current.map((v, i) => (i === index ? next : v))\n latest.current = nextValues\n setUncontrolled(nextValues)\n onValueChange?.(nextValues)\n }\n\n const valueAt = (x: number) => min + (clamp(x - THUMB / 2, 0, usable) / usable) * range\n\n const onGrant = (event: GestureResponderEvent) => {\n const raw = valueAt(event.nativeEvent.locationX)\n const current = latest.current\n // Alça mais próxima do toque; em empate, a de cima (permite abrir um intervalo colapsado).\n let nearest = 0\n current.forEach((v, i) => {\n if (Math.abs(v - raw) <= Math.abs((current[nearest] ?? min) - raw)) nearest = i\n })\n active.current = nearest\n update(nearest, raw)\n }\n\n const onAction = (index: number) => (event: AccessibilityActionEvent) => {\n const dir = event.nativeEvent.actionName === 'increment' ? 1 : -1\n update(index, (latest.current[index] ?? min) + dir * step)\n onValueCommit?.(latest.current)\n }\n\n const labelFor = (i: number) =>\n thumbLabels?.[i] ??\n (count > 1 && accessibilityLabel ? `${accessibilityLabel} ${i + 1}` : accessibilityLabel)\n\n const start = count > 1 ? toPos(values[0] ?? min) : 0\n const end = toPos(values[count - 1] ?? min)\n\n return (\n <View\n onLayout={(e: LayoutChangeEvent) => setWidth(e.nativeEvent.layout.width)}\n onStartShouldSetResponder={() => !disabled}\n onMoveShouldSetResponder={() => !disabled}\n onResponderTerminationRequest={() => false}\n onResponderGrant={onGrant}\n onResponderMove={(e) => update(active.current, valueAt(e.nativeEvent.locationX))}\n onResponderRelease={() => onValueCommit?.(latest.current)}\n className={cn('h-11 w-full justify-center', className)}\n {...props}\n >\n {/* Filhos ignoram toques para que `locationX` seja sempre relativo à raiz. */}\n <View pointerEvents=\"none\" className=\"h-1.5 w-full rounded-full bg-border-default\">\n <View\n className={cn(\n 'absolute h-full rounded-full bg-interactive-primary',\n disabled && 'bg-interactive-disabled',\n )}\n style={{ left: start + THUMB / 2, width: end - start }}\n />\n </View>\n {values.map((v, i) => (\n <View\n // biome-ignore lint/suspicious/noArrayIndexKey: as alças são posicionais e a ordem nunca muda\n key={i}\n pointerEvents=\"none\"\n accessible\n accessibilityRole=\"adjustable\"\n accessibilityLabel={labelFor(i)}\n accessibilityValue={{ min, max, now: v }}\n accessibilityState={{ ...accessibilityState, disabled }}\n accessibilityActions={[{ name: 'increment' }, { name: 'decrement' }]}\n onAccessibilityAction={disabled ? undefined : onAction(i)}\n className={cn(\n 'absolute top-3 size-5 rounded-full border-2 border-interactive-primary bg-bg-primary shadow-sm',\n disabled && 'border-interactive-disabled',\n )}\n style={{ left: toPos(v) }}\n />\n ))}\n </View>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ViewProps } from "react-native";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
|
+
//#region src/components/spinner/spinner.d.ts
|
|
4
|
+
declare const spinnerVariants: (props?: ({
|
|
5
|
+
size?: "lg" | "md" | "sm" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
export type SpinnerProps = Omit<ViewProps, 'children'> & VariantProps<typeof spinnerVariants> & {
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
/** Indicador indeterminado. Para carregamento de tela/seção, prefira um Skeleton. */
|
|
11
|
+
export declare function Spinner({ className, size, accessibilityLabel, ...props }: SpinnerProps): import("react").JSX.Element;
|
|
12
|
+
//#endregion
|
|
13
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { Animated, Easing, StyleSheet, View } from "react-native";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
//#region src/components/spinner/spinner.tsx
|
|
7
|
+
const spinnerVariants = cva("shrink-0", {
|
|
8
|
+
variants: { size: {
|
|
9
|
+
lg: "size-6",
|
|
10
|
+
md: "size-5",
|
|
11
|
+
sm: "size-4"
|
|
12
|
+
} },
|
|
13
|
+
defaultVariants: { size: "md" }
|
|
14
|
+
});
|
|
15
|
+
/** Indicador indeterminado. Para carregamento de tela/seção, prefira um Skeleton. */
|
|
16
|
+
function Spinner({ className, size, accessibilityLabel = "Carregando", ...props }) {
|
|
17
|
+
const rotation = useRef(new Animated.Value(0)).current;
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const loop = Animated.loop(Animated.timing(rotation, {
|
|
20
|
+
toValue: 1,
|
|
21
|
+
duration: 800,
|
|
22
|
+
easing: Easing.linear,
|
|
23
|
+
useNativeDriver: true
|
|
24
|
+
}));
|
|
25
|
+
loop.start();
|
|
26
|
+
return () => loop.stop();
|
|
27
|
+
}, [rotation]);
|
|
28
|
+
const rotate = rotation.interpolate({
|
|
29
|
+
inputRange: [0, 1],
|
|
30
|
+
outputRange: ["0deg", "360deg"]
|
|
31
|
+
});
|
|
32
|
+
return /* @__PURE__ */ jsxs(View, {
|
|
33
|
+
accessible: true,
|
|
34
|
+
accessibilityRole: "progressbar",
|
|
35
|
+
accessibilityLabel,
|
|
36
|
+
className: cn(spinnerVariants({ size }), className),
|
|
37
|
+
...props,
|
|
38
|
+
children: [/* @__PURE__ */ jsx(View, { className: "absolute inset-0 rounded-full border-2 border-spinner-track opacity-30" }), /* @__PURE__ */ jsx(Animated.View, {
|
|
39
|
+
style: [StyleSheet.absoluteFill, { transform: [{ rotate }] }],
|
|
40
|
+
children: /* @__PURE__ */ jsx(View, { className: "size-full rounded-full border-2 border-transparent border-t-spinner-indicator" })
|
|
41
|
+
})]
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { Spinner };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","names":[],"sources":["../../../src/components/spinner/spinner.tsx"],"sourcesContent":["/// <reference types=\"uniwind/types\" />\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { useEffect, useRef } from 'react'\nimport { Animated, Easing, StyleSheet, View, type ViewProps } from 'react-native'\nimport { cn } from '../../utils/cn'\n\nconst spinnerVariants = cva('shrink-0', {\n variants: {\n size: {\n lg: 'size-6', // 24px\n md: 'size-5', // 20px\n sm: 'size-4', // 16px\n },\n },\n defaultVariants: { size: 'md' },\n})\n\nexport type SpinnerProps = Omit<ViewProps, 'children'> &\n VariantProps<typeof spinnerVariants> & {\n className?: string\n }\n\n/** Indicador indeterminado. Para carregamento de tela/seção, prefira um Skeleton. */\nexport function Spinner({\n className,\n size,\n accessibilityLabel = 'Carregando',\n ...props\n}: SpinnerProps) {\n const rotation = useRef(new Animated.Value(0)).current\n\n useEffect(() => {\n const loop = Animated.loop(\n Animated.timing(rotation, {\n toValue: 1,\n duration: 800,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n )\n loop.start()\n return () => loop.stop()\n }, [rotation])\n\n const rotate = rotation.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'] })\n\n return (\n <View\n accessible\n accessibilityRole=\"progressbar\"\n accessibilityLabel={accessibilityLabel}\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n >\n <View className=\"absolute inset-0 rounded-full border-2 border-spinner-track opacity-30\" />\n {/* O Animated não recebe className: só gira; o visual fica no View de dentro. */}\n <Animated.View style={[StyleSheet.absoluteFill, { transform: [{ rotate }] }]}>\n <View className=\"size-full rounded-full border-2 border-transparent border-t-spinner-indicator\" />\n </Animated.View>\n </View>\n )\n}\n"],"mappings":";;;;;;AAOA,MAAM,kBAAkB,IAAI,YAAY;CACtC,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;;AAQD,SAAgB,QAAQ,EACtB,WACA,MACA,qBAAqB,cACrB,GAAG,SACY;CACf,MAAM,WAAW,OAAO,IAAI,SAAS,MAAM,CAAC,CAAC,CAAC,CAAC;CAE/C,gBAAgB;EACd,MAAM,OAAO,SAAS,KACpB,SAAS,OAAO,UAAU;GACxB,SAAS;GACT,UAAU;GACV,QAAQ,OAAO;GACf,iBAAiB;EACnB,CAAC,CACH;EACA,KAAK,MAAM;EACX,aAAa,KAAK,KAAK;CACzB,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,SAAS,SAAS,YAAY;EAAE,YAAY,CAAC,GAAG,CAAC;EAAG,aAAa,CAAC,QAAQ,QAAQ;CAAE,CAAC;CAE3F,OACE,qBAAC,MAAD;EACE,YAAA;EACA,mBAAkB;EACE;EACpB,WAAW,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,SAAS;EAClD,GAAI;EALN,UAAA,CAOE,oBAAC,MAAD,EAAM,WAAU,yEAA0E,CAAA,GAE1F,oBAAC,SAAS,MAAV;GAAe,OAAO,CAAC,WAAW,cAAc,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;GACzE,UAAA,oBAAC,MAAD,EAAM,WAAU,gFAAiF,CAAA;EACpF,CAAA,CACX;;AAEV"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ViewProps } from "react-native";
|
|
2
|
+
import { VariantProps } from "class-variance-authority";
|
|
3
|
+
//#region src/components/stack/stack.d.ts
|
|
4
|
+
declare const stackVariants: (props?: ({
|
|
5
|
+
direction?: "column" | "row" | null | undefined;
|
|
6
|
+
gap?: 0 | 1 | 2 | 3 | 4 | 6 | 8 | null | undefined;
|
|
7
|
+
align?: "center" | "end" | "start" | "stretch" | null | undefined;
|
|
8
|
+
justify?: "between" | "center" | "end" | "start" | null | undefined;
|
|
9
|
+
wrap?: boolean | null | undefined;
|
|
10
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
|
+
export type StackProps = ViewProps & VariantProps<typeof stackVariants> & {
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
/** Empilha filhos numa direção com espaçamento uniforme (flexbox + a escala de gap do DS). */
|
|
15
|
+
export declare function Stack({ className, direction, gap, align, justify, wrap, ...props }: StackProps): import("react").JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
//# sourceMappingURL=stack.d.ts.map
|