panelui-native 0.30.0 → 0.34.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/README.md +4 -2
- package/lib/module/components/button/index.js +106 -11
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/calendar/index.js +107 -24
- package/lib/module/components/calendar/index.js.map +1 -1
- package/lib/module/components/drawer/index.js +477 -0
- package/lib/module/components/drawer/index.js.map +1 -0
- package/lib/module/components/heatmap-chart/index.js +9 -8
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/map/index.js +50 -4
- package/lib/module/components/map/index.js.map +1 -1
- package/lib/module/components/panelside/index.js +1024 -0
- package/lib/module/components/panelside/index.js.map +1 -0
- package/lib/module/components/plan/index.js +307 -21
- package/lib/module/components/plan/index.js.map +1 -1
- package/lib/module/components/post/index.js +992 -0
- package/lib/module/components/post/index.js.map +1 -0
- package/lib/module/components/response/index.js +296 -0
- package/lib/module/components/response/index.js.map +1 -0
- package/lib/module/components/response/markdown.js +463 -0
- package/lib/module/components/response/markdown.js.map +1 -0
- package/lib/module/components/select/index.js +111 -18
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/table/index.js +632 -0
- package/lib/module/components/table/index.js.map +1 -0
- package/lib/module/icons/index.js +220 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +6 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/index.js +30 -0
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/utils/date.js +129 -22
- package/lib/module/utils/date.js.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts +22 -1
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/calendar/index.d.ts +12 -3
- package/lib/typescript/src/components/calendar/index.d.ts.map +1 -1
- package/lib/typescript/src/components/drawer/index.d.ts +146 -0
- package/lib/typescript/src/components/drawer/index.d.ts.map +1 -0
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/map/index.d.ts +8 -1
- package/lib/typescript/src/components/map/index.d.ts.map +1 -1
- package/lib/typescript/src/components/panelside/index.d.ts +354 -0
- package/lib/typescript/src/components/panelside/index.d.ts.map +1 -0
- package/lib/typescript/src/components/plan/index.d.ts +121 -10
- package/lib/typescript/src/components/plan/index.d.ts.map +1 -1
- package/lib/typescript/src/components/post/index.d.ts +358 -0
- package/lib/typescript/src/components/post/index.d.ts.map +1 -0
- package/lib/typescript/src/components/response/index.d.ts +91 -0
- package/lib/typescript/src/components/response/index.d.ts.map +1 -0
- package/lib/typescript/src/components/response/markdown.d.ts +88 -0
- package/lib/typescript/src/components/response/markdown.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts +27 -2
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/table/index.d.ts +274 -0
- package/lib/typescript/src/components/table/index.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +41 -1
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +7 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +20 -0
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/date.d.ts +49 -5
- package/lib/typescript/src/utils/date.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/button/index.tsx +140 -8
- package/src/components/calendar/index.tsx +136 -23
- package/src/components/drawer/index.tsx +662 -0
- package/src/components/heatmap-chart/index.tsx +9 -9
- package/src/components/map/index.tsx +53 -4
- package/src/components/panelside/index.tsx +1358 -0
- package/src/components/plan/index.tsx +344 -21
- package/src/components/post/index.tsx +1116 -0
- package/src/components/response/index.tsx +390 -0
- package/src/components/response/markdown.ts +489 -0
- package/src/components/select/index.tsx +155 -15
- package/src/components/table/index.tsx +747 -0
- package/src/icons/index.tsx +176 -1
- package/src/index.ts +93 -0
- package/src/native/index.ts +51 -0
- package/src/utils/date.ts +146 -22
package/src/icons/index.tsx
CHANGED
|
@@ -133,12 +133,18 @@ export function CalendarIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
export function BookmarkIcon({
|
|
136
|
+
export function BookmarkIcon({
|
|
137
|
+
size = 16,
|
|
138
|
+
color,
|
|
139
|
+
filled,
|
|
140
|
+
...props
|
|
141
|
+
}: IconProps & { filled?: boolean }) {
|
|
137
142
|
const resolved = useResolvedColor(color, '#737373');
|
|
138
143
|
return (
|
|
139
144
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
140
145
|
<Path
|
|
141
146
|
d="m19 21-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2Z"
|
|
147
|
+
fill={filled ? resolved : 'none'}
|
|
142
148
|
stroke={resolved}
|
|
143
149
|
strokeWidth={2}
|
|
144
150
|
strokeLinecap="round"
|
|
@@ -225,6 +231,20 @@ export function EllipsisIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
225
231
|
);
|
|
226
232
|
}
|
|
227
233
|
|
|
234
|
+
export function MenuIcon({ size = 20, color, ...props }: IconProps) {
|
|
235
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
236
|
+
return (
|
|
237
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
238
|
+
<Path
|
|
239
|
+
d="M4 6h16M4 12h16M4 18h16"
|
|
240
|
+
stroke={resolved}
|
|
241
|
+
strokeWidth={2}
|
|
242
|
+
strokeLinecap="round"
|
|
243
|
+
/>
|
|
244
|
+
</Svg>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
228
248
|
export function XIcon({ size = 16, color, ...props }: IconProps) {
|
|
229
249
|
const resolved = useResolvedColor(color, '#737373');
|
|
230
250
|
return (
|
|
@@ -974,3 +994,158 @@ export function CircleIcon({ size = 16, color, ...props }: IconProps) {
|
|
|
974
994
|
</Svg>
|
|
975
995
|
);
|
|
976
996
|
}
|
|
997
|
+
|
|
998
|
+
/* -------------------------------------------------------------------------- */
|
|
999
|
+
/* Social */
|
|
1000
|
+
/* -------------------------------------------------------------------------- */
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* The icons a post's action row is made of.
|
|
1004
|
+
*
|
|
1005
|
+
* These are the only ones in the set that take `filled`, and it is not a
|
|
1006
|
+
* stylistic choice: a like, a save and a vote are toggles, and the outline and
|
|
1007
|
+
* the solid are the two states of one control. Drawn as two different icons
|
|
1008
|
+
* they would swap shape under the finger; drawn as one that fills, the shape
|
|
1009
|
+
* stays put and only the inside changes.
|
|
1010
|
+
*/
|
|
1011
|
+
export interface ToggleIconProps extends IconProps {
|
|
1012
|
+
/** Solid rather than outlined — the on state of a like, a save, a vote. */
|
|
1013
|
+
filled?: boolean;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/** Upvote. Filled once the vote is cast. */
|
|
1017
|
+
export function ArrowUpIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1018
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1019
|
+
return (
|
|
1020
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1021
|
+
<Path
|
|
1022
|
+
d="M9 18v-6H5l7-7 7 7h-4v6z"
|
|
1023
|
+
fill={filled ? resolved : 'none'}
|
|
1024
|
+
stroke={resolved}
|
|
1025
|
+
strokeWidth={2}
|
|
1026
|
+
strokeLinecap="round"
|
|
1027
|
+
strokeLinejoin="round"
|
|
1028
|
+
/>
|
|
1029
|
+
</Svg>
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/** Downvote. The same arrow, turned over. */
|
|
1034
|
+
export function ArrowDownIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1035
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1036
|
+
return (
|
|
1037
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1038
|
+
<Path
|
|
1039
|
+
d="M9 6v6H5l7 7 7-7h-4V6z"
|
|
1040
|
+
fill={filled ? resolved : 'none'}
|
|
1041
|
+
stroke={resolved}
|
|
1042
|
+
strokeWidth={2}
|
|
1043
|
+
strokeLinecap="round"
|
|
1044
|
+
strokeLinejoin="round"
|
|
1045
|
+
/>
|
|
1046
|
+
</Svg>
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/** A like. */
|
|
1051
|
+
export function HeartIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1052
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1053
|
+
return (
|
|
1054
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1055
|
+
<Path
|
|
1056
|
+
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
|
|
1057
|
+
fill={filled ? resolved : 'none'}
|
|
1058
|
+
stroke={resolved}
|
|
1059
|
+
strokeWidth={2}
|
|
1060
|
+
strokeLinecap="round"
|
|
1061
|
+
strokeLinejoin="round"
|
|
1062
|
+
/>
|
|
1063
|
+
</Svg>
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/** A reply, or the count of them. */
|
|
1068
|
+
export function MessageCircleIcon({ size = 16, color, filled, ...props }: ToggleIconProps) {
|
|
1069
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1070
|
+
return (
|
|
1071
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1072
|
+
<Path
|
|
1073
|
+
d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"
|
|
1074
|
+
fill={filled ? resolved : 'none'}
|
|
1075
|
+
stroke={resolved}
|
|
1076
|
+
strokeWidth={2}
|
|
1077
|
+
strokeLinecap="round"
|
|
1078
|
+
strokeLinejoin="round"
|
|
1079
|
+
/>
|
|
1080
|
+
</Svg>
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/** Views — how many times a thing has been seen rather than acted on. */
|
|
1085
|
+
export function EyeIcon({ size = 16, color, ...props }: IconProps) {
|
|
1086
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1087
|
+
return (
|
|
1088
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1089
|
+
<Path
|
|
1090
|
+
d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0"
|
|
1091
|
+
stroke={resolved}
|
|
1092
|
+
strokeWidth={2}
|
|
1093
|
+
strokeLinecap="round"
|
|
1094
|
+
strokeLinejoin="round"
|
|
1095
|
+
/>
|
|
1096
|
+
<Circle cx={12} cy={12} r={3} stroke={resolved} strokeWidth={2} />
|
|
1097
|
+
</Svg>
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/** A repost — the same thing sent round again. */
|
|
1102
|
+
export function RepeatIcon({ size = 16, color, ...props }: IconProps) {
|
|
1103
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1104
|
+
return (
|
|
1105
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1106
|
+
<Path
|
|
1107
|
+
d="m2 9 3-3 3 3M5 6v10a2 2 0 0 0 2 2h6M22 15l-3 3-3-3M19 18V8a2 2 0 0 0-2-2h-6"
|
|
1108
|
+
stroke={resolved}
|
|
1109
|
+
strokeWidth={2}
|
|
1110
|
+
strokeLinecap="round"
|
|
1111
|
+
strokeLinejoin="round"
|
|
1112
|
+
/>
|
|
1113
|
+
</Svg>
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
export interface BadgeCheckIconProps extends IconProps {
|
|
1118
|
+
/** The tick's colour when the rosette is solid. Defaults to white. */
|
|
1119
|
+
checkColor?: string;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* The verified rosette.
|
|
1124
|
+
*
|
|
1125
|
+
* Solid by default, unlike everything else here. A verification mark is a
|
|
1126
|
+
* claim about the account rather than a control, and an outlined one next to
|
|
1127
|
+
* an outlined like button reads as another thing you could press.
|
|
1128
|
+
*/
|
|
1129
|
+
export function BadgeCheckIcon({
|
|
1130
|
+
size = 16,
|
|
1131
|
+
color,
|
|
1132
|
+
checkColor = '#ffffff',
|
|
1133
|
+
...props
|
|
1134
|
+
}: BadgeCheckIconProps) {
|
|
1135
|
+
const resolved = useResolvedColor(color, '#737373');
|
|
1136
|
+
return (
|
|
1137
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
1138
|
+
<Path
|
|
1139
|
+
d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"
|
|
1140
|
+
fill={resolved}
|
|
1141
|
+
/>
|
|
1142
|
+
<Path
|
|
1143
|
+
d="m9 12 2 2 4-4"
|
|
1144
|
+
stroke={checkColor}
|
|
1145
|
+
strokeWidth={2.5}
|
|
1146
|
+
strokeLinecap="round"
|
|
1147
|
+
strokeLinejoin="round"
|
|
1148
|
+
/>
|
|
1149
|
+
</Svg>
|
|
1150
|
+
);
|
|
1151
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -254,6 +254,20 @@ export {
|
|
|
254
254
|
type TabsTriggerProps,
|
|
255
255
|
type TabsContentProps,
|
|
256
256
|
} from './components/tabs';
|
|
257
|
+
export {
|
|
258
|
+
Table,
|
|
259
|
+
type TableProps,
|
|
260
|
+
type TableFrameProps,
|
|
261
|
+
type TableHeaderProps,
|
|
262
|
+
type TableBodyProps,
|
|
263
|
+
type TableFooterProps,
|
|
264
|
+
type TableRowProps,
|
|
265
|
+
type TableHeadProps,
|
|
266
|
+
type TableCellProps,
|
|
267
|
+
type TableCaptionProps,
|
|
268
|
+
type TableEmptyProps,
|
|
269
|
+
type TableSortDirection,
|
|
270
|
+
} from './components/table';
|
|
257
271
|
export {
|
|
258
272
|
Carousel,
|
|
259
273
|
useCarouselState,
|
|
@@ -290,6 +304,18 @@ export {
|
|
|
290
304
|
type ChipVariant,
|
|
291
305
|
type ChipSize,
|
|
292
306
|
} from './components/chip';
|
|
307
|
+
export {
|
|
308
|
+
Drawer,
|
|
309
|
+
type DrawerProps,
|
|
310
|
+
type DrawerTriggerProps,
|
|
311
|
+
type DrawerContentProps,
|
|
312
|
+
type DrawerHeaderProps,
|
|
313
|
+
type DrawerBodyProps,
|
|
314
|
+
type DrawerFooterProps,
|
|
315
|
+
type DrawerCloseProps,
|
|
316
|
+
type DrawerSide,
|
|
317
|
+
type DrawerSize,
|
|
318
|
+
} from './components/drawer';
|
|
293
319
|
export {
|
|
294
320
|
EmptyState,
|
|
295
321
|
type EmptyStateProps,
|
|
@@ -335,6 +361,26 @@ export {
|
|
|
335
361
|
type NumberInputProps,
|
|
336
362
|
} from './components/number-input';
|
|
337
363
|
export { OtpInput, type OtpInputProps } from './components/otp-input';
|
|
364
|
+
export {
|
|
365
|
+
Panelside,
|
|
366
|
+
usePanelside,
|
|
367
|
+
type PanelsideProps,
|
|
368
|
+
type PanelsidePanelProps,
|
|
369
|
+
type PanelsideHeaderProps,
|
|
370
|
+
type PanelsideSearchProps,
|
|
371
|
+
type PanelsideContentProps,
|
|
372
|
+
type PanelsideGroupProps,
|
|
373
|
+
type PanelsideGroupLabelProps,
|
|
374
|
+
type PanelsideItemProps,
|
|
375
|
+
type PanelsideActionProps,
|
|
376
|
+
type PanelsideFooterProps,
|
|
377
|
+
type PanelsideCtaProps,
|
|
378
|
+
type PanelsideSceneProps,
|
|
379
|
+
type PanelsideTriggerProps,
|
|
380
|
+
type PanelsideMode,
|
|
381
|
+
type PanelsideSwipeFrom,
|
|
382
|
+
type UsePanelsideResult,
|
|
383
|
+
} from './components/panelside';
|
|
338
384
|
export { Textarea, type TextareaProps } from './components/textarea';
|
|
339
385
|
export {
|
|
340
386
|
Item,
|
|
@@ -431,6 +477,21 @@ export {
|
|
|
431
477
|
type PopoverAlign,
|
|
432
478
|
type PopoverPresentation,
|
|
433
479
|
} from './components/popover';
|
|
480
|
+
export {
|
|
481
|
+
Post,
|
|
482
|
+
type PostProps,
|
|
483
|
+
type PostHeaderProps,
|
|
484
|
+
type PostAuthorProps,
|
|
485
|
+
type PostActionProps,
|
|
486
|
+
type PostCommunityProps,
|
|
487
|
+
type PostTitleProps,
|
|
488
|
+
type PostBodyProps,
|
|
489
|
+
type PostMediaProps,
|
|
490
|
+
type PostFooterProps,
|
|
491
|
+
type PostStatProps,
|
|
492
|
+
type PostVotesProps,
|
|
493
|
+
type PostVote,
|
|
494
|
+
} from './components/post';
|
|
434
495
|
export { Progress, type ProgressProps } from './components/progress';
|
|
435
496
|
export {
|
|
436
497
|
RadioGroup,
|
|
@@ -476,6 +537,13 @@ export {
|
|
|
476
537
|
type ReasoningTriggerProps,
|
|
477
538
|
type ReasoningContentProps,
|
|
478
539
|
} from './components/reasoning';
|
|
540
|
+
export {
|
|
541
|
+
Response,
|
|
542
|
+
type ResponseProps,
|
|
543
|
+
type ResponseComponents,
|
|
544
|
+
type ResponseBlock,
|
|
545
|
+
type ResponseInline,
|
|
546
|
+
} from './components/response';
|
|
479
547
|
export {
|
|
480
548
|
Sources,
|
|
481
549
|
type SourcesProps,
|
|
@@ -509,11 +577,17 @@ export {
|
|
|
509
577
|
Plan,
|
|
510
578
|
type PlanProps,
|
|
511
579
|
type PlanHeaderProps,
|
|
580
|
+
type PlanIconProps,
|
|
512
581
|
type PlanTitleProps,
|
|
513
582
|
type PlanDescriptionProps,
|
|
514
583
|
type PlanActionProps,
|
|
584
|
+
type PlanProgressProps,
|
|
515
585
|
type PlanTriggerProps,
|
|
516
586
|
type PlanContentProps,
|
|
587
|
+
type PlanStepsProps,
|
|
588
|
+
type PlanStepProps,
|
|
589
|
+
type PlanStepStatus,
|
|
590
|
+
type PlanStepCounts,
|
|
517
591
|
type PlanFooterProps,
|
|
518
592
|
} from './components/plan';
|
|
519
593
|
export {
|
|
@@ -596,7 +670,10 @@ export {
|
|
|
596
670
|
export {
|
|
597
671
|
AlertTriangleIcon,
|
|
598
672
|
AppleIcon,
|
|
673
|
+
ArrowDownIcon,
|
|
674
|
+
ArrowUpIcon,
|
|
599
675
|
ArrowUpRightIcon,
|
|
676
|
+
BadgeCheckIcon,
|
|
600
677
|
BellIcon,
|
|
601
678
|
BookmarkIcon,
|
|
602
679
|
CalendarIcon,
|
|
@@ -607,16 +684,26 @@ export {
|
|
|
607
684
|
ChevronLeftIcon,
|
|
608
685
|
ChevronRightIcon,
|
|
609
686
|
ChevronUpIcon,
|
|
687
|
+
CircleIcon,
|
|
688
|
+
CompassIcon,
|
|
689
|
+
CopyIcon,
|
|
690
|
+
CrosshairIcon,
|
|
610
691
|
DownloadIcon,
|
|
611
692
|
EllipsisIcon,
|
|
693
|
+
EyeIcon,
|
|
612
694
|
FacebookIcon,
|
|
613
695
|
FileIcon,
|
|
614
696
|
GoogleIcon,
|
|
697
|
+
HeartIcon,
|
|
615
698
|
IconColorProvider,
|
|
616
699
|
InfoIcon,
|
|
617
700
|
ImageIcon,
|
|
701
|
+
LinkIcon,
|
|
702
|
+
ListChecksIcon,
|
|
618
703
|
LockIcon,
|
|
619
704
|
MaximizeIcon,
|
|
705
|
+
MenuIcon,
|
|
706
|
+
MessageCircleIcon,
|
|
620
707
|
MicIcon,
|
|
621
708
|
MinusIcon,
|
|
622
709
|
MoonIcon,
|
|
@@ -628,18 +715,24 @@ export {
|
|
|
628
715
|
PlusIcon,
|
|
629
716
|
PlusSquareIcon,
|
|
630
717
|
ReceiptIcon,
|
|
718
|
+
RepeatIcon,
|
|
631
719
|
RotateCcwIcon,
|
|
720
|
+
RotateCwIcon,
|
|
632
721
|
SearchIcon,
|
|
633
722
|
SendIcon,
|
|
634
723
|
ShareNodesIcon,
|
|
635
724
|
ShieldAlertIcon,
|
|
636
725
|
ShieldCheckIcon,
|
|
726
|
+
SparklesIcon,
|
|
727
|
+
StarIcon,
|
|
637
728
|
SunIcon,
|
|
638
729
|
TrashIcon,
|
|
639
730
|
UnlockIcon,
|
|
640
731
|
XIcon,
|
|
641
732
|
useIconColor,
|
|
642
733
|
type IconProps,
|
|
734
|
+
type ToggleIconProps,
|
|
735
|
+
type BadgeCheckIconProps,
|
|
643
736
|
} from './icons';
|
|
644
737
|
|
|
645
738
|
// Hooks
|
package/src/native/index.ts
CHANGED
|
@@ -97,3 +97,54 @@ export function getNativeUI(): NativeUIModule | null {
|
|
|
97
97
|
export function hasNativeUI(): boolean {
|
|
98
98
|
return getNativeUI() !== null;
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The SwiftUI view modifiers, for the handful of things the portable props
|
|
103
|
+
* cannot express.
|
|
104
|
+
*
|
|
105
|
+
* The universal components cover the common ground — a variant, a size, an
|
|
106
|
+
* enabled flag — but the platform has looks with no cross-platform equivalent,
|
|
107
|
+
* and Liquid Glass is the one that matters: it is the material iOS 26 draws its
|
|
108
|
+
* own floating controls in, and nothing in the portable API asks for it. A
|
|
109
|
+
* modifier is how SwiftUI is configured, so this is the door to it.
|
|
110
|
+
*
|
|
111
|
+
* iOS only, and lazily resolved like everything else here. Android's toolkit
|
|
112
|
+
* has its own modifier system and no equivalent material, so asking for glass
|
|
113
|
+
* there is not a downgrade — it is a different question.
|
|
114
|
+
*/
|
|
115
|
+
interface SwiftUIModifiers {
|
|
116
|
+
buttonStyle: (
|
|
117
|
+
style:
|
|
118
|
+
| 'automatic'
|
|
119
|
+
| 'bordered'
|
|
120
|
+
| 'borderedProminent'
|
|
121
|
+
| 'borderless'
|
|
122
|
+
| 'glass'
|
|
123
|
+
| 'glassProminent'
|
|
124
|
+
| 'plain'
|
|
125
|
+
) => unknown;
|
|
126
|
+
buttonBorderShape: (
|
|
127
|
+
shape: 'automatic' | 'capsule' | 'roundedRectangle' | 'circle',
|
|
128
|
+
cornerRadius?: number
|
|
129
|
+
) => unknown;
|
|
130
|
+
controlSize: (size: 'mini' | 'small' | 'regular' | 'large' | 'extraLarge') => unknown;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let modifiersResolved = false;
|
|
134
|
+
let modifiers: SwiftUIModifiers | null = null;
|
|
135
|
+
|
|
136
|
+
export function getSwiftUIModifiers(): SwiftUIModifiers | null {
|
|
137
|
+
if (modifiersResolved) return modifiers;
|
|
138
|
+
modifiersResolved = true;
|
|
139
|
+
|
|
140
|
+
if (Platform.OS !== 'ios') return null;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
144
|
+
modifiers = require('@expo/ui/swift-ui/modifiers') as SwiftUIModifiers;
|
|
145
|
+
} catch {
|
|
146
|
+
modifiers = null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return modifiers;
|
|
150
|
+
}
|
package/src/utils/date.ts
CHANGED
|
@@ -12,6 +12,18 @@
|
|
|
12
12
|
* comparison here goes through `startOfDay`.
|
|
13
13
|
* - **Month arithmetic is done with `setMonth`, not by adding days.** The
|
|
14
14
|
* platform already knows that a month is 28, 29, 30 or 31 days long.
|
|
15
|
+
*
|
|
16
|
+
* Three more that are easy to lose and expensive to get back:
|
|
17
|
+
*
|
|
18
|
+
* - **Days are stepped with `setDate`, never with milliseconds.** Adding
|
|
19
|
+
* `n * 86400000` is wrong by an hour on the two days a year the clocks move,
|
|
20
|
+
* which is enough to land the wrong side of midnight and shift a whole grid.
|
|
21
|
+
* - **A month is normalised to the 1st before months are added.** `setMonth`
|
|
22
|
+
* on the 31st of a month whose neighbour is shorter overflows into the month
|
|
23
|
+
* after next — January 31st plus one month is March 3rd.
|
|
24
|
+
* - **Nothing is ever parsed back out of formatted text.** `Intl` is asked for
|
|
25
|
+
* the *parts* and the numbers are read from those, because the order and the
|
|
26
|
+
* separators belong to the locale and only the numbers are ours.
|
|
15
27
|
*/
|
|
16
28
|
|
|
17
29
|
/** Names of the weekdays and months, in the caller's locale. */
|
|
@@ -60,12 +72,6 @@ export function addDays(date: Date, days: number): Date {
|
|
|
60
72
|
return copy;
|
|
61
73
|
}
|
|
62
74
|
|
|
63
|
-
/** Whole days from `a` to `b`, ignoring the time of day on both. */
|
|
64
|
-
export function daysBetween(a: Date, b: Date): number {
|
|
65
|
-
const MS = 24 * 60 * 60 * 1000;
|
|
66
|
-
return Math.round((startOfDay(b).getTime() - startOfDay(a).getTime()) / MS);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
75
|
export function isBefore(a: Date, b: Date): boolean {
|
|
70
76
|
return startOfDay(a).getTime() < startOfDay(b).getTime();
|
|
71
77
|
}
|
|
@@ -82,12 +88,59 @@ export function isWithin(date: Date, from: Date, to: Date): boolean {
|
|
|
82
88
|
return time >= Math.min(start, end) && time <= Math.max(start, end);
|
|
83
89
|
}
|
|
84
90
|
|
|
91
|
+
/**
|
|
92
|
+
* `date` pulled inside `min`..`max`, at day resolution.
|
|
93
|
+
*
|
|
94
|
+
* The bound is returned at its own start of day rather than as given, so a
|
|
95
|
+
* `maxDate` that happens to carry a time of 17:30 does not hand back a value
|
|
96
|
+
* that then compares as *after* the last selectable day.
|
|
97
|
+
*/
|
|
85
98
|
export function clampDate(date: Date, min?: Date, max?: Date): Date {
|
|
86
|
-
if (min && isBefore(date, min)) return min;
|
|
87
|
-
if (max && isAfter(date, max)) return max;
|
|
99
|
+
if (min && isBefore(date, min)) return startOfDay(min);
|
|
100
|
+
if (max && isAfter(date, max)) return startOfDay(max);
|
|
88
101
|
return date;
|
|
89
102
|
}
|
|
90
103
|
|
|
104
|
+
/**
|
|
105
|
+
* A first-day-of-week index made safe.
|
|
106
|
+
*
|
|
107
|
+
* `weekStartsOn` arrives from a caller and reaches an array index by way of
|
|
108
|
+
* `(day + weekStartsOn) % 7`, where a negative or out-of-range value yields a
|
|
109
|
+
* negative index and an `undefined` weekday name. Both directions wrap.
|
|
110
|
+
*/
|
|
111
|
+
export function normalizeWeekStart(weekStartsOn: number): number {
|
|
112
|
+
if (!Number.isFinite(weekStartsOn)) return 0;
|
|
113
|
+
return ((Math.trunc(weekStartsOn) % 7) + 7) % 7;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The day the week starts on where this locale is spoken, as `Date.getDay()`
|
|
118
|
+
* counts it — 0 for Sunday.
|
|
119
|
+
*
|
|
120
|
+
* Sunday is a poor default outside North America and a handful of other
|
|
121
|
+
* regions, but it is the one the platform gives us when it knows nothing. Where
|
|
122
|
+
* `Intl` carries week data, ask it: `en-GB` and `fr-FR` start on Monday, and a
|
|
123
|
+
* calendar that says otherwise is wrong in a way its reader notices immediately.
|
|
124
|
+
*/
|
|
125
|
+
export function localeWeekStart(locale: DateLocale): number {
|
|
126
|
+
try {
|
|
127
|
+
const tag = (locale ?? 'en').split('-u-')[0] || 'en';
|
|
128
|
+
const resolved = new Intl.Locale(tag) as Intl.Locale & {
|
|
129
|
+
getWeekInfo?: () => { firstDay?: number };
|
|
130
|
+
weekInfo?: { firstDay?: number };
|
|
131
|
+
};
|
|
132
|
+
// Proposed as a method and shipped as a property first; both are in the
|
|
133
|
+
// wild, and neither is guaranteed to be there at all.
|
|
134
|
+
const info = resolved.getWeekInfo?.() ?? resolved.weekInfo;
|
|
135
|
+
const first = info?.firstDay;
|
|
136
|
+
// `Intl` counts 1 = Monday … 7 = Sunday; `Date.getDay()` counts 0 = Sunday.
|
|
137
|
+
if (typeof first === 'number' && Number.isFinite(first)) return first % 7;
|
|
138
|
+
} catch {
|
|
139
|
+
// No week data in this build.
|
|
140
|
+
}
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
91
144
|
/**
|
|
92
145
|
* The six-week grid for a month.
|
|
93
146
|
*
|
|
@@ -98,12 +151,20 @@ export function clampDate(date: Date, min?: Date, max?: Date): Date {
|
|
|
98
151
|
*
|
|
99
152
|
* The leading and trailing cells belong to the neighbouring months; whether
|
|
100
153
|
* they are drawn or left blank is the caller's decision.
|
|
154
|
+
*
|
|
155
|
+
* `system` and `locale` are what make this work for a Hijri grid, where the
|
|
156
|
+
* month it has to open on is not the Gregorian 1st.
|
|
101
157
|
*/
|
|
102
|
-
export function monthGrid(
|
|
103
|
-
|
|
158
|
+
export function monthGrid(
|
|
159
|
+
month: Date,
|
|
160
|
+
weekStartsOn: number,
|
|
161
|
+
system: 'gregory' | 'islamic' = 'gregory',
|
|
162
|
+
locale?: DateLocale
|
|
163
|
+
): Date[][] {
|
|
164
|
+
const first = startOfCalendarMonth(month, system, locale);
|
|
104
165
|
// How far back to the most recent `weekStartsOn`. The `+ 7` keeps the
|
|
105
166
|
// modulo positive when the week starts later than the first of the month.
|
|
106
|
-
const lead = (first.getDay() - weekStartsOn + 7) % 7;
|
|
167
|
+
const lead = (first.getDay() - normalizeWeekStart(weekStartsOn) + 7) % 7;
|
|
107
168
|
const start = addDays(first, -lead);
|
|
108
169
|
|
|
109
170
|
return Array.from({ length: 6 }, (_unusedWeek, week) =>
|
|
@@ -119,10 +180,49 @@ export function monthGrid(month: Date, weekStartsOn: number): Date[][] {
|
|
|
119
180
|
* locales they do not carry. Every formatter here falls back to English rather
|
|
120
181
|
* than taking the screen down over a month name.
|
|
121
182
|
*/
|
|
183
|
+
/**
|
|
184
|
+
* Formatters, kept.
|
|
185
|
+
*
|
|
186
|
+
* Building an `Intl.DateTimeFormat` is the expensive part of formatting, and a
|
|
187
|
+
* Hijri grid asks for one per cell twice over — once to decide which month the
|
|
188
|
+
* cell belongs to and once for the number in it — which is 84 constructions a
|
|
189
|
+
* render before anyone touches the year dropdown, where a century jump used to
|
|
190
|
+
* cost tens of thousands. The set of shapes asked for is tiny and fixed, so it
|
|
191
|
+
* is cached for the life of the process. A failure is cached too: a build with
|
|
192
|
+
* no data for a locale will not acquire any by being asked again.
|
|
193
|
+
*/
|
|
194
|
+
const formatters = new Map<string, Intl.DateTimeFormat | null>();
|
|
195
|
+
|
|
196
|
+
function formatter(
|
|
197
|
+
locale: DateLocale,
|
|
198
|
+
options: Intl.DateTimeFormatOptions
|
|
199
|
+
): Intl.DateTimeFormat | null {
|
|
200
|
+
// Sorted, so two call sites writing the same options in a different order
|
|
201
|
+
// share one entry rather than building two.
|
|
202
|
+
const shape = Object.keys(options)
|
|
203
|
+
.sort()
|
|
204
|
+
.map((key) => `${key}=${String(options[key as keyof Intl.DateTimeFormatOptions])}`)
|
|
205
|
+
.join(',');
|
|
206
|
+
const key = `${locale ?? ''}|${shape}`;
|
|
207
|
+
|
|
208
|
+
if (formatters.has(key)) return formatters.get(key) ?? null;
|
|
209
|
+
|
|
210
|
+
let made: Intl.DateTimeFormat | null = null;
|
|
211
|
+
try {
|
|
212
|
+
made = new Intl.DateTimeFormat(locale, options);
|
|
213
|
+
} catch {
|
|
214
|
+
made = null;
|
|
215
|
+
}
|
|
216
|
+
formatters.set(key, made);
|
|
217
|
+
return made;
|
|
218
|
+
}
|
|
219
|
+
|
|
122
220
|
function format(date: Date, locale: DateLocale, options: Intl.DateTimeFormatOptions): string | null {
|
|
123
221
|
try {
|
|
124
|
-
return
|
|
222
|
+
return formatter(locale, options)?.format(date) ?? null;
|
|
125
223
|
} catch {
|
|
224
|
+
// Constructing succeeded and formatting still failed — some builds only
|
|
225
|
+
// discover a missing calendar at format time.
|
|
126
226
|
return null;
|
|
127
227
|
}
|
|
128
228
|
}
|
|
@@ -189,7 +289,7 @@ export interface CalendarParts {
|
|
|
189
289
|
export function resolveCalendar(system: CalendarSystem, locale: DateLocale): 'gregory' | 'islamic' {
|
|
190
290
|
if (system !== 'auto') return system;
|
|
191
291
|
try {
|
|
192
|
-
const resolved =
|
|
292
|
+
const resolved = formatter(locale, {})?.resolvedOptions().calendar ?? '';
|
|
193
293
|
return resolved.startsWith('islamic') ? 'islamic' : 'gregory';
|
|
194
294
|
} catch {
|
|
195
295
|
return 'gregory';
|
|
@@ -214,11 +314,12 @@ export function calendarParts(
|
|
|
214
314
|
return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() };
|
|
215
315
|
}
|
|
216
316
|
try {
|
|
217
|
-
const parts =
|
|
317
|
+
const parts = formatter(withCalendar(locale, 'islamic'), {
|
|
218
318
|
year: 'numeric',
|
|
219
319
|
month: 'numeric',
|
|
220
320
|
day: 'numeric',
|
|
221
|
-
})
|
|
321
|
+
})?.formatToParts(date);
|
|
322
|
+
if (!parts) throw new Error('unusable');
|
|
222
323
|
const read = (type: string) => Number(parts.find((part) => part.type === type)?.value);
|
|
223
324
|
const year = read('year');
|
|
224
325
|
const month = read('month');
|
|
@@ -246,13 +347,24 @@ export function isSameCalendarMonth(
|
|
|
246
347
|
return one.year === two.year && one.month === two.month;
|
|
247
348
|
}
|
|
248
349
|
|
|
350
|
+
/** Whether two days fall in the same year and month of the given calendar. */
|
|
351
|
+
function sameCalendarMonthParts(a: CalendarParts, b: CalendarParts): boolean {
|
|
352
|
+
return a.year === b.year && a.month === b.month;
|
|
353
|
+
}
|
|
354
|
+
|
|
249
355
|
/**
|
|
250
356
|
* The first day of the calendar month `date` falls in.
|
|
251
357
|
*
|
|
252
358
|
* Walked back a day at a time rather than computed. A Hijri month is 29 or 30
|
|
253
359
|
* days depending on the year, and the platform's own calendar data already
|
|
254
|
-
* knows which —
|
|
255
|
-
*
|
|
360
|
+
* knows which — asking it is better than shipping a table that will disagree
|
|
361
|
+
* with the device.
|
|
362
|
+
*
|
|
363
|
+
* The walk stops where the *month number changes*, not where the day number
|
|
364
|
+
* reads 1. The observational Islamic calendars the platforms ship do not always
|
|
365
|
+
* label a day 1: in 1450 AH, month 11 day 30 is followed directly by month 12
|
|
366
|
+
* day 2. Hunting for a day 1 that is never emitted walked the cursor off the
|
|
367
|
+
* end of its budget and then paged the calendar to the same month forever.
|
|
256
368
|
*/
|
|
257
369
|
export function startOfCalendarMonth(
|
|
258
370
|
date: Date,
|
|
@@ -261,9 +373,15 @@ export function startOfCalendarMonth(
|
|
|
261
373
|
): Date {
|
|
262
374
|
if (system === 'gregory') return startOfMonth(date);
|
|
263
375
|
let cursor = startOfDay(date);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
376
|
+
const target = calendarParts(cursor, system, locale);
|
|
377
|
+
// 32, not 31: one step more than the longest month, so the loop is bounded
|
|
378
|
+
// by a real answer rather than by running out.
|
|
379
|
+
for (let step = 0; step < 32; step += 1) {
|
|
380
|
+
const previous = addDays(cursor, -1);
|
|
381
|
+
if (!sameCalendarMonthParts(calendarParts(previous, system, locale), target)) {
|
|
382
|
+
return cursor;
|
|
383
|
+
}
|
|
384
|
+
cursor = previous;
|
|
267
385
|
}
|
|
268
386
|
return cursor;
|
|
269
387
|
}
|
|
@@ -276,8 +394,13 @@ export function daysInCalendarMonth(
|
|
|
276
394
|
): number {
|
|
277
395
|
if (system === 'gregory') return endOfMonth(date).getDate();
|
|
278
396
|
const start = startOfCalendarMonth(date, system, locale);
|
|
397
|
+
const parts = calendarParts(start, system, locale);
|
|
398
|
+
// The first offset that lands outside this month is its length, whatever the
|
|
399
|
+
// day numbers along the way are called.
|
|
279
400
|
for (let length = 28; length <= 31; length += 1) {
|
|
280
|
-
if (calendarParts(addDays(start, length), system, locale)
|
|
401
|
+
if (!sameCalendarMonthParts(calendarParts(addDays(start, length), system, locale), parts)) {
|
|
402
|
+
return length;
|
|
403
|
+
}
|
|
281
404
|
}
|
|
282
405
|
return 30;
|
|
283
406
|
}
|
|
@@ -413,9 +536,10 @@ export function monthNames(locale: DateLocale, style: 'long' | 'short' = 'long')
|
|
|
413
536
|
|
|
414
537
|
/** Column headings, rotated so the first one is `weekStartsOn`. */
|
|
415
538
|
export function weekdayNames(locale: DateLocale, weekStartsOn: number): string[] {
|
|
539
|
+
const first = normalizeWeekStart(weekStartsOn);
|
|
416
540
|
// 2021-08-01 was a Sunday, so adding the index lands on each weekday in turn.
|
|
417
541
|
return Array.from({ length: 7 }, (_unused, day) => {
|
|
418
|
-
const index = (day +
|
|
542
|
+
const index = (day + first) % 7;
|
|
419
543
|
const formatted = formatGregory(new Date(2021, 7, 1 + index), locale, { weekday: 'short' });
|
|
420
544
|
return formatted ?? WEEKDAYS_EN[index]!;
|
|
421
545
|
});
|