ferns-ui 1.3.0 → 1.4.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/dist/Common.d.ts CHANGED
@@ -443,11 +443,17 @@ export interface IconProps {
443
443
  }
444
444
  export type TooltipPosition = "top" | "bottom" | "left" | "right";
445
445
  export type IndicatorDirection = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
446
+ export type SegmentedControlBadgeConfig = {
447
+ count: number;
448
+ status?: "info" | "error" | "warning" | "success" | "neutral";
449
+ };
446
450
  export interface SegmentedControlProps {
447
451
  items: string[];
448
452
  size?: "md" | "lg";
449
453
  onChange: (activeIndex: number) => void;
450
454
  selectedIndex?: number;
455
+ maxItems?: number;
456
+ badges?: SegmentedControlBadgeConfig[];
451
457
  }
452
458
  export interface TimezonePickerProps {
453
459
  timezone?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"AAuXA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;CACP,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,WAAW,CAAC,OAAyB,CAAC,CAAC;AAChD,CAAC;AAqBD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAE,EAAE;IAClD,OAAO;QACL,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,KAAK,EAAE,EAAE;KACV,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAClB,CAAC,CAAC;AAiRF,MAAM,YAAY,GAAG;IACnB,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,GAAG;IACZ,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,QAAkB;IAC5C,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
1
+ {"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"AAuXA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;CACP,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,WAAW,CAAC,OAAyB,CAAC,CAAC;AAChD,CAAC;AAqBD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAE,EAAE;IAClD,OAAO;QACL,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,KAAK,EAAE,EAAE;KACV,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAClB,CAAC,CAAC;AAwRF,MAAM,YAAY,GAAG;IACnB,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,GAAG;IACZ,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;CACX,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,QAAkB;IAC5C,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import { SegmentedControlProps } from "./Common";
3
- export declare const SegmentedControl: ({ items, onChange, size, selectedIndex, }: SegmentedControlProps) => React.JSX.Element;
3
+ export declare const SegmentedControl: React.FC<SegmentedControlProps>;
@@ -1,37 +1,72 @@
1
- import React from "react";
1
+ import React, { useCallback, useState } from "react";
2
2
  import { Pressable, View } from "react-native";
3
+ import { Badge } from "./Badge";
3
4
  import { Heading } from "./Heading";
5
+ import { Icon } from "./Icon";
4
6
  import { useTheme } from "./Theme";
5
- export const SegmentedControl = ({ items, onChange = () => { }, size = "md", selectedIndex, }) => {
7
+ export const SegmentedControl = ({ items, onChange = () => { }, size = "md", selectedIndex, maxItems, badges = [], }) => {
6
8
  const height = size === "md" ? 36 : 44;
7
9
  const { theme } = useTheme();
10
+ const [startIndex, setStartIndex] = useState(0);
11
+ const handlePrevious = useCallback(() => {
12
+ setStartIndex((prev) => Math.max(0, prev - (maxItems !== null && maxItems !== void 0 ? maxItems : 4)));
13
+ }, []);
14
+ const handleNext = useCallback(() => {
15
+ setStartIndex((prev) => Math.min(items.length - (maxItems !== null && maxItems !== void 0 ? maxItems : items.length), prev + (maxItems !== null && maxItems !== void 0 ? maxItems : 4)));
16
+ }, [items.length, maxItems]);
17
+ const visibleItems = maxItems ? items.slice(startIndex, startIndex + maxItems) : items;
18
+ const visibleBadges = maxItems ? badges.slice(startIndex, startIndex + maxItems) : badges;
19
+ const canScrollLeft = startIndex > 0;
20
+ const canScrollRight = maxItems ? startIndex + maxItems < items.length : false;
21
+ const shouldShowScrollButtons = maxItems ? maxItems < items.length : false;
8
22
  return (React.createElement(View, { style: {
9
23
  display: "flex",
10
- flexGrow: 1,
11
24
  flexDirection: "row",
12
- flexShrink: 1,
13
25
  alignItems: "center",
14
- gap: 4,
15
- height,
16
- maxHeight: height,
17
- borderRadius: theme.primitives.radius3xl,
18
- borderColor: theme.primitives.neutral300,
19
- borderWidth: 3,
20
- backgroundColor: theme.primitives.neutral300,
21
- } }, items.map((item, index) => (React.createElement(Pressable, { key: index, "aria-role": "button", style: {
22
- display: "flex",
23
- paddingHorizontal: size === "md" ? theme.spacing.sm : theme.spacing.md,
24
- justifyContent: "center",
25
- alignItems: "center",
26
- height: "100%",
27
- flexBasis: 0,
28
- gap: 12,
29
- flexGrow: 1,
30
- flexShrink: 0,
31
- borderRadius: theme.primitives.radius3xl,
32
- backgroundColor: index === selectedIndex ? theme.surface.base : undefined,
33
- overflow: "hidden",
34
- }, onPress: () => onChange(index) },
35
- React.createElement(Heading, { size: "sm" }, item))))));
26
+ gap: 8,
27
+ } },
28
+ Boolean(shouldShowScrollButtons) && (React.createElement(Pressable, { disabled: !canScrollLeft, onPress: handlePrevious },
29
+ React.createElement(Icon, { color: canScrollLeft ? "linkLight" : "extraLight", iconName: "chevron-left", size: "lg" }))),
30
+ React.createElement(View, { style: {
31
+ display: "flex",
32
+ flexGrow: 1,
33
+ flexDirection: "row",
34
+ flexShrink: 1,
35
+ alignItems: "center",
36
+ height,
37
+ maxHeight: height,
38
+ backgroundColor: theme.primitives.neutral300,
39
+ overflow: "hidden",
40
+ borderRadius: theme.primitives.radius3xl,
41
+ } },
42
+ React.createElement(View, { style: {
43
+ display: "flex",
44
+ flexDirection: "row",
45
+ gap: 4,
46
+ flexGrow: 1,
47
+ paddingHorizontal: 4,
48
+ height: height - 4,
49
+ } }, visibleItems.map((item, index) => {
50
+ var _a;
51
+ const actualIndex = startIndex + index;
52
+ return (React.createElement(Pressable, { key: actualIndex, "aria-role": "button", style: {
53
+ display: "flex",
54
+ paddingHorizontal: 2,
55
+ justifyContent: "center",
56
+ alignItems: "center",
57
+ height: "100%",
58
+ flexDirection: "row",
59
+ gap: 8,
60
+ flexGrow: 1,
61
+ flexBasis: 0,
62
+ borderRadius: theme.primitives.radius3xl,
63
+ backgroundColor: actualIndex === selectedIndex ? theme.surface.base : undefined,
64
+ overflow: "hidden",
65
+ }, onPress: () => onChange(actualIndex) },
66
+ React.createElement(Heading, { size: "sm" }, item),
67
+ visibleBadges[index] && (React.createElement(Badge, { status: (_a = visibleBadges[index].status) !== null && _a !== void 0 ? _a : "info", value: visibleBadges[index].count, variant: "numberOnly" }))));
68
+ }))),
69
+ Boolean(shouldShowScrollButtons) && (React.createElement(Pressable, { disabled: !canScrollRight, onPress: handleNext },
70
+ React.createElement(Icon, { color: canScrollRight ? "linkLight" : "extraLight", iconName: "chevron-right", size: "lg" })))));
36
71
  };
37
72
  //# sourceMappingURL=SegmentedControl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl.js","sourceRoot":"","sources":["../src/SegmentedControl.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,SAAS,EAAE,IAAI,EAAC,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAClC,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAC/B,KAAK,EACL,QAAQ,GAAG,GAAG,EAAE,GAAE,CAAC,EACnB,IAAI,GAAG,IAAI,EACX,aAAa,GACS,EAAE,EAAE;IAC1B,MAAM,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,MAAM,EAAC,KAAK,EAAC,GAAG,QAAQ,EAAE,CAAC;IAC3B,OAAO,CACL,oBAAC,IAAI,IACH,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,QAAQ,EAAE,CAAC;YACX,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,QAAQ;YACpB,GAAG,EAAE,CAAC;YACN,MAAM;YACN,SAAS,EAAE,MAAM;YACjB,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS;YACxC,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,UAAU;YACxC,WAAW,EAAE,CAAC;YACd,eAAe,EAAE,KAAK,CAAC,UAAU,CAAC,UAAU;SAC7C,IAEA,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1B,oBAAC,SAAS,IACR,GAAG,EAAE,KAAK,eACA,QAAQ,EAClB,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,iBAAiB,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;YACtE,cAAc,EAAE,QAAQ;YACxB,UAAU,EAAE,QAAQ;YACpB,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,CAAC;YACZ,GAAG,EAAE,EAAE;YACP,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,CAAC;YACb,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS;YACxC,eAAe,EAAE,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;YACzE,QAAQ,EAAE,QAAQ;SACnB,EACD,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QAE9B,oBAAC,OAAO,IAAC,IAAI,EAAC,IAAI,IAAE,IAAI,CAAW,CACzB,CACb,CAAC,CACG,CACR,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"SegmentedControl.js","sourceRoot":"","sources":["../src/SegmentedControl.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ferns-ui",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "main": "dist/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {
package/src/Common.ts CHANGED
@@ -575,11 +575,18 @@ export type TooltipPosition = "top" | "bottom" | "left" | "right";
575
575
 
576
576
  export type IndicatorDirection = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
577
577
 
578
+ export type SegmentedControlBadgeConfig = {
579
+ count: number;
580
+ status?: "info" | "error" | "warning" | "success" | "neutral";
581
+ };
582
+
578
583
  export interface SegmentedControlProps {
579
584
  items: string[];
580
585
  size?: "md" | "lg"; // default "md"
581
586
  onChange: (activeIndex: number) => void;
582
587
  selectedIndex?: number;
588
+ maxItems?: number;
589
+ badges?: SegmentedControlBadgeConfig[];
583
590
  }
584
591
 
585
592
  export interface TimezonePickerProps {
@@ -1,58 +1,124 @@
1
- import React from "react";
1
+ import React, {useCallback, useState} from "react";
2
2
  import {Pressable, View} from "react-native";
3
3
 
4
+ import {Badge} from "./Badge";
4
5
  import {SegmentedControlProps} from "./Common";
5
6
  import {Heading} from "./Heading";
7
+ import {Icon} from "./Icon";
6
8
  import {useTheme} from "./Theme";
7
9
 
8
- export const SegmentedControl = ({
10
+ export const SegmentedControl: React.FC<SegmentedControlProps> = ({
9
11
  items,
10
12
  onChange = () => {},
11
13
  size = "md",
12
14
  selectedIndex,
13
- }: SegmentedControlProps) => {
15
+ maxItems,
16
+ badges = [],
17
+ }) => {
14
18
  const height = size === "md" ? 36 : 44;
15
19
  const {theme} = useTheme();
20
+ const [startIndex, setStartIndex] = useState(0);
21
+
22
+ const handlePrevious = useCallback(() => {
23
+ setStartIndex((prev) => Math.max(0, prev - (maxItems ?? 4)));
24
+ }, []);
25
+
26
+ const handleNext = useCallback(() => {
27
+ setStartIndex((prev) => Math.min(items.length - (maxItems ?? items.length), prev + (maxItems ?? 4)));
28
+ }, [items.length, maxItems]);
29
+
30
+ const visibleItems = maxItems ? items.slice(startIndex, startIndex + maxItems) : items;
31
+ const visibleBadges = maxItems ? badges.slice(startIndex, startIndex + maxItems) : badges;
32
+ const canScrollLeft = startIndex > 0;
33
+ const canScrollRight = maxItems ? startIndex + maxItems < items.length : false;
34
+ const shouldShowScrollButtons = maxItems ? maxItems < items.length : false;
35
+
16
36
  return (
17
37
  <View
18
38
  style={{
19
39
  display: "flex",
20
- flexGrow: 1,
21
40
  flexDirection: "row",
22
- flexShrink: 1,
23
41
  alignItems: "center",
24
- gap: 4,
25
- height,
26
- maxHeight: height,
27
- borderRadius: theme.primitives.radius3xl,
28
- borderColor: theme.primitives.neutral300,
29
- borderWidth: 3,
30
- backgroundColor: theme.primitives.neutral300,
42
+ gap: 8,
31
43
  }}
32
44
  >
33
- {items.map((item, index) => (
34
- <Pressable
35
- key={index}
36
- aria-role="button"
45
+ {Boolean(shouldShowScrollButtons) && (
46
+ <Pressable disabled={!canScrollLeft} onPress={handlePrevious}>
47
+ <Icon
48
+ color={canScrollLeft ? "linkLight" : "extraLight"}
49
+ iconName="chevron-left"
50
+ size="lg"
51
+ />
52
+ </Pressable>
53
+ )}
54
+ <View
55
+ style={{
56
+ display: "flex",
57
+ flexGrow: 1,
58
+ flexDirection: "row",
59
+ flexShrink: 1,
60
+ alignItems: "center",
61
+ height,
62
+ maxHeight: height,
63
+ backgroundColor: theme.primitives.neutral300,
64
+ overflow: "hidden",
65
+ borderRadius: theme.primitives.radius3xl,
66
+ }}
67
+ >
68
+ <View
37
69
  style={{
38
70
  display: "flex",
39
- paddingHorizontal: size === "md" ? theme.spacing.sm : theme.spacing.md,
40
- justifyContent: "center",
41
- alignItems: "center",
42
- height: "100%",
43
- flexBasis: 0,
44
- gap: 12,
71
+ flexDirection: "row",
72
+ gap: 4,
45
73
  flexGrow: 1,
46
- flexShrink: 0,
47
- borderRadius: theme.primitives.radius3xl,
48
- backgroundColor: index === selectedIndex ? theme.surface.base : undefined,
49
- overflow: "hidden",
74
+ paddingHorizontal: 4,
75
+ height: height - 4,
50
76
  }}
51
- onPress={() => onChange(index)}
52
77
  >
53
- <Heading size="sm">{item}</Heading>
78
+ {visibleItems.map((item, index) => {
79
+ const actualIndex = startIndex + index;
80
+ return (
81
+ <Pressable
82
+ key={actualIndex}
83
+ aria-role="button"
84
+ style={{
85
+ display: "flex",
86
+ paddingHorizontal: 2,
87
+ justifyContent: "center",
88
+ alignItems: "center",
89
+ height: "100%",
90
+ flexDirection: "row",
91
+ gap: 8,
92
+ flexGrow: 1,
93
+ flexBasis: 0,
94
+ borderRadius: theme.primitives.radius3xl,
95
+ backgroundColor: actualIndex === selectedIndex ? theme.surface.base : undefined,
96
+ overflow: "hidden",
97
+ }}
98
+ onPress={() => onChange(actualIndex)}
99
+ >
100
+ <Heading size="sm">{item}</Heading>
101
+ {visibleBadges[index] && (
102
+ <Badge
103
+ status={visibleBadges[index].status ?? "info"}
104
+ value={visibleBadges[index].count}
105
+ variant="numberOnly"
106
+ />
107
+ )}
108
+ </Pressable>
109
+ );
110
+ })}
111
+ </View>
112
+ </View>
113
+ {Boolean(shouldShowScrollButtons) && (
114
+ <Pressable disabled={!canScrollRight} onPress={handleNext}>
115
+ <Icon
116
+ color={canScrollRight ? "linkLight" : "extraLight"}
117
+ iconName="chevron-right"
118
+ size="lg"
119
+ />
54
120
  </Pressable>
55
- ))}
121
+ )}
56
122
  </View>
57
123
  );
58
124
  };