@ecohouse/ui 0.1.32 → 0.1.33
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/dist/index.cjs +796 -509
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -8
- package/dist/index.d.ts +74 -8
- package/dist/index.js +729 -448
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DatePicker/DatePicker.tsx +115 -45
- package/src/components/Select/Select.tsx +5 -2
- package/src/components/Sidebar/Sidebar.tsx +25 -11
- package/src/components/StepPager/StepPager.tsx +150 -0
- package/src/components/StepPager/index.ts +2 -0
- package/src/components/index.ts +3 -0
- package/src/icons/Ban/BanIcon.tsx +15 -0
- package/src/icons/Ban/banPath.ts +2 -0
- package/src/icons/Ban/index.ts +1 -0
- package/src/icons/Fullscreen/FullscreenIcon.tsx +21 -0
- package/src/icons/Fullscreen/fullscreenPath.ts +2 -0
- package/src/icons/Fullscreen/index.ts +1 -0
- package/src/icons/Home/homePath.ts +1 -1
- package/src/icons/Sort/SortIcon.tsx +3 -3
- package/src/icons/Sort/SortIcon.web.tsx +2 -2
- package/src/icons/Sort/sortPath.ts +3 -2
- package/src/icons/VideoOff/VideoOffIcon.tsx +21 -0
- package/src/icons/VideoOff/VideoOffIcon.web.tsx +27 -0
- package/src/icons/VideoOff/index.ts +1 -0
- package/src/icons/VideoOff/videoOffPath.ts +2 -0
- package/src/icons/ZoomOut/ZoomOutIcon.tsx +21 -0
- package/src/icons/ZoomOut/index.ts +1 -0
- package/src/icons/ZoomOut/zoomOutPath.ts +2 -0
- package/src/icons/index.ts +4 -0
- package/src/icons/registry.ts +12 -0
- package/src/index.ts +8 -0
- package/src/theme/colors.test.ts +1 -1
- package/src/theme/typography.ts +4 -4
- package/src/utils/popoverAlign.ts +85 -0
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
forwardRef,
|
|
3
3
|
useCallback,
|
|
4
4
|
useEffect,
|
|
5
|
+
useLayoutEffect,
|
|
5
6
|
useMemo,
|
|
6
7
|
useRef,
|
|
7
8
|
useState,
|
|
@@ -93,6 +94,11 @@ type DatePickerBaseProps = Omit<ViewProps, "style" | "children"> & {
|
|
|
93
94
|
weekStartsOn?: 0 | 1;
|
|
94
95
|
/** Styles the calendar popup independently from the trigger width. */
|
|
95
96
|
calendarStyle?: StyleProp<ViewStyle>;
|
|
97
|
+
/**
|
|
98
|
+
* When true, only the calendar panel is rendered (no field/trigger).
|
|
99
|
+
* Use with controlled `open` for icon-button / popover hosts.
|
|
100
|
+
*/
|
|
101
|
+
hideTrigger?: boolean;
|
|
96
102
|
style?: StyleProp<ViewStyle>;
|
|
97
103
|
className?: string;
|
|
98
104
|
};
|
|
@@ -174,6 +180,7 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
|
|
|
174
180
|
formatValue: formatValueProp,
|
|
175
181
|
weekStartsOn = 1,
|
|
176
182
|
calendarStyle,
|
|
183
|
+
hideTrigger = false,
|
|
177
184
|
style,
|
|
178
185
|
className,
|
|
179
186
|
accessibilityLabel,
|
|
@@ -207,6 +214,8 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
|
|
|
207
214
|
);
|
|
208
215
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
209
216
|
const [hoveredDayKey, setHoveredDayKey] = useState<number | null>(null);
|
|
217
|
+
/** Web: flip calendar to the trigger's right edge when it would overflow the viewport. */
|
|
218
|
+
const [menuAlignEnd, setMenuAlignEnd] = useState(false);
|
|
210
219
|
|
|
211
220
|
const rangeValue: DateRange = isRange
|
|
212
221
|
? isValueControlled
|
|
@@ -221,6 +230,13 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
|
|
|
221
230
|
|
|
222
231
|
const isOpen = isOpenControlled ? openProp : uncontrolledOpen;
|
|
223
232
|
|
|
233
|
+
const defaultMenuWidth = usesRangeTrigger ? RANGE_TRIGGER_WIDTH : DEFAULT_WIDTH;
|
|
234
|
+
const resolvedMenuWidth = useMemo(() => {
|
|
235
|
+
const flat = StyleSheet.flatten(calendarStyle);
|
|
236
|
+
const width = flat?.width;
|
|
237
|
+
return typeof width === "number" ? width : defaultMenuWidth;
|
|
238
|
+
}, [calendarStyle, defaultMenuWidth]);
|
|
239
|
+
|
|
224
240
|
const [visibleMonth, setVisibleMonth] = useState(() => {
|
|
225
241
|
const anchor =
|
|
226
242
|
defaultVisibleMonth ?? (isRange ? rangeValue.start : singleValue) ?? new Date();
|
|
@@ -238,11 +254,33 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
|
|
|
238
254
|
setVisibleMonth(new Date(anchor.getFullYear(), anchor.getMonth(), 1));
|
|
239
255
|
} else {
|
|
240
256
|
setHoveredDayKey(null);
|
|
257
|
+
setMenuAlignEnd(false);
|
|
241
258
|
}
|
|
242
259
|
},
|
|
243
260
|
[isOpenControlled, isRange, onOpenChange, rangeValue.start, singleValue],
|
|
244
261
|
);
|
|
245
262
|
|
|
263
|
+
// Prefer left alignment; flip to right when the menu would overflow the viewport (web).
|
|
264
|
+
useLayoutEffect(() => {
|
|
265
|
+
if (Platform.OS !== "web" || !isOpen || hideTrigger) return;
|
|
266
|
+
|
|
267
|
+
const updateMenuAlign = () => {
|
|
268
|
+
const trigger = triggerRef.current as unknown as {
|
|
269
|
+
getBoundingClientRect?: () => DOMRect;
|
|
270
|
+
} | null;
|
|
271
|
+
const rect = trigger?.getBoundingClientRect?.();
|
|
272
|
+
if (!rect) return;
|
|
273
|
+
|
|
274
|
+
const viewportPadding = 8;
|
|
275
|
+
const overflowsRight = rect.left + resolvedMenuWidth > window.innerWidth - viewportPadding;
|
|
276
|
+
setMenuAlignEnd(overflowsRight);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
updateMenuAlign();
|
|
280
|
+
window.addEventListener("resize", updateMenuAlign);
|
|
281
|
+
return () => window.removeEventListener("resize", updateMenuAlign);
|
|
282
|
+
}, [hideTrigger, isOpen, resolvedMenuWidth]);
|
|
283
|
+
|
|
246
284
|
// Close on outside click (web only; native has no document-level events).
|
|
247
285
|
useEffect(() => {
|
|
248
286
|
if (Platform.OS !== "web" || !isOpen) return;
|
|
@@ -371,6 +409,7 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
|
|
|
371
409
|
style={[
|
|
372
410
|
styles.root,
|
|
373
411
|
usesRangeTrigger && styles.rangeRoot,
|
|
412
|
+
hideTrigger && styles.rootHideTrigger,
|
|
374
413
|
isOpen && styles.rootOpen,
|
|
375
414
|
disabled && styles.disabled,
|
|
376
415
|
style,
|
|
@@ -378,52 +417,65 @@ export const DatePicker = forwardRef<ComponentRef<typeof View>, DatePickerProps>
|
|
|
378
417
|
accessibilityState={{ disabled, expanded: isOpen }}
|
|
379
418
|
>
|
|
380
419
|
<View style={[styles.triggerWrap, isOpen && styles.triggerWrapOpen]}>
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
{
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
<
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
420
|
+
{hideTrigger ? null : (
|
|
421
|
+
<Pressable
|
|
422
|
+
ref={triggerRef}
|
|
423
|
+
onPress={handleTriggerPress}
|
|
424
|
+
disabled={disabled}
|
|
425
|
+
accessibilityRole="button"
|
|
426
|
+
accessibilityLabel={resolvedAccessibilityLabel}
|
|
427
|
+
accessibilityState={{ disabled, expanded: isOpen }}
|
|
428
|
+
style={[
|
|
429
|
+
styles.trigger,
|
|
430
|
+
usesRangeTrigger && styles.rangeTrigger,
|
|
431
|
+
usesRangeTrigger &&
|
|
432
|
+
(usesVerticalRangeTrigger
|
|
433
|
+
? styles.rangeTriggerVertical
|
|
434
|
+
: styles.rangeTriggerHorizontal),
|
|
435
|
+
{
|
|
436
|
+
borderColor: triggerBorderColor,
|
|
437
|
+
backgroundColor: usesRangeTrigger ? "transparent" : colors.grey700,
|
|
438
|
+
},
|
|
439
|
+
]}
|
|
440
|
+
>
|
|
441
|
+
{usesRangeTrigger ? (
|
|
442
|
+
<>
|
|
443
|
+
<RangeTriggerField label={startLabel} time={startTime} value={startValue} />
|
|
444
|
+
<View
|
|
445
|
+
style={
|
|
446
|
+
usesVerticalRangeTrigger
|
|
447
|
+
? styles.rangeDividerVertical
|
|
448
|
+
: styles.rangeDividerHorizontal
|
|
449
|
+
}
|
|
450
|
+
/>
|
|
451
|
+
<RangeTriggerField label={endLabel} time={endTime} value={endValue} />
|
|
452
|
+
</>
|
|
453
|
+
) : (
|
|
454
|
+
<>
|
|
455
|
+
<Text style={[styles.triggerText, { color: triggerTextColor }]} numberOfLines={1}>
|
|
456
|
+
{triggerLabel}
|
|
457
|
+
</Text>
|
|
458
|
+
<View style={styles.iconSlot}>
|
|
459
|
+
<CalendarOutlineIcon size={ICON_SIZE} color={colors.grey100} />
|
|
460
|
+
</View>
|
|
461
|
+
</>
|
|
462
|
+
)}
|
|
463
|
+
</Pressable>
|
|
464
|
+
)}
|
|
424
465
|
|
|
425
466
|
{isOpen ? (
|
|
426
|
-
<View
|
|
467
|
+
<View
|
|
468
|
+
style={[
|
|
469
|
+
styles.menu,
|
|
470
|
+
usesRangeTrigger && styles.rangeMenu,
|
|
471
|
+
hideTrigger
|
|
472
|
+
? styles.menuInline
|
|
473
|
+
: menuAlignEnd
|
|
474
|
+
? styles.menuAlignEnd
|
|
475
|
+
: styles.menuAlignStart,
|
|
476
|
+
calendarStyle,
|
|
477
|
+
]}
|
|
478
|
+
>
|
|
427
479
|
<View style={styles.calendarHeader}>
|
|
428
480
|
<Pressable
|
|
429
481
|
onPress={handlePrevMonth}
|
|
@@ -590,6 +642,10 @@ const styles = StyleSheet.create({
|
|
|
590
642
|
gap: 8,
|
|
591
643
|
alignSelf: "flex-start",
|
|
592
644
|
},
|
|
645
|
+
rootHideTrigger: {
|
|
646
|
+
width: "auto",
|
|
647
|
+
gap: 0,
|
|
648
|
+
},
|
|
593
649
|
rootOpen: {
|
|
594
650
|
zIndex: 30,
|
|
595
651
|
},
|
|
@@ -694,7 +750,6 @@ const styles = StyleSheet.create({
|
|
|
694
750
|
menu: {
|
|
695
751
|
position: "absolute",
|
|
696
752
|
top: "100%",
|
|
697
|
-
left: 0,
|
|
698
753
|
width: DEFAULT_WIDTH,
|
|
699
754
|
marginTop: 8,
|
|
700
755
|
zIndex: 10,
|
|
@@ -711,6 +766,21 @@ const styles = StyleSheet.create({
|
|
|
711
766
|
},
|
|
712
767
|
}),
|
|
713
768
|
},
|
|
769
|
+
menuAlignStart: {
|
|
770
|
+
left: 0,
|
|
771
|
+
right: "auto",
|
|
772
|
+
},
|
|
773
|
+
menuAlignEnd: {
|
|
774
|
+
left: "auto",
|
|
775
|
+
right: 0,
|
|
776
|
+
},
|
|
777
|
+
menuInline: {
|
|
778
|
+
position: "relative",
|
|
779
|
+
top: "auto",
|
|
780
|
+
left: "auto",
|
|
781
|
+
right: "auto",
|
|
782
|
+
marginTop: 0,
|
|
783
|
+
},
|
|
714
784
|
rangeMenu: {
|
|
715
785
|
width: RANGE_TRIGGER_WIDTH,
|
|
716
786
|
},
|
|
@@ -61,6 +61,8 @@ type SelectBaseProps = Omit<ViewProps, "style" | "children"> & {
|
|
|
61
61
|
loading?: boolean;
|
|
62
62
|
/** Text shown when no options match. */
|
|
63
63
|
emptyText?: string;
|
|
64
|
+
/** Override color for the selected value text in the trigger. */
|
|
65
|
+
valueColor?: string;
|
|
64
66
|
style?: StyleProp<ViewStyle>;
|
|
65
67
|
className?: string;
|
|
66
68
|
};
|
|
@@ -264,6 +266,7 @@ export const Select = forwardRef<ComponentRef<typeof View>, SelectProps>(
|
|
|
264
266
|
onSearchChange,
|
|
265
267
|
loading = false,
|
|
266
268
|
emptyText = "No results",
|
|
269
|
+
valueColor,
|
|
267
270
|
style,
|
|
268
271
|
className,
|
|
269
272
|
accessibilityLabel,
|
|
@@ -508,7 +511,7 @@ export const Select = forwardRef<ComponentRef<typeof View>, SelectProps>(
|
|
|
508
511
|
|
|
509
512
|
const leftIconNode = renderStatefulIcon(leftIcon, UserIcon, {
|
|
510
513
|
size: ICON_SIZE,
|
|
511
|
-
color: error ? colors.red :
|
|
514
|
+
color: error ? colors.red : colors.primary,
|
|
512
515
|
});
|
|
513
516
|
const hasLeftIcon = showLeftIcon || leftIcon != null;
|
|
514
517
|
|
|
@@ -520,7 +523,7 @@ export const Select = forwardRef<ComponentRef<typeof View>, SelectProps>(
|
|
|
520
523
|
const triggerTextColor = hasValue
|
|
521
524
|
? error
|
|
522
525
|
? colors.red
|
|
523
|
-
: colors.white
|
|
526
|
+
: (valueColor ?? colors.white)
|
|
524
527
|
: error
|
|
525
528
|
? colors.red
|
|
526
529
|
: colors.grey100;
|
|
@@ -59,6 +59,8 @@ export interface SidebarProps extends Omit<ViewProps, "style" | "children"> {
|
|
|
59
59
|
collapsed?: boolean;
|
|
60
60
|
defaultCollapsed?: boolean;
|
|
61
61
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
62
|
+
/** When false, hides the expand/collapse control. Defaults to `true`. */
|
|
63
|
+
showCollapseToggle?: boolean;
|
|
62
64
|
style?: StyleProp<ViewStyle>;
|
|
63
65
|
className?: string;
|
|
64
66
|
}
|
|
@@ -86,6 +88,7 @@ export const Sidebar = forwardRef<ComponentRef<typeof View>, SidebarProps>(funct
|
|
|
86
88
|
collapsed: collapsedProp,
|
|
87
89
|
defaultCollapsed = false,
|
|
88
90
|
onCollapsedChange,
|
|
91
|
+
showCollapseToggle = true,
|
|
89
92
|
style,
|
|
90
93
|
className,
|
|
91
94
|
...rest
|
|
@@ -136,7 +139,13 @@ export const Sidebar = forwardRef<ComponentRef<typeof View>, SidebarProps>(funct
|
|
|
136
139
|
style={[styles.root, { width: widthAnim }, style]}
|
|
137
140
|
accessibilityRole="menu"
|
|
138
141
|
>
|
|
139
|
-
<View
|
|
142
|
+
<View
|
|
143
|
+
style={[
|
|
144
|
+
styles.header,
|
|
145
|
+
collapsed ? styles.headerCollapsed : styles.headerExpanded,
|
|
146
|
+
!showCollapseToggle ? styles.headerNoToggle : null,
|
|
147
|
+
]}
|
|
148
|
+
>
|
|
140
149
|
<Animated.View
|
|
141
150
|
pointerEvents={collapsed ? "none" : "auto"}
|
|
142
151
|
style={[
|
|
@@ -157,16 +166,18 @@ export const Sidebar = forwardRef<ComponentRef<typeof View>, SidebarProps>(funct
|
|
|
157
166
|
/>
|
|
158
167
|
</Animated.View>
|
|
159
168
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
{showCollapseToggle ? (
|
|
170
|
+
<Pressable
|
|
171
|
+
accessibilityRole="button"
|
|
172
|
+
accessibilityLabel={collapsed ? expandAccessibilityLabel : collapseAccessibilityLabel}
|
|
173
|
+
accessibilityState={{ expanded: !collapsed }}
|
|
174
|
+
hitSlop={8}
|
|
175
|
+
onPress={() => setCollapsed(!collapsed)}
|
|
176
|
+
style={styles.toggleButton}
|
|
177
|
+
>
|
|
178
|
+
<SidebarIcon color={colors.grey100} size={TOGGLE_SIZE} strokeWidth={2} />
|
|
179
|
+
</Pressable>
|
|
180
|
+
) : null}
|
|
170
181
|
</View>
|
|
171
182
|
|
|
172
183
|
<View
|
|
@@ -242,6 +253,9 @@ const styles = StyleSheet.create({
|
|
|
242
253
|
justifyContent: "space-between",
|
|
243
254
|
paddingHorizontal: 24,
|
|
244
255
|
},
|
|
256
|
+
headerNoToggle: {
|
|
257
|
+
justifyContent: "flex-start",
|
|
258
|
+
},
|
|
245
259
|
headerCollapsed: {
|
|
246
260
|
justifyContent: "center",
|
|
247
261
|
paddingHorizontal: 0,
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { forwardRef, useMemo, useRef, type ComponentRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Platform,
|
|
4
|
+
Pressable,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from "react-native";
|
|
12
|
+
import { ChevronLeftIcon } from "../../icons";
|
|
13
|
+
import { colors, fonts } from "../../theme";
|
|
14
|
+
import { mergeRefs } from "../../utils/mergeRefs";
|
|
15
|
+
import { useApplyWebClassName } from "../../utils/useApplyWebClassName";
|
|
16
|
+
|
|
17
|
+
export interface StepPagerProps extends Omit<ViewProps, "style" | "children"> {
|
|
18
|
+
/** Zero-based active step index. */
|
|
19
|
+
index: number;
|
|
20
|
+
/** Total number of steps. */
|
|
21
|
+
count: number;
|
|
22
|
+
onPrevious?: () => void;
|
|
23
|
+
onNext?: () => void;
|
|
24
|
+
previousAccessibilityLabel?: string;
|
|
25
|
+
nextAccessibilityLabel?: string;
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const WIDTH = 121;
|
|
31
|
+
const HEIGHT = 48;
|
|
32
|
+
const ICON_SIZE = 24;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compact prev / current-of-total / next control (e.g. camera fullscreen pager).
|
|
36
|
+
* Fixed 121×48 pill, 12 gap/padding, 77 radius.
|
|
37
|
+
*/
|
|
38
|
+
export const StepPager = forwardRef<ComponentRef<typeof View>, StepPagerProps>(function StepPager(
|
|
39
|
+
{
|
|
40
|
+
index,
|
|
41
|
+
count,
|
|
42
|
+
onPrevious,
|
|
43
|
+
onNext,
|
|
44
|
+
previousAccessibilityLabel = "Previous",
|
|
45
|
+
nextAccessibilityLabel = "Next",
|
|
46
|
+
style,
|
|
47
|
+
className,
|
|
48
|
+
accessibilityLabel,
|
|
49
|
+
...rest
|
|
50
|
+
},
|
|
51
|
+
forwardedRef,
|
|
52
|
+
) {
|
|
53
|
+
const containerRef = useRef<ComponentRef<typeof View>>(null);
|
|
54
|
+
const setContainerRef = useMemo(() => mergeRefs(containerRef, forwardedRef), [forwardedRef]);
|
|
55
|
+
const safeCount = Math.max(0, count);
|
|
56
|
+
const safeIndex = safeCount === 0 ? 0 : Math.min(Math.max(index, 0), safeCount - 1);
|
|
57
|
+
const label = safeCount === 0 ? "0/0" : `${safeIndex + 1}/${safeCount}`;
|
|
58
|
+
const atStart = safeIndex <= 0;
|
|
59
|
+
const atEnd = safeIndex >= safeCount - 1;
|
|
60
|
+
|
|
61
|
+
useApplyWebClassName(containerRef, className);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View
|
|
65
|
+
{...rest}
|
|
66
|
+
ref={setContainerRef}
|
|
67
|
+
accessibilityRole="adjustable"
|
|
68
|
+
accessibilityLabel={accessibilityLabel ?? label}
|
|
69
|
+
accessibilityValue={{
|
|
70
|
+
min: safeCount === 0 ? 0 : 1,
|
|
71
|
+
max: safeCount,
|
|
72
|
+
now: safeCount === 0 ? 0 : safeIndex + 1,
|
|
73
|
+
text: label,
|
|
74
|
+
}}
|
|
75
|
+
style={[styles.root, webBlurStyle, style]}
|
|
76
|
+
>
|
|
77
|
+
<Pressable
|
|
78
|
+
onPress={onPrevious}
|
|
79
|
+
disabled={atStart || !onPrevious}
|
|
80
|
+
hitSlop={4}
|
|
81
|
+
accessibilityRole="button"
|
|
82
|
+
accessibilityLabel={previousAccessibilityLabel}
|
|
83
|
+
accessibilityState={{ disabled: atStart || !onPrevious }}
|
|
84
|
+
style={[styles.navButton, (atStart || !onPrevious) && styles.navDisabled]}
|
|
85
|
+
>
|
|
86
|
+
<ChevronLeftIcon size={ICON_SIZE} color={colors.white} strokeWidth={2} />
|
|
87
|
+
</Pressable>
|
|
88
|
+
|
|
89
|
+
<Text style={styles.label}>{label}</Text>
|
|
90
|
+
|
|
91
|
+
<Pressable
|
|
92
|
+
onPress={onNext}
|
|
93
|
+
disabled={atEnd || !onNext}
|
|
94
|
+
hitSlop={4}
|
|
95
|
+
accessibilityRole="button"
|
|
96
|
+
accessibilityLabel={nextAccessibilityLabel}
|
|
97
|
+
accessibilityState={{ disabled: atEnd || !onNext }}
|
|
98
|
+
style={[styles.navButton, (atEnd || !onNext) && styles.navDisabled]}
|
|
99
|
+
>
|
|
100
|
+
<View style={styles.chevronRight}>
|
|
101
|
+
<ChevronLeftIcon size={ICON_SIZE} color={colors.white} strokeWidth={2} />
|
|
102
|
+
</View>
|
|
103
|
+
</Pressable>
|
|
104
|
+
</View>
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const webBlurStyle: ViewStyle =
|
|
109
|
+
Platform.OS === "web"
|
|
110
|
+
? ({
|
|
111
|
+
backdropFilter: "blur(50px)",
|
|
112
|
+
WebkitBackdropFilter: "blur(50px)",
|
|
113
|
+
} as ViewStyle)
|
|
114
|
+
: {};
|
|
115
|
+
|
|
116
|
+
const styles = StyleSheet.create({
|
|
117
|
+
root: {
|
|
118
|
+
width: WIDTH,
|
|
119
|
+
height: HEIGHT,
|
|
120
|
+
flexDirection: "row",
|
|
121
|
+
alignItems: "center",
|
|
122
|
+
justifyContent: "center",
|
|
123
|
+
gap: 12,
|
|
124
|
+
padding: 12,
|
|
125
|
+
borderRadius: 77,
|
|
126
|
+
backgroundColor: colors.whiteAlpha6,
|
|
127
|
+
overflow: "hidden",
|
|
128
|
+
},
|
|
129
|
+
navButton: {
|
|
130
|
+
width: ICON_SIZE,
|
|
131
|
+
height: ICON_SIZE,
|
|
132
|
+
alignItems: "center",
|
|
133
|
+
justifyContent: "center",
|
|
134
|
+
},
|
|
135
|
+
navDisabled: {
|
|
136
|
+
opacity: 0.35,
|
|
137
|
+
},
|
|
138
|
+
chevronRight: {
|
|
139
|
+
transform: [{ scaleX: -1 }],
|
|
140
|
+
},
|
|
141
|
+
label: {
|
|
142
|
+
fontFamily: fonts.sans,
|
|
143
|
+
fontSize: 16,
|
|
144
|
+
fontWeight: "700",
|
|
145
|
+
lineHeight: 16,
|
|
146
|
+
letterSpacing: 0,
|
|
147
|
+
color: colors.white,
|
|
148
|
+
textAlign: "center",
|
|
149
|
+
},
|
|
150
|
+
});
|
package/src/components/index.ts
CHANGED
|
@@ -16,6 +16,9 @@ export type { StatusBadgeProps, StatusBadgeTone } from "./StatusBadge";
|
|
|
16
16
|
export { PaginationDots } from "./PaginationDots";
|
|
17
17
|
export type { PaginationDotsProps } from "./PaginationDots";
|
|
18
18
|
|
|
19
|
+
export { StepPager } from "./StepPager";
|
|
20
|
+
export type { StepPagerProps } from "./StepPager";
|
|
21
|
+
|
|
19
22
|
export { InfoTile } from "./InfoTile";
|
|
20
23
|
export type { InfoTileProps } from "./InfoTile";
|
|
21
24
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Svg, { Path } from "react-native-svg";
|
|
2
|
+
import { colors } from "../../theme";
|
|
3
|
+
import type { IconProps } from "../types";
|
|
4
|
+
import { BAN_PATH } from "./banPath";
|
|
5
|
+
|
|
6
|
+
export { BAN_PATH } from "./banPath";
|
|
7
|
+
|
|
8
|
+
/** Filled prohibition / deactivated mark (28×28 viewBox). */
|
|
9
|
+
export function BanIcon({ size = 28, color = colors.primary }: IconProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Svg width={size} height={size} viewBox="0 0 28 28" fill="none">
|
|
12
|
+
<Path d={BAN_PATH} fill={color} />
|
|
13
|
+
</Svg>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export const BAN_PATH =
|
|
2
|
+
"M14.0007 2.33301C11.6932 2.33301 9.43758 3.01725 7.519 4.2992C5.60043 5.58115 4.10508 7.40323 3.22206 9.53503C2.33904 11.6668 2.108 14.0126 2.55816 16.2757C3.00832 18.5388 4.11946 20.6176 5.75108 22.2493C7.38269 23.8809 9.46149 24.992 11.7246 25.4422C13.9877 25.8923 16.3335 25.6613 18.4653 24.7783C20.5971 23.8952 22.4192 22.3999 23.7011 20.4813C24.9831 18.5628 25.6673 16.3071 25.6673 13.9997C25.6673 12.4676 25.3656 10.9505 24.7793 9.53503C24.1929 8.11957 23.3336 6.83345 22.2502 5.7501C21.1669 4.66675 19.8808 3.80738 18.4653 3.22108C17.0498 2.63478 15.5327 2.33301 14.0007 2.33301ZM14.0007 23.333C11.5253 23.333 9.15133 22.3497 7.40099 20.5993C5.65065 18.849 4.66732 16.475 4.66732 13.9997C4.66475 11.9269 5.35917 9.91348 6.63899 8.28301L19.7173 21.3613C18.0869 22.6412 16.0734 23.3356 14.0007 23.333ZM21.3623 19.7163L8.28399 6.63801C9.91446 5.35819 11.9279 4.66376 14.0007 4.66634C16.476 4.66634 18.85 5.64967 20.6003 7.40001C22.3507 9.15035 23.334 11.5243 23.334 13.9997C23.3366 16.0724 22.6421 18.0859 21.3623 19.7163Z";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BanIcon, BAN_PATH } from "./BanIcon";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Svg, { Path } from "react-native-svg";
|
|
2
|
+
import { colors } from "../../theme";
|
|
3
|
+
import type { IconProps } from "../types";
|
|
4
|
+
import { FULLSCREEN_PATH } from "./fullscreenPath";
|
|
5
|
+
|
|
6
|
+
export { FULLSCREEN_PATH } from "./fullscreenPath";
|
|
7
|
+
|
|
8
|
+
/** Four-corner fullscreen / maximize control (24×24 viewBox). */
|
|
9
|
+
export function FullscreenIcon({ size = 24, color = colors.white, strokeWidth = 2 }: IconProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
12
|
+
<Path
|
|
13
|
+
d={FULLSCREEN_PATH}
|
|
14
|
+
stroke={color}
|
|
15
|
+
strokeWidth={strokeWidth}
|
|
16
|
+
strokeLinecap="round"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
/>
|
|
19
|
+
</Svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FullscreenIcon, FULLSCREEN_PATH } from "./FullscreenIcon";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const HOME_PATH =
|
|
2
|
-
"
|
|
2
|
+
"M6.77168 11.6663C7.14172 13.104 8.4468 14.1663 10 14.1663C11.5532 14.1663 12.8583 13.104 13.2283 11.6663M9.18141 2.30297L3.52949 6.6989C3.15168 6.99276 2.96278 7.13968 2.82669 7.32368C2.70614 7.48667 2.61633 7.67029 2.56169 7.86551C2.5 8.0859 2.5 8.32521 2.5 8.80384V14.833C2.5 15.7664 2.5 16.2331 2.68166 16.5896C2.84144 16.9032 3.09641 17.1582 3.41002 17.318C3.76654 17.4996 4.23325 17.4996 5.16667 17.4996H14.8333C15.7668 17.4996 16.2335 17.4996 16.59 17.318C16.9036 17.1582 17.1586 16.9032 17.3183 16.5896C17.5 16.2331 17.5 15.7664 17.5 14.833V8.80384C17.5 8.32521 17.5 8.0859 17.4383 7.86551C17.3837 7.67029 17.2939 7.48667 17.1733 7.32368C17.0372 7.13968 16.8483 6.99276 16.4705 6.69891L10.8186 2.30297C10.5258 2.07526 10.3794 1.9614 10.2178 1.91763C10.0752 1.87902 9.92484 1.87902 9.78221 1.91763C9.62057 1.9614 9.47418 2.07526 9.18141 2.30297Z";
|
|
@@ -5,10 +5,10 @@ import type { IconProps } from "../types";
|
|
|
5
5
|
|
|
6
6
|
export { SORT_PATH } from "./sortPath";
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
export function SortIcon({ size =
|
|
8
|
+
/** Bidirectional sort (20×20 viewBox). */
|
|
9
|
+
export function SortIcon({ size = 20, color = colors.grey300, strokeWidth = 2 }: IconProps) {
|
|
10
10
|
return (
|
|
11
|
-
<Svg width={size} height={size} viewBox="0 0
|
|
11
|
+
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none">
|
|
12
12
|
<Path
|
|
13
13
|
d={SORT_PATH}
|
|
14
14
|
stroke={color}
|
|
@@ -3,12 +3,12 @@ import { colors } from "../../theme";
|
|
|
3
3
|
import type { IconProps } from "../types";
|
|
4
4
|
|
|
5
5
|
/** Web / Storybook — plain SVG (no react-native-svg). */
|
|
6
|
-
export function SortIcon({ size =
|
|
6
|
+
export function SortIcon({ size = 20, color = colors.grey300, strokeWidth = 2 }: IconProps) {
|
|
7
7
|
return (
|
|
8
8
|
<svg
|
|
9
9
|
width={size}
|
|
10
10
|
height={size}
|
|
11
|
-
viewBox="0 0
|
|
11
|
+
viewBox="0 0 20 20"
|
|
12
12
|
fill="none"
|
|
13
13
|
xmlns="http://www.w3.org/2000/svg"
|
|
14
14
|
aria-hidden
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
/** Bidirectional sort icon path —
|
|
2
|
-
export const SORT_PATH =
|
|
1
|
+
/** Bidirectional sort icon path — 20×20 viewBox. */
|
|
2
|
+
export const SORT_PATH =
|
|
3
|
+
"M5.83333 3.33301V16.6663M9.16667 13.333L5.83333 16.6663L2.5 13.333M14.1667 16.6663V3.33301M17.5 6.66634L14.1667 3.33301L10.8333 6.66634";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Svg, { Path } from "react-native-svg";
|
|
2
|
+
import { colors } from "../../theme";
|
|
3
|
+
import type { IconProps } from "../types";
|
|
4
|
+
import { VIDEO_OFF_PATH } from "./videoOffPath";
|
|
5
|
+
|
|
6
|
+
export { VIDEO_OFF_PATH } from "./videoOffPath";
|
|
7
|
+
|
|
8
|
+
/** Video / camera off (24×24 viewBox). */
|
|
9
|
+
export function VideoOffIcon({ size = 24, color = colors.primary, strokeWidth = 2 }: IconProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
12
|
+
<Path
|
|
13
|
+
d={VIDEO_OFF_PATH}
|
|
14
|
+
stroke={color}
|
|
15
|
+
strokeWidth={strokeWidth}
|
|
16
|
+
strokeLinecap="round"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
/>
|
|
19
|
+
</Svg>
|
|
20
|
+
);
|
|
21
|
+
}
|