@bacon-pos/ui 0.9.0 → 0.10.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.
Files changed (123) hide show
  1. package/lib/commonjs/components/EmptyState/EmptyState.js +88 -0
  2. package/lib/commonjs/components/EmptyState/EmptyState.js.map +1 -0
  3. package/lib/commonjs/components/EmptyState/EmptyState.stories.js +46 -0
  4. package/lib/commonjs/components/EmptyState/EmptyState.stories.js.map +1 -0
  5. package/lib/commonjs/components/EmptyState/EmptyState.test.js +40 -0
  6. package/lib/commonjs/components/EmptyState/EmptyState.test.js.map +1 -0
  7. package/lib/commonjs/components/EmptyState/index.js +13 -0
  8. package/lib/commonjs/components/EmptyState/index.js.map +1 -0
  9. package/lib/commonjs/components/Sidebar/Sidebar.js +264 -0
  10. package/lib/commonjs/components/Sidebar/Sidebar.js.map +1 -0
  11. package/lib/commonjs/components/Sidebar/Sidebar.stories.js +229 -0
  12. package/lib/commonjs/components/Sidebar/Sidebar.stories.js.map +1 -0
  13. package/lib/commonjs/components/Sidebar/Sidebar.test.js +131 -0
  14. package/lib/commonjs/components/Sidebar/Sidebar.test.js.map +1 -0
  15. package/lib/commonjs/components/Sidebar/index.js +13 -0
  16. package/lib/commonjs/components/Sidebar/index.js.map +1 -0
  17. package/lib/commonjs/components/Table/DataTable.js +149 -0
  18. package/lib/commonjs/components/Table/DataTable.js.map +1 -0
  19. package/lib/commonjs/components/Table/DataTable.stories.js +214 -0
  20. package/lib/commonjs/components/Table/DataTable.stories.js.map +1 -0
  21. package/lib/commonjs/components/Table/DataTable.test.js +143 -0
  22. package/lib/commonjs/components/Table/DataTable.test.js.map +1 -0
  23. package/lib/commonjs/components/Table/Table.js +520 -0
  24. package/lib/commonjs/components/Table/Table.js.map +1 -0
  25. package/lib/commonjs/components/Table/Table.stories.js +385 -0
  26. package/lib/commonjs/components/Table/Table.stories.js.map +1 -0
  27. package/lib/commonjs/components/Table/Table.test.js +278 -0
  28. package/lib/commonjs/components/Table/Table.test.js.map +1 -0
  29. package/lib/commonjs/components/Table/index.js +20 -0
  30. package/lib/commonjs/components/Table/index.js.map +1 -0
  31. package/lib/commonjs/components/brand/BaconLogo.js +29 -0
  32. package/lib/commonjs/components/brand/BaconLogo.js.map +1 -0
  33. package/lib/commonjs/components/brand/index.js +7 -0
  34. package/lib/commonjs/components/brand/index.js.map +1 -1
  35. package/lib/commonjs/components/index.js +50 -17
  36. package/lib/commonjs/components/index.js.map +1 -1
  37. package/lib/module/components/EmptyState/EmptyState.js +84 -0
  38. package/lib/module/components/EmptyState/EmptyState.js.map +1 -0
  39. package/lib/module/components/EmptyState/EmptyState.stories.js +42 -0
  40. package/lib/module/components/EmptyState/EmptyState.stories.js.map +1 -0
  41. package/lib/module/components/EmptyState/EmptyState.test.js +40 -0
  42. package/lib/module/components/EmptyState/EmptyState.test.js.map +1 -0
  43. package/lib/module/components/EmptyState/index.js +4 -0
  44. package/lib/module/components/EmptyState/index.js.map +1 -0
  45. package/lib/module/components/Sidebar/Sidebar.js +260 -0
  46. package/lib/module/components/Sidebar/Sidebar.js.map +1 -0
  47. package/lib/module/components/Sidebar/Sidebar.stories.js +225 -0
  48. package/lib/module/components/Sidebar/Sidebar.stories.js.map +1 -0
  49. package/lib/module/components/Sidebar/Sidebar.test.js +131 -0
  50. package/lib/module/components/Sidebar/Sidebar.test.js.map +1 -0
  51. package/lib/module/components/Sidebar/index.js +4 -0
  52. package/lib/module/components/Sidebar/index.js.map +1 -0
  53. package/lib/module/components/Table/DataTable.js +145 -0
  54. package/lib/module/components/Table/DataTable.js.map +1 -0
  55. package/lib/module/components/Table/DataTable.stories.js +210 -0
  56. package/lib/module/components/Table/DataTable.stories.js.map +1 -0
  57. package/lib/module/components/Table/DataTable.test.js +143 -0
  58. package/lib/module/components/Table/DataTable.test.js.map +1 -0
  59. package/lib/module/components/Table/Table.js +516 -0
  60. package/lib/module/components/Table/Table.js.map +1 -0
  61. package/lib/module/components/Table/Table.stories.js +381 -0
  62. package/lib/module/components/Table/Table.stories.js.map +1 -0
  63. package/lib/module/components/Table/Table.test.js +278 -0
  64. package/lib/module/components/Table/Table.test.js.map +1 -0
  65. package/lib/module/components/Table/index.js +5 -0
  66. package/lib/module/components/Table/index.js.map +1 -0
  67. package/lib/module/components/brand/BaconLogo.js +25 -0
  68. package/lib/module/components/brand/BaconLogo.js.map +1 -0
  69. package/lib/module/components/brand/index.js +1 -0
  70. package/lib/module/components/brand/index.js.map +1 -1
  71. package/lib/module/components/index.js +3 -0
  72. package/lib/module/components/index.js.map +1 -1
  73. package/lib/typescript/commonjs/components/EmptyState/EmptyState.d.ts +30 -0
  74. package/lib/typescript/commonjs/components/EmptyState/EmptyState.d.ts.map +1 -0
  75. package/lib/typescript/commonjs/components/EmptyState/index.d.ts +3 -0
  76. package/lib/typescript/commonjs/components/EmptyState/index.d.ts.map +1 -0
  77. package/lib/typescript/commonjs/components/Sidebar/Sidebar.d.ts +57 -0
  78. package/lib/typescript/commonjs/components/Sidebar/Sidebar.d.ts.map +1 -0
  79. package/lib/typescript/commonjs/components/Sidebar/index.d.ts +3 -0
  80. package/lib/typescript/commonjs/components/Sidebar/index.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/components/Table/DataTable.d.ts +53 -0
  82. package/lib/typescript/commonjs/components/Table/DataTable.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/components/Table/Table.d.ts +85 -0
  84. package/lib/typescript/commonjs/components/Table/Table.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/components/Table/index.d.ts +5 -0
  86. package/lib/typescript/commonjs/components/Table/index.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/components/brand/BaconLogo.d.ts +13 -0
  88. package/lib/typescript/commonjs/components/brand/BaconLogo.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/components/brand/index.d.ts +1 -0
  90. package/lib/typescript/commonjs/components/brand/index.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/components/index.d.ts +3 -0
  92. package/lib/typescript/commonjs/components/index.d.ts.map +1 -1
  93. package/lib/typescript/module/components/EmptyState/EmptyState.d.ts +30 -0
  94. package/lib/typescript/module/components/EmptyState/EmptyState.d.ts.map +1 -0
  95. package/lib/typescript/module/components/EmptyState/index.d.ts +3 -0
  96. package/lib/typescript/module/components/EmptyState/index.d.ts.map +1 -0
  97. package/lib/typescript/module/components/Sidebar/Sidebar.d.ts +57 -0
  98. package/lib/typescript/module/components/Sidebar/Sidebar.d.ts.map +1 -0
  99. package/lib/typescript/module/components/Sidebar/index.d.ts +3 -0
  100. package/lib/typescript/module/components/Sidebar/index.d.ts.map +1 -0
  101. package/lib/typescript/module/components/Table/DataTable.d.ts +53 -0
  102. package/lib/typescript/module/components/Table/DataTable.d.ts.map +1 -0
  103. package/lib/typescript/module/components/Table/Table.d.ts +85 -0
  104. package/lib/typescript/module/components/Table/Table.d.ts.map +1 -0
  105. package/lib/typescript/module/components/Table/index.d.ts +5 -0
  106. package/lib/typescript/module/components/Table/index.d.ts.map +1 -0
  107. package/lib/typescript/module/components/brand/BaconLogo.d.ts +13 -0
  108. package/lib/typescript/module/components/brand/BaconLogo.d.ts.map +1 -0
  109. package/lib/typescript/module/components/brand/index.d.ts +1 -0
  110. package/lib/typescript/module/components/brand/index.d.ts.map +1 -1
  111. package/lib/typescript/module/components/index.d.ts +3 -0
  112. package/lib/typescript/module/components/index.d.ts.map +1 -1
  113. package/package.json +1 -1
  114. package/src/components/EmptyState/EmptyState.tsx +91 -0
  115. package/src/components/EmptyState/index.ts +2 -0
  116. package/src/components/Sidebar/Sidebar.tsx +320 -0
  117. package/src/components/Sidebar/index.ts +2 -0
  118. package/src/components/Table/DataTable.tsx +173 -0
  119. package/src/components/Table/Table.tsx +556 -0
  120. package/src/components/Table/index.ts +10 -0
  121. package/src/components/brand/BaconLogo.tsx +25 -0
  122. package/src/components/brand/index.ts +1 -0
  123. package/src/components/index.ts +3 -0
@@ -0,0 +1,85 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ export type ColumnAlign = 'left' | 'center' | 'right';
4
+ export type SortDirection = 'asc' | 'desc';
5
+ export interface TableSort {
6
+ key: string;
7
+ direction: SortDirection;
8
+ }
9
+ export interface TableColumn<T> {
10
+ /** Unique column id. */
11
+ key: string;
12
+ /** Header content (string or node). */
13
+ header: ReactNode;
14
+ /** Cell renderer. Defaults to `String(row[key])`. Return a string/number or any node (e.g. a `Badge`). */
15
+ render?: (row: T, index: number) => ReactNode;
16
+ /** Flex grow. Defaults to 1. Ignored when `width` is set. */
17
+ flex?: number;
18
+ /** Fixed column width in px. */
19
+ width?: number;
20
+ /** Text/content alignment. Defaults to `left`. */
21
+ align?: ColumnAlign;
22
+ /** Enable sorting on this column (needs `sortAccessor`). */
23
+ sortable?: boolean;
24
+ /** Value used to sort this column when sorting internally. */
25
+ sortAccessor?: (row: T) => string | number;
26
+ }
27
+ export interface TableProps<T> {
28
+ columns: TableColumn<T>[];
29
+ data: T[];
30
+ /** Stable key per row. */
31
+ keyExtractor: (row: T, index: number) => string;
32
+ /** Row press (non-expandable tables). Expandable rows toggle on press instead. */
33
+ onRowPress?: (row: T, index: number) => void;
34
+ /** Expandable (accordion) rows — mutually exclusive with `renderActions`. */
35
+ expandable?: boolean;
36
+ /** Content shown when a row is expanded. */
37
+ renderExpanded?: (row: T, index: number) => ReactNode;
38
+ /** One open row at a time (`single`) or many (`multiple`). Defaults to `single`. */
39
+ expansionMode?: 'single' | 'multiple';
40
+ /** Controlled expanded row keys. */
41
+ expandedKeys?: string[];
42
+ defaultExpandedKeys?: string[];
43
+ onExpandedChange?: (keys: string[]) => void;
44
+ /** Trailing per-row actions (e.g. edit/delete) — mutually exclusive with `expandable`. */
45
+ renderActions?: (row: T, index: number) => ReactNode;
46
+ /** Width reserved for the actions column. Defaults to 96. */
47
+ actionsWidth?: number;
48
+ /** Row selection via checkboxes. */
49
+ selectable?: boolean;
50
+ selectedKeys?: string[];
51
+ defaultSelectedKeys?: string[];
52
+ onSelectionChange?: (keys: string[]) => void;
53
+ /** Controlled sort. Omit for internal sorting via each column's `sortAccessor`. */
54
+ sort?: TableSort;
55
+ defaultSort?: TableSort;
56
+ onSortChange?: (sort: TableSort) => void;
57
+ /** Rounded, clipped container. Defaults to true. */
58
+ rounded?: boolean;
59
+ /** Cap the body height; rows scroll with the elegant indicator, header stays. */
60
+ maxHeight?: number;
61
+ scrollbarColor?: string;
62
+ /** Show a loading placeholder instead of rows. */
63
+ loading?: boolean;
64
+ /** Loading style: a centered `spinner` (default) or shimmering `skeleton` rows. */
65
+ loadingVariant?: 'spinner' | 'skeleton';
66
+ /** Text under the spinner. Defaults to "Loading data"; pass `null` to hide it. */
67
+ loadingText?: string | null;
68
+ /** Number of skeleton rows. Defaults to 5. */
69
+ skeletonRows?: number;
70
+ /** Shown when there is no data (and not loading). */
71
+ empty?: ReactNode;
72
+ /** Below this width the table renders each row as a card. Defaults to 640. */
73
+ mobileBreakpoint?: number;
74
+ style?: StyleProp<ViewStyle>;
75
+ }
76
+ /**
77
+ * Table — a flexible, token-driven data table (Figma "Table"). Columns define
78
+ * width/flex, alignment and cell renderers (text, `Badge`, anything). Supports
79
+ * sortable headers, checkbox selection, and accordion rows (expand on row press)
80
+ * OR a trailing actions column (the two are mutually exclusive). A sticky header
81
+ * with an elegant scrolling body (`maxHeight`), loading and empty states, and a
82
+ * card fallback below `mobileBreakpoint`.
83
+ */
84
+ export declare function Table<T>({ columns, data, keyExtractor, onRowPress, expandable, renderExpanded, expansionMode, expandedKeys, defaultExpandedKeys, onExpandedChange, renderActions, actionsWidth, selectable, selectedKeys, defaultSelectedKeys, onSelectionChange, sort, defaultSort, onSortChange, rounded, maxHeight, scrollbarColor, loading, loadingVariant, loadingText, skeletonRows, empty, mobileBreakpoint, style, }: TableProps<T>): import("react/jsx-runtime").JSX.Element;
85
+ //# sourceMappingURL=Table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../../../../src/components/Table/Table.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { Table } from './Table';
2
+ export type { TableProps, TableColumn, TableSort, ColumnAlign, SortDirection, } from './Table';
3
+ export { DataTable } from './DataTable';
4
+ export type { DataTableProps, DataTableTab, DataTablePagination } from './DataTable';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/Table/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface BaconLogoProps {
2
+ /** Rendered height in px; width follows the mark's aspect ratio. Defaults to 40. */
3
+ height?: number;
4
+ /** Mark color. Defaults to the Bacon red. */
5
+ color?: string;
6
+ }
7
+ /**
8
+ * The Bacon POS brand mark (the red "B"). Rendered at its natural aspect ratio
9
+ * and not tinted by default — the brand color is preserved. Used as the default
10
+ * logo in `Sidebar`.
11
+ */
12
+ export declare function BaconLogo({ height, color }: BaconLogoProps): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=BaconLogo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaconLogo.d.ts","sourceRoot":"","sources":["../../../../../src/components/brand/BaconLogo.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAW,EAAE,KAAiB,EAAE,EAAE,cAAc,2CAG3E"}
@@ -1,4 +1,5 @@
1
1
  export { Logo, type LogoProps, type LogoName } from './Logo';
2
+ export { BaconLogo, type BaconLogoProps } from './BaconLogo';
2
3
  export { Flag, type FlagProps, type FlagCountry } from './Flag';
3
4
  export { graphics, type GraphicName, type Graphic } from './registry';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/brand/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/brand/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC"}
@@ -9,6 +9,7 @@ export * from './CodeField';
9
9
  export * from './CodeInput';
10
10
  export * from './Dialog';
11
11
  export * from './Dropdown';
12
+ export * from './EmptyState';
12
13
  export * from './Icon';
13
14
  export * from './IconButton';
14
15
  export * from './Input';
@@ -20,9 +21,11 @@ export * from './ProgressBar';
20
21
  export * from './QuantityStepper';
21
22
  export * from './Radio';
22
23
  export * from './Select';
24
+ export * from './Sidebar';
23
25
  export * from './Spinner';
24
26
  export * from './Surface';
25
27
  export * from './Switch';
28
+ export * from './Table';
26
29
  export * from './Text';
27
30
  export * from './Toast';
28
31
  export * from './UserChip';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAE3B,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAE3B,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bacon-pos/ui",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Bacon POS design system — React Native component library",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
@@ -0,0 +1,91 @@
1
+ import { View, type StyleProp, type ViewStyle } from 'react-native';
2
+ import { useTheme } from '../../theme';
3
+ import { Button, type ButtonVariant } from '../Button/Button';
4
+ import { Icon } from '../Icon/Icon';
5
+ import type { IconName } from '../Icon/icons.generated';
6
+ import { Text } from '../Text/Text';
7
+
8
+ export interface EmptyStateAction {
9
+ label: string;
10
+ onPress: () => void;
11
+ icon?: IconName;
12
+ variant?: ButtonVariant;
13
+ }
14
+
15
+ export interface EmptyStateProps {
16
+ /** Icon shown in the badge above the title. */
17
+ icon?: IconName;
18
+ /** Title line. */
19
+ title: string;
20
+ /** Supporting description below the title. */
21
+ description?: string;
22
+ /** Optional call-to-action button. */
23
+ action?: EmptyStateAction;
24
+ /** Vertical padding around the block. Defaults to 32. */
25
+ paddingVertical?: number;
26
+ /** Override the container style. */
27
+ style?: StyleProp<ViewStyle>;
28
+ }
29
+
30
+ /**
31
+ * Empty state — a centered icon, title, description and optional action button.
32
+ * Used by `Table`/`DataTable` when there's no data, and reusable anywhere a
33
+ * "nothing here yet" message is needed.
34
+ */
35
+ export function EmptyState({
36
+ icon = 'no-photo',
37
+ title,
38
+ description,
39
+ action,
40
+ paddingVertical = 32,
41
+ style,
42
+ }: EmptyStateProps) {
43
+ const theme = useTheme();
44
+ const variant = action?.variant ?? 'inverse';
45
+ const actionIconColor =
46
+ variant === 'default'
47
+ ? theme.colors.action.primaryForeground
48
+ : variant === 'secondary'
49
+ ? theme.colors.action.secondaryForeground
50
+ : theme.colors.action.inverseForeground;
51
+
52
+ return (
53
+ <View style={[{ alignItems: 'center', gap: 12, paddingVertical, paddingHorizontal: 24 }, style]}>
54
+ <View
55
+ style={{
56
+ width: 48,
57
+ height: 48,
58
+ borderRadius: 24,
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ backgroundColor: theme.colors.background.subtle,
62
+ }}
63
+ >
64
+ <Icon name={icon} size={24} color="tertiary" />
65
+ </View>
66
+
67
+ <View style={{ alignItems: 'center', gap: 4 }}>
68
+ <Text variant="bodyLarge" weight="medium" color="primary" align="center">
69
+ {title}
70
+ </Text>
71
+ {description ? (
72
+ <Text variant="bodyMedium" color="tertiary" align="center" style={{ maxWidth: 420 }}>
73
+ {description}
74
+ </Text>
75
+ ) : null}
76
+ </View>
77
+
78
+ {action ? (
79
+ <View style={{ marginTop: 4 }}>
80
+ <Button
81
+ label={action.label}
82
+ variant={action.variant ?? 'inverse'}
83
+ size="sm"
84
+ onPress={action.onPress}
85
+ leftIcon={action.icon ? <Icon name={action.icon} size={20} color={actionIconColor} /> : undefined}
86
+ />
87
+ </View>
88
+ ) : null}
89
+ </View>
90
+ );
91
+ }
@@ -0,0 +1,2 @@
1
+ export { EmptyState } from './EmptyState';
2
+ export type { EmptyStateProps, EmptyStateAction } from './EmptyState';
@@ -0,0 +1,320 @@
1
+ import { useEffect, useRef, useState, type ReactNode } from 'react';
2
+ import {
3
+ Animated,
4
+ Easing,
5
+ Pressable,
6
+ View,
7
+ type StyleProp,
8
+ type ViewStyle,
9
+ } from 'react-native';
10
+ import { useTheme } from '../../theme';
11
+ import { BaconLogo } from '../brand/BaconLogo';
12
+ import { Icon } from '../Icon/Icon';
13
+ import type { IconName } from '../Icon/icons.generated';
14
+ import { IconButton } from '../IconButton/IconButton';
15
+ import { Text } from '../Text/Text';
16
+ import { ScrollArea } from '../_internal/ScrollArea';
17
+
18
+ export interface SidebarItem {
19
+ /** Stable key; also the active identifier. */
20
+ key: string;
21
+ /** Button text (hidden when collapsed). */
22
+ label: string;
23
+ /** Leading icon. When omitted, expanded reserves the icon space and collapsed shows the label's initial. */
24
+ icon?: IconName;
25
+ /** Optional icon shown when the item is active (e.g. a filled variant). */
26
+ activeIcon?: IconName;
27
+ /** Press handler. */
28
+ onPress?: () => void;
29
+ /** Dim and block the item. */
30
+ disabled?: boolean;
31
+ }
32
+
33
+ export interface SidebarProps {
34
+ /** Navigation items — rendered top-to-bottom. Defaults to none. */
35
+ items?: SidebarItem[];
36
+ /** Active item key (controlled). */
37
+ activeKey?: string;
38
+ /** Active item key for the uncontrolled case. */
39
+ defaultActiveKey?: string;
40
+ /** Called with the pressed item's key. */
41
+ onChange?: (key: string) => void;
42
+
43
+ /** Collapsed (icons only) state — controlled. */
44
+ collapsed?: boolean;
45
+ /** Initial collapsed state (uncontrolled). Defaults to false. */
46
+ defaultCollapsed?: boolean;
47
+ /** Called when the collapse state changes. */
48
+ onCollapsedChange?: (collapsed: boolean) => void;
49
+ /** Show the built-in expand/collapse toggle. Defaults to true. */
50
+ showToggle?: boolean;
51
+
52
+ /** Logo shown at the top. Defaults to the Bacon mark. */
53
+ logo?: ReactNode;
54
+ /** Bottom supporting text (e.g. a version). Defaults to none. */
55
+ footer?: string;
56
+
57
+ /** Expanded width. Defaults to 284 (Figma). */
58
+ expandedWidth?: number;
59
+ /** Collapsed width. Defaults to 122 (Figma). */
60
+ collapsedWidth?: number;
61
+ /** Scroll indicator color. Defaults to a subtle neutral. */
62
+ scrollbarColor?: string;
63
+ /** Override the container style. */
64
+ style?: StyleProp<ViewStyle>;
65
+ }
66
+
67
+ const ITEM_HEIGHT = 74;
68
+ const ITEM_GAP = 16;
69
+ const PADDING = 24;
70
+ const TOGGLE_SIZE = 40; // IconButton size "sm"
71
+
72
+ function SidebarButton({
73
+ item,
74
+ active,
75
+ collapsed,
76
+ onPress,
77
+ }: {
78
+ item: SidebarItem;
79
+ active: boolean;
80
+ collapsed: boolean;
81
+ onPress: () => void;
82
+ }) {
83
+ const theme = useTheme();
84
+ const [hovered, setHovered] = useState(false);
85
+ const disabled = item.disabled;
86
+
87
+ const backgroundColor = active
88
+ ? theme.colors.action.secondary
89
+ : hovered && !disabled
90
+ ? theme.colors.background.muted
91
+ : 'transparent';
92
+ const contentColor = active
93
+ ? theme.colors.action.secondaryForeground
94
+ : theme.colors.text.secondary;
95
+ const iconColor = active ? theme.colors.action.secondaryForeground : 'secondary';
96
+ const iconName = active && item.activeIcon ? item.activeIcon : item.icon;
97
+ const initial = item.label.trim().charAt(0).toUpperCase();
98
+
99
+ // Leading slot is always a fixed 24×24 box (flexShrink: 0) so the icon never
100
+ // shrinks next to a long label and labels stay aligned with or without an icon.
101
+ // No icon → reserve the space when expanded; show the initial when collapsed.
102
+ const leadingBox: ViewStyle = {
103
+ width: 24,
104
+ height: 24,
105
+ flexShrink: 0,
106
+ alignItems: 'center',
107
+ justifyContent: 'center',
108
+ };
109
+ const leading = (
110
+ <View style={leadingBox}>
111
+ {iconName ? (
112
+ <Icon name={iconName} size={24} color={iconColor} />
113
+ ) : collapsed ? (
114
+ <Text variant="bodyLarge" weight="medium" color={contentColor}>
115
+ {initial}
116
+ </Text>
117
+ ) : null}
118
+ </View>
119
+ );
120
+
121
+ return (
122
+ <Pressable
123
+ onPress={disabled ? undefined : onPress}
124
+ onHoverIn={() => setHovered(true)}
125
+ onHoverOut={() => setHovered(false)}
126
+ disabled={disabled}
127
+ accessibilityRole="tab"
128
+ accessibilityState={{ selected: active, disabled }}
129
+ accessibilityLabel={item.label}
130
+ style={{
131
+ flexDirection: 'row',
132
+ alignItems: 'center',
133
+ justifyContent: collapsed ? 'center' : 'flex-start',
134
+ gap: 10,
135
+ height: ITEM_HEIGHT,
136
+ paddingHorizontal: PADDING,
137
+ borderRadius: theme.radii.pill,
138
+ borderWidth: active ? 1 : 0,
139
+ borderColor: active ? theme.colors.border.default : 'transparent',
140
+ backgroundColor,
141
+ opacity: disabled ? 0.5 : 1,
142
+ }}
143
+ >
144
+ {leading}
145
+ {!collapsed ? (
146
+ <Text
147
+ variant="bodyLarge"
148
+ weight="medium"
149
+ color={contentColor}
150
+ numberOfLines={1}
151
+ ellipsizeMode="tail"
152
+ style={{ flexShrink: 1, minWidth: 0 }}
153
+ >
154
+ {item.label}
155
+ </Text>
156
+ ) : null}
157
+ </Pressable>
158
+ );
159
+ }
160
+
161
+ /**
162
+ * Sidebar — the app's main navigation drawer (Figma "Sidebar"). Expands/collapses
163
+ * between a labelled and an icon-only rail (animated width). The logo defaults to
164
+ * the Bacon mark (override via `logo`); an optional `footer` shows supporting text
165
+ * at the bottom. Items are dynamic buttons (icon + label via props); the list
166
+ * scrolls with an elegant auto-hiding indicator when it overflows, leaving the
167
+ * footer pinned. Buttons have hover and active states to mark the current section.
168
+ */
169
+ export function Sidebar({
170
+ items = [],
171
+ activeKey,
172
+ defaultActiveKey,
173
+ onChange,
174
+ collapsed: collapsedProp,
175
+ defaultCollapsed = false,
176
+ onCollapsedChange,
177
+ showToggle = true,
178
+ logo,
179
+ footer,
180
+ expandedWidth = 284,
181
+ collapsedWidth = 122,
182
+ scrollbarColor,
183
+ style,
184
+ }: SidebarProps) {
185
+ const theme = useTheme();
186
+
187
+ const activeControlled = activeKey !== undefined;
188
+ const [activeInternal, setActiveInternal] = useState(defaultActiveKey);
189
+ const active = activeControlled ? activeKey : activeInternal;
190
+
191
+ const collapseControlled = collapsedProp !== undefined;
192
+ const [collapsedInternal, setCollapsedInternal] = useState(defaultCollapsed);
193
+ const collapsed = collapseControlled ? collapsedProp : collapsedInternal;
194
+
195
+ const width = useRef(new Animated.Value(collapsed ? collapsedWidth : expandedWidth)).current;
196
+ // `visualCollapsed` drives the layout (labels, alignment, footer). It lags on
197
+ // collapse — labels stay mounted (clipped by overflow) until the width finishes
198
+ // shrinking — and leads on expand, so both directions animate without a jump.
199
+ const [visualCollapsed, setVisualCollapsed] = useState(collapsed);
200
+ useEffect(() => {
201
+ if (!collapsed) setVisualCollapsed(false);
202
+ const anim = Animated.timing(width, {
203
+ toValue: collapsed ? collapsedWidth : expandedWidth,
204
+ duration: 220,
205
+ easing: Easing.out(Easing.ease),
206
+ useNativeDriver: false, // width
207
+ });
208
+ anim.start(({ finished }) => {
209
+ if (finished && collapsed) setVisualCollapsed(true);
210
+ });
211
+ return () => anim.stop();
212
+ }, [collapsed, collapsedWidth, expandedWidth, width]);
213
+
214
+ // The toggle arrow spins in place (0° → 180°) instead of moving, so collapsing
215
+ // never nudges it. Driven by intent (`collapsed`), in sync with the width.
216
+ const arrowRot = useRef(new Animated.Value(collapsed ? 1 : 0)).current;
217
+ useEffect(() => {
218
+ const anim = Animated.timing(arrowRot, {
219
+ toValue: collapsed ? 1 : 0,
220
+ duration: 220,
221
+ easing: Easing.out(Easing.ease),
222
+ useNativeDriver: true,
223
+ });
224
+ anim.start();
225
+ return () => anim.stop();
226
+ }, [collapsed, arrowRot]);
227
+ const arrowRotate = arrowRot.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'] });
228
+
229
+ // Glide the toggle from flush-left (expanded) to centered on the icon axis
230
+ // (collapsed), tracking the width so it never jumps.
231
+ const toggleTranslateX = width.interpolate({
232
+ inputRange: [collapsedWidth, expandedWidth],
233
+ outputRange: [Math.max(0, (collapsedWidth - 2 * PADDING - TOGGLE_SIZE) / 2), 0],
234
+ extrapolate: 'clamp',
235
+ });
236
+
237
+ const selectItem = (item: SidebarItem) => {
238
+ if (!activeControlled) setActiveInternal(item.key);
239
+ item.onPress?.();
240
+ onChange?.(item.key);
241
+ };
242
+
243
+ const toggleCollapsed = () => {
244
+ const next = !collapsed;
245
+ if (!collapseControlled) setCollapsedInternal(next);
246
+ onCollapsedChange?.(next);
247
+ };
248
+
249
+ const logoNode = logo ?? <BaconLogo height={visualCollapsed ? 36 : 40} />;
250
+ const toggle = showToggle ? (
251
+ <Animated.View style={{ transform: [{ translateX: toggleTranslateX }] }}>
252
+ <Animated.View style={{ transform: [{ rotate: arrowRotate }] }}>
253
+ <IconButton
254
+ icon="arrow-left"
255
+ accessibilityLabel={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
256
+ variant="ghost"
257
+ size="sm"
258
+ onPress={toggleCollapsed}
259
+ />
260
+ </Animated.View>
261
+ </Animated.View>
262
+ ) : null;
263
+
264
+ return (
265
+ <Animated.View
266
+ style={[
267
+ {
268
+ width,
269
+ height: '100%',
270
+ backgroundColor: theme.colors.background.default,
271
+ paddingTop: 48,
272
+ paddingBottom: PADDING,
273
+ paddingHorizontal: PADDING,
274
+ overflow: 'hidden',
275
+ },
276
+ style,
277
+ ]}
278
+ >
279
+ {/* Logo */}
280
+ <View
281
+ style={{
282
+ minHeight: 56,
283
+ alignItems: visualCollapsed ? 'center' : 'flex-start',
284
+ justifyContent: 'center',
285
+ }}
286
+ >
287
+ {logoNode}
288
+ </View>
289
+
290
+ {/* Tabs (scrolls when overflowing; footer stays pinned below) */}
291
+ <ScrollArea
292
+ style={{ flex: 1, marginTop: 32 }}
293
+ indicatorColor={scrollbarColor ?? theme.colors.border.strong}
294
+ contentContainerStyle={{ gap: ITEM_GAP, paddingBottom: 8 }}
295
+ >
296
+ {items.map((item) => (
297
+ <SidebarButton
298
+ key={item.key}
299
+ item={item}
300
+ active={item.key === active}
301
+ collapsed={visualCollapsed}
302
+ onPress={() => selectItem(item)}
303
+ />
304
+ ))}
305
+ </ScrollArea>
306
+
307
+ {/* Bottom: supporting footer + fixed collapse/expand toggle */}
308
+ <View style={{ marginTop: 28, gap: 16 }}>
309
+ {footer && !visualCollapsed ? (
310
+ <Text variant="bodySmall" color="tertiary" numberOfLines={1} style={{ marginLeft: 8 }}>
311
+ {footer}
312
+ </Text>
313
+ ) : null}
314
+ {toggle ? (
315
+ <View style={{ alignItems: 'flex-start' }}>{toggle}</View>
316
+ ) : null}
317
+ </View>
318
+ </Animated.View>
319
+ );
320
+ }
@@ -0,0 +1,2 @@
1
+ export { Sidebar } from './Sidebar';
2
+ export type { SidebarProps, SidebarItem } from './Sidebar';