@flipdish/ui-library 0.14.2 → 0.15.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 CHANGED
@@ -100,7 +100,7 @@ component), see [Getting Started → Installation (React
100
100
  Native)](https://flipdishbytes.github.io/flipdish-design-system/?path=/docs/getting-started-installation-react-native--docs).
101
101
  The summary below covers the essentials.
102
102
 
103
- **Available on React Native today:** `Avatar`, plus the `cx`, `testIdUtils` and
103
+ **Available on React Native today:** `Avatar`, `Button`, plus the `cx`, `testIdUtils` and
104
104
  `isReactComponent` utilities. Storybook is the live reference: each component's docs page
105
105
  carries a **React Native** section listing exactly which props that platform supports, and
106
106
  the **Platform** switch in the toolbar re-renders its stories with the React Native
@@ -141,7 +141,7 @@ In your app's `global.css` — the file you import from your root component and
141
141
  ```
142
142
 
143
143
  `styles.native` contributes exactly one thing: `@source "./native"`, which tells Tailwind to
144
- scan this package's React Native build output so the class strings inside `Avatar` (and every
144
+ scan this package's React Native build output so the class strings inside `Avatar`, `Button` (and every
145
145
  component that follows) generate utilities. Without it those components render unstyled —
146
146
  Tailwind ignores `node_modules` unless a source is declared explicitly.
147
147
 
@@ -0,0 +1,90 @@
1
+ /**
2
+ * React Native–safe Button class strings (NativeWind).
3
+ *
4
+ * Mirrors the web variant keys in `index.tsx` (`sizes` / `colors`) but drops
5
+ * web-only utilities (`before:`, `hover:`, `focus-visible:`, `in-data-input-wrapper:`,
6
+ * `*:data-icon:`, etc.). Pressed equivalents of web `hover:` live under `pressed`.
7
+ *
8
+ * InputGroup nesting (`in-data-input-wrapper:*`) is omitted until a native InputGroup exists.
9
+ */
10
+ export declare const styles: {
11
+ common: {
12
+ root: string;
13
+ icon: string;
14
+ };
15
+ sizes: {
16
+ xs: {
17
+ root: string;
18
+ iconOnly: string;
19
+ linkRoot: string;
20
+ };
21
+ sm: {
22
+ root: string;
23
+ iconOnly: string;
24
+ linkRoot: string;
25
+ };
26
+ md: {
27
+ root: string;
28
+ iconOnly: string;
29
+ linkRoot: string;
30
+ };
31
+ lg: {
32
+ root: string;
33
+ iconOnly: string;
34
+ linkRoot: string;
35
+ };
36
+ xl: {
37
+ root: string;
38
+ iconOnly: string;
39
+ linkRoot: string;
40
+ };
41
+ };
42
+ colors: {
43
+ primary: {
44
+ root: string;
45
+ pressed: string;
46
+ loading: string;
47
+ };
48
+ secondary: {
49
+ root: string;
50
+ pressed: string;
51
+ loading: string;
52
+ };
53
+ tertiary: {
54
+ root: string;
55
+ pressed: string;
56
+ loading: string;
57
+ };
58
+ 'link-color': {
59
+ root: string;
60
+ pressed: string;
61
+ loading: string;
62
+ };
63
+ 'link-gray': {
64
+ root: string;
65
+ pressed: string;
66
+ loading: string;
67
+ };
68
+ 'primary-destructive': {
69
+ root: string;
70
+ pressed: string;
71
+ loading: string;
72
+ };
73
+ 'secondary-destructive': {
74
+ root: string;
75
+ pressed: string;
76
+ loading: string;
77
+ };
78
+ 'tertiary-destructive': {
79
+ root: string;
80
+ pressed: string;
81
+ loading: string;
82
+ };
83
+ 'link-destructive': {
84
+ root: string;
85
+ pressed: string;
86
+ loading: string;
87
+ };
88
+ };
89
+ };
90
+ //# sourceMappingURL=Button.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/Button/Button.styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+ElB,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * React Native–safe Button class strings (NativeWind).
3
+ *
4
+ * Mirrors the web variant keys in `index.tsx` (`sizes` / `colors`) but drops
5
+ * web-only utilities (`before:`, `hover:`, `focus-visible:`, `in-data-input-wrapper:`,
6
+ * `*:data-icon:`, etc.). Pressed equivalents of web `hover:` live under `pressed`.
7
+ *
8
+ * InputGroup nesting (`in-data-input-wrapper:*`) is omitted until a native InputGroup exists.
9
+ */
10
+ export declare const styles: {
11
+ common: {
12
+ root: string;
13
+ icon: string;
14
+ };
15
+ sizes: {
16
+ xs: {
17
+ root: string;
18
+ iconOnly: string;
19
+ linkRoot: string;
20
+ };
21
+ sm: {
22
+ root: string;
23
+ iconOnly: string;
24
+ linkRoot: string;
25
+ };
26
+ md: {
27
+ root: string;
28
+ iconOnly: string;
29
+ linkRoot: string;
30
+ };
31
+ lg: {
32
+ root: string;
33
+ iconOnly: string;
34
+ linkRoot: string;
35
+ };
36
+ xl: {
37
+ root: string;
38
+ iconOnly: string;
39
+ linkRoot: string;
40
+ };
41
+ };
42
+ colors: {
43
+ primary: {
44
+ root: string;
45
+ pressed: string;
46
+ loading: string;
47
+ };
48
+ secondary: {
49
+ root: string;
50
+ pressed: string;
51
+ loading: string;
52
+ };
53
+ tertiary: {
54
+ root: string;
55
+ pressed: string;
56
+ loading: string;
57
+ };
58
+ 'link-color': {
59
+ root: string;
60
+ pressed: string;
61
+ loading: string;
62
+ };
63
+ 'link-gray': {
64
+ root: string;
65
+ pressed: string;
66
+ loading: string;
67
+ };
68
+ 'primary-destructive': {
69
+ root: string;
70
+ pressed: string;
71
+ loading: string;
72
+ };
73
+ 'secondary-destructive': {
74
+ root: string;
75
+ pressed: string;
76
+ loading: string;
77
+ };
78
+ 'tertiary-destructive': {
79
+ root: string;
80
+ pressed: string;
81
+ loading: string;
82
+ };
83
+ 'link-destructive': {
84
+ root: string;
85
+ pressed: string;
86
+ loading: string;
87
+ };
88
+ };
89
+ };
90
+ //# sourceMappingURL=Button.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/atoms/Button/Button.styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+ElB,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * React Native–safe Button class strings (NativeWind).
3
+ *
4
+ * Mirrors the web variant keys in `index.tsx` (`sizes` / `colors`) but drops
5
+ * web-only utilities (`before:`, `hover:`, `focus-visible:`, `in-data-input-wrapper:`,
6
+ * `*:data-icon:`, etc.). Pressed equivalents of web `hover:` live under `pressed`.
7
+ *
8
+ * InputGroup nesting (`in-data-input-wrapper:*`) is omitted until a native InputGroup exists.
9
+ */
10
+ const styles = {
11
+ common: {
12
+ root: 'relative flex flex-row items-center justify-center',
13
+ icon: 'shrink-0',
14
+ },
15
+ sizes: {
16
+ xs: {
17
+ root: 'gap-1 rounded-lg px-2.5 py-1.5',
18
+ iconOnly: 'p-2',
19
+ linkRoot: 'gap-1 p-0',
20
+ },
21
+ sm: {
22
+ root: 'gap-1 rounded-lg px-3 py-2',
23
+ iconOnly: 'p-2',
24
+ linkRoot: 'gap-1 p-0',
25
+ },
26
+ md: {
27
+ root: 'gap-1 rounded-lg px-3.5 py-2.5',
28
+ iconOnly: 'p-2.5',
29
+ linkRoot: 'gap-1 p-0',
30
+ },
31
+ lg: {
32
+ root: 'gap-1.5 rounded-lg px-4 py-2.5',
33
+ iconOnly: 'p-3',
34
+ linkRoot: 'gap-1.5 p-0',
35
+ },
36
+ xl: {
37
+ root: 'gap-1.5 rounded-lg px-4.5 py-3',
38
+ iconOnly: 'p-3.5',
39
+ linkRoot: 'gap-1.5 p-0',
40
+ },
41
+ },
42
+ colors: {
43
+ primary: {
44
+ root: 'bg-brand-solid shadow-xs-skeuomorphic ring-1 ring-transparent ring-inset',
45
+ pressed: 'bg-brand-solid_hover',
46
+ loading: 'bg-brand-solid_hover',
47
+ },
48
+ secondary: {
49
+ root: 'bg-primary shadow-xs-skeuomorphic ring-1 ring-primary ring-inset',
50
+ pressed: 'bg-primary_hover',
51
+ loading: 'bg-primary_hover',
52
+ },
53
+ tertiary: {
54
+ root: '',
55
+ pressed: 'bg-primary_hover',
56
+ loading: 'bg-primary_hover',
57
+ },
58
+ 'link-color': {
59
+ root: 'justify-start rounded p-0',
60
+ pressed: '',
61
+ loading: '',
62
+ },
63
+ 'link-gray': {
64
+ root: 'justify-start rounded p-0',
65
+ pressed: '',
66
+ loading: '',
67
+ },
68
+ 'primary-destructive': {
69
+ root: 'bg-error-solid shadow-xs-skeuomorphic ring-1 ring-transparent ring-inset',
70
+ pressed: 'bg-error-solid_hover',
71
+ loading: 'bg-error-solid_hover',
72
+ },
73
+ 'secondary-destructive': {
74
+ root: 'bg-primary shadow-xs-skeuomorphic ring-1 ring-error_subtle ring-inset',
75
+ pressed: 'bg-error-primary',
76
+ loading: 'bg-error-primary',
77
+ },
78
+ 'tertiary-destructive': {
79
+ root: '',
80
+ pressed: 'bg-error-primary',
81
+ loading: 'bg-error-primary',
82
+ },
83
+ 'link-destructive': {
84
+ root: 'justify-start rounded p-0',
85
+ pressed: '',
86
+ loading: '',
87
+ },
88
+ },
89
+ };
90
+
91
+ export { styles };
92
+ //# sourceMappingURL=Button.styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Button.styles.js","sources":["../../../../../../src/components/atoms/Button/Button.styles.ts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;;;;;;;;AAQG;AACI,MAAM,MAAM,GAAG;AACpB,IAAA,MAAM,EAAE;AACN,QAAA,IAAI,EAAE,oDAAoD;AAC1D,QAAA,IAAI,EAAE,UAAU;AACjB,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,EAAE,EAAE;AACF,YAAA,IAAI,EAAE,gCAAgC;AACtC,YAAA,QAAQ,EAAE,KAAK;AACf,YAAA,QAAQ,EAAE,WAAW;AACtB,SAAA;AACD,QAAA,EAAE,EAAE;AACF,YAAA,IAAI,EAAE,4BAA4B;AAClC,YAAA,QAAQ,EAAE,KAAK;AACf,YAAA,QAAQ,EAAE,WAAW;AACtB,SAAA;AACD,QAAA,EAAE,EAAE;AACF,YAAA,IAAI,EAAE,gCAAgC;AACtC,YAAA,QAAQ,EAAE,OAAO;AACjB,YAAA,QAAQ,EAAE,WAAW;AACtB,SAAA;AACD,QAAA,EAAE,EAAE;AACF,YAAA,IAAI,EAAE,gCAAgC;AACtC,YAAA,QAAQ,EAAE,KAAK;AACf,YAAA,QAAQ,EAAE,aAAa;AACxB,SAAA;AACD,QAAA,EAAE,EAAE;AACF,YAAA,IAAI,EAAE,gCAAgC;AACtC,YAAA,QAAQ,EAAE,OAAO;AACjB,YAAA,QAAQ,EAAE,aAAa;AACxB,SAAA;AACF,KAAA;AACD,IAAA,MAAM,EAAE;AACN,QAAA,OAAO,EAAE;AACP,YAAA,IAAI,EAAE,0EAA0E;AAChF,YAAA,OAAO,EAAE,sBAAsB;AAC/B,YAAA,OAAO,EAAE,sBAAsB;AAChC,SAAA;AACD,QAAA,SAAS,EAAE;AACT,YAAA,IAAI,EAAE,kEAAkE;AACxE,YAAA,OAAO,EAAE,kBAAkB;AAC3B,YAAA,OAAO,EAAE,kBAAkB;AAC5B,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,IAAI,EAAE,EAAE;AACR,YAAA,OAAO,EAAE,kBAAkB;AAC3B,YAAA,OAAO,EAAE,kBAAkB;AAC5B,SAAA;AACD,QAAA,YAAY,EAAE;AACZ,YAAA,IAAI,EAAE,2BAA2B;AACjC,YAAA,OAAO,EAAE,EAAE;AACX,YAAA,OAAO,EAAE,EAAE;AACZ,SAAA;AACD,QAAA,WAAW,EAAE;AACX,YAAA,IAAI,EAAE,2BAA2B;AACjC,YAAA,OAAO,EAAE,EAAE;AACX,YAAA,OAAO,EAAE,EAAE;AACZ,SAAA;AACD,QAAA,qBAAqB,EAAE;AACrB,YAAA,IAAI,EAAE,0EAA0E;AAChF,YAAA,OAAO,EAAE,sBAAsB;AAC/B,YAAA,OAAO,EAAE,sBAAsB;AAChC,SAAA;AACD,QAAA,uBAAuB,EAAE;AACvB,YAAA,IAAI,EAAE,uEAAuE;AAC7E,YAAA,OAAO,EAAE,kBAAkB;AAC3B,YAAA,OAAO,EAAE,kBAAkB;AAC5B,SAAA;AACD,QAAA,sBAAsB,EAAE;AACtB,YAAA,IAAI,EAAE,EAAE;AACR,YAAA,OAAO,EAAE,kBAAkB;AAC3B,YAAA,OAAO,EAAE,kBAAkB;AAC5B,SAAA;AACD,QAAA,kBAAkB,EAAE;AAClB,YAAA,IAAI,EAAE,2BAA2B;AACjC,YAAA,OAAO,EAAE,EAAE;AACX,YAAA,OAAO,EAAE,EAAE;AACZ,SAAA;AACF,KAAA;;;;;"}
@@ -1,12 +1,64 @@
1
+ import { type FC, type ReactElement, type ReactNode } from 'react';
2
+ import { type PressableProps } from 'react-native';
3
+ import { styles } from './Button.styles';
4
+ type IconProp = FC<{
5
+ className?: string;
6
+ size?: number;
7
+ color?: string;
8
+ }> | ReactNode;
9
+ export type ButtonSize = keyof typeof styles.sizes;
10
+ export type ButtonColor = keyof typeof styles.colors;
11
+ interface CommonProps {
12
+ /** Disables the button and shows a disabled state */
13
+ isDisabled?: boolean;
14
+ /** Shows a loading spinner and disables the button */
15
+ isLoading?: boolean;
16
+ /** The size variant of the button */
17
+ size?: ButtonSize;
18
+ /** The color variant of the button */
19
+ color?: ButtonColor;
20
+ /** Icon component or element to show before the text */
21
+ iconLeading?: IconProp;
22
+ /** Icon component or element to show after the text */
23
+ iconTrailing?: IconProp;
24
+ /** Removes horizontal padding from the text content */
25
+ noTextPadding?: boolean;
26
+ /** When true, keeps the text visible during loading state */
27
+ showTextWhileLoading?: boolean;
28
+ /** Overrides the generated `testID` (defaults to `fd-button`). */
29
+ 'data-testid'?: string;
30
+ /**
31
+ * Web-style accessible name. Accepted as an alias of `accessibilityLabel` so shared
32
+ * stories (and cross-platform call sites) can pass `aria-label` on both platforms.
33
+ */
34
+ 'aria-label'?: string;
35
+ children?: ReactNode;
36
+ className?: string;
37
+ }
38
+ type PressBits = Pick<PressableProps, 'onPress' | 'hitSlop' | 'accessibilityLabel' | 'accessibilityHint'>;
39
+ /** Props for the button variant (non-link) */
40
+ export type ButtonProps = CommonProps & PressBits;
41
+ /** Props for the link variant — pass `href` to open a URL via `Linking.openURL` */
42
+ export type LinkProps = CommonProps & PressBits & {
43
+ href: string;
44
+ };
45
+ export type Props = ButtonProps | LinkProps;
1
46
  /**
2
- * `Button` has no React Native implementation.
47
+ * React Native `Button` mirrors the web prop surface (sizes, colors, icons, loading,
48
+ * `href`) with platform adaptations:
3
49
  *
4
- * This is a generated types-only stub (scripts/emit-native-stubs.mjs). It exists so
5
- * that importing `@flipdish/ui-library/components/atoms/Button` from React Native fails at
6
- * compile time rather than resolving to the web build's types. There is no
7
- * matching `index.js`, so Metro cannot bundle this module either.
50
+ * - `Pressable` instead of react-aria-components `Button` / `Link`
51
+ * - pressed styles instead of `hover:`
52
+ * - `ActivityIndicator` instead of the web SVG spinner
53
+ * - `href` opens via `Linking.openURL` (disabled/loading skips navigation)
54
+ * - no `before:` inset border gradient; no InputGroup nesting selectors
55
+ * - label colour/size via `style` on `Text` (NativeWind Text `className` bug — see Avatar)
8
56
  *
9
- * To make `Button` available on React Native, add an `index.native.tsx` beside
10
- * its `index.tsx` — the build picks it up automatically and this stub disappears.
57
+ * @summary action trigger or URL control (React Native)
11
58
  */
12
- export declare const __NO_REACT_NATIVE_IMPLEMENTATION__: never;
59
+ export declare const Button: {
60
+ (props: LinkProps): ReactElement;
61
+ (props: ButtonProps): ReactElement;
62
+ };
63
+ export {};
64
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/atoms/Button/index.native.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,EAAkB,KAAK,YAAY,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAC7F,OAAO,EAAyC,KAAK,cAAc,EAAc,MAAM,cAAc,CAAC;AACtG,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,KAAK,QAAQ,GAAG,EAAE,CAAC;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAAC;AAEtF,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,MAAM,CAAC,KAAK,CAAC;AACnD,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC;AAErD,UAAU,WAAW;IACnB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qCAAqC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,sCAAsC;IACtC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,wDAAwD;IACxD,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,uDAAuD;IACvD,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,uDAAuD;IACvD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,KAAK,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,oBAAoB,GAAG,mBAAmB,CAAC,CAAC;AAE1G,8CAA8C;AAC9C,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;AAElD,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;AA4F5C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,EAAE;IACnB,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAAC;IACjC,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAAC;CAsGpC,CAAC"}
@@ -0,0 +1,147 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { cx } from '../../../utilities/cx/index.js';
3
+ import { isReactComponent } from '../../../utilities/isReactComponent/index.js';
4
+ import { useResolvedTestId } from '../../../utilities/testIdUtils/index.js';
5
+ import { useState, isValidElement } from 'react';
6
+ import { Pressable, View, ActivityIndicator, Text, Linking } from 'react-native';
7
+ import { styles } from './Button.styles.js';
8
+
9
+ const LINK_COLORS = ['link-gray', 'link-color', 'link-destructive'];
10
+ /**
11
+ * Icon pixel sizes — web uses `size-4` (16) for `xs` and `size-5` (20) otherwise.
12
+ * RN icon components take a numeric `size`, not a className.
13
+ */
14
+ const ICON_PIXEL_SIZE = {
15
+ xs: 16,
16
+ sm: 20,
17
+ md: 20,
18
+ lg: 20,
19
+ xl: 20,
20
+ };
21
+ /**
22
+ * Label typography via plain `style` rather than `className` on `Text`.
23
+ *
24
+ * Same workaround as Avatar: `className` on `Text` silently fails under the current
25
+ * `react-native-css` preview, while `style` works. Mirrors web `text-sm` (xs–md) /
26
+ * `text-md` (lg–xl) + `font-semibold`. Revisit once upstream Text className works.
27
+ */
28
+ const LABEL_TEXT_SIZE = {
29
+ xs: { fontSize: 14, lineHeight: 20 },
30
+ sm: { fontSize: 14, lineHeight: 20 },
31
+ md: { fontSize: 14, lineHeight: 20 },
32
+ lg: { fontSize: 16, lineHeight: 24 },
33
+ xl: { fontSize: 16, lineHeight: 24 },
34
+ };
35
+ /**
36
+ * Light-mode hex approximations of semantic text tokens for the Text `style` workaround.
37
+ * Hardcoded like Avatar — className can't resolve tokens on Text yet, and the consuming
38
+ * app does not support dark mode elsewhere yet.
39
+ */
40
+ const LABEL_TEXT_COLOR = {
41
+ primary: '#ffffff',
42
+ secondary: '#404040', // text-secondary → neutral-700
43
+ tertiary: '#525252', // text-tertiary → neutral-600
44
+ 'link-color': '#085eb1', // text-brand-secondary → brand-700
45
+ 'link-gray': '#525252', // text-tertiary
46
+ 'primary-destructive': '#ffffff',
47
+ 'secondary-destructive': '#dc2626', // text-error-primary → red-600
48
+ 'tertiary-destructive': '#dc2626',
49
+ 'link-destructive': '#dc2626',
50
+ };
51
+ const LABEL_TEXT_COLOR_PRESSED = {
52
+ secondary: '#262626', // text-secondary_hover → neutral-800
53
+ tertiary: '#404040', // text-tertiary_hover → neutral-700
54
+ 'link-color': '#074c8f', // text-brand-secondary_hover → brand-800
55
+ 'link-gray': '#404040',
56
+ 'secondary-destructive': '#b91c1c', // text-error-primary_hover → red-700
57
+ 'tertiary-destructive': '#b91c1c',
58
+ 'link-destructive': '#b91c1c',
59
+ };
60
+ /** Icon tint — mirrors web `*:data-icon:text-…` (light-mode hex). */
61
+ const ICON_COLOR = {
62
+ primary: 'rgba(255,255,255,0.6)',
63
+ secondary: '#a3a3a3', // fg-quaternary → neutral-400
64
+ tertiary: '#a3a3a3',
65
+ 'link-color': '#1c8bf3', // fg-brand-secondary → brand-500
66
+ 'link-gray': '#a3a3a3',
67
+ 'primary-destructive': 'rgba(255,255,255,0.6)',
68
+ 'secondary-destructive': '#f04438', // approximate fg-error-secondary
69
+ 'tertiary-destructive': '#f04438',
70
+ 'link-destructive': '#f04438',
71
+ };
72
+ const ICON_COLOR_PRESSED = {
73
+ primary: 'rgba(255,255,255,0.7)',
74
+ secondary: '#737373', // fg-quaternary_hover → neutral-500
75
+ tertiary: '#737373',
76
+ 'link-color': '#0b75d7', // fg-brand-secondary_hover → brand-600
77
+ 'link-gray': '#737373',
78
+ 'primary-destructive': 'rgba(255,255,255,0.7)',
79
+ 'secondary-destructive': '#d92d20',
80
+ 'tertiary-destructive': '#d92d20',
81
+ 'link-destructive': '#d92d20',
82
+ };
83
+ const renderIcon = (icon, size, color, className) => {
84
+ if (isValidElement(icon))
85
+ return icon;
86
+ if (isReactComponent(icon)) {
87
+ const Icon = icon;
88
+ return jsx(Icon, { className: className, size: ICON_PIXEL_SIZE[size], color: color });
89
+ }
90
+ return null;
91
+ };
92
+ /**
93
+ * React Native `Button` — mirrors the web prop surface (sizes, colors, icons, loading,
94
+ * `href`) with platform adaptations:
95
+ *
96
+ * - `Pressable` instead of react-aria-components `Button` / `Link`
97
+ * - pressed styles instead of `hover:`
98
+ * - `ActivityIndicator` instead of the web SVG spinner
99
+ * - `href` opens via `Linking.openURL` (disabled/loading skips navigation)
100
+ * - no `before:` inset border gradient; no InputGroup nesting selectors
101
+ * - label colour/size via `style` on `Text` (NativeWind Text `className` bug — see Avatar)
102
+ *
103
+ * @summary action trigger or URL control (React Native)
104
+ */
105
+ const Button = ({ size = 'sm', color = 'primary', children, className, noTextPadding: noTextPaddingProp, iconLeading: IconLeading, iconTrailing: IconTrailing, isDisabled, isLoading, showTextWhileLoading, 'data-testid': dataTestId, 'aria-label': ariaLabel, onPress, hitSlop, accessibilityLabel, accessibilityHint, ...props }) => {
106
+ const href = 'href' in props ? props.href : undefined;
107
+ const resolvedAccessibilityLabel = accessibilityLabel ?? ariaLabel;
108
+ const buttonTestId = useResolvedTestId({ testId: dataTestId, segment: 'button', rootName: 'button' });
109
+ const [pressed, setPressed] = useState(false);
110
+ const hasChildren = children !== null && children !== undefined;
111
+ const isIcon = Boolean((IconLeading || IconTrailing) && !hasChildren);
112
+ const isLinkType = LINK_COLORS.includes(color);
113
+ const noTextPadding = isLinkType || noTextPaddingProp;
114
+ const disabled = Boolean(isDisabled || isLoading);
115
+ const iconColor = (pressed && ICON_COLOR_PRESSED[color]) || ICON_COLOR[color];
116
+ const labelColor = (pressed && LABEL_TEXT_COLOR_PRESSED[color]) || LABEL_TEXT_COLOR[color];
117
+ // Match web: hide non-spinner content with opacity-0 (keeps layout + a11y name) unless
118
+ // showTextWhileLoading — then only icons are omitted and the label stays visible.
119
+ const dimNonSpinner = Boolean(isLoading && !showTextWhileLoading);
120
+ const omitIconsWhileLoading = Boolean(isLoading && showTextWhileLoading);
121
+ const handlePress = (event) => {
122
+ if (disabled)
123
+ return;
124
+ onPress?.(event);
125
+ if (href) {
126
+ // `openURL` rejects when no app can handle the URL — swallow it so it doesn't
127
+ // surface as an unhandled promise rejection (no established error path to route to).
128
+ Linking.openURL(href).catch(() => { });
129
+ }
130
+ };
131
+ const leadingIcon = !omitIconsWhileLoading ? renderIcon(IconLeading, size, iconColor, styles.common.icon) : null;
132
+ const trailingIcon = !omitIconsWhileLoading ? renderIcon(IconTrailing, size, iconColor, styles.common.icon) : null;
133
+ return (jsxs(Pressable, { testID: buttonTestId, disabled: disabled, onPress: handlePress, onPressIn: () => setPressed(true), onPressOut: () => setPressed(false), hitSlop: hitSlop, accessibilityRole: href ? 'link' : 'button', accessibilityLabel: resolvedAccessibilityLabel, accessibilityHint: accessibilityHint, accessibilityState: { disabled, busy: Boolean(isLoading) }, className: cx(styles.common.root, isLinkType ? styles.sizes[size].linkRoot : styles.sizes[size].root,
134
+ // Merged (not swapped) so icon-only keeps `rounded-lg`/`gap-*` from `root` —
135
+ // `cx` (tailwind-merge) resolves the padding conflict, keeping this override.
136
+ isIcon && !isLinkType && styles.sizes[size].iconOnly, styles.colors[color].root, pressed && !disabled && styles.colors[color].pressed, isLoading && styles.colors[color].loading, disabled && 'opacity-50', className), children: [leadingIcon ? jsx(View, { style: dimNonSpinner ? { opacity: 0 } : undefined, children: leadingIcon }) : null, isLoading && (jsx(View, { className: cx(styles.common.icon, dimNonSpinner && 'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'), children: jsx(ActivityIndicator, { size: "small", color: labelColor }) })), hasChildren && (jsx(Text, { style: {
137
+ color: labelColor,
138
+ fontWeight: '600',
139
+ ...LABEL_TEXT_SIZE[size],
140
+ ...(dimNonSpinner ? { opacity: 0 } : null),
141
+ ...(isLinkType ? { textDecorationLine: 'underline' } : null),
142
+ ...(!noTextPadding ? { paddingHorizontal: 2 } : null),
143
+ }, children: children })), trailingIcon ? jsx(View, { style: dimNonSpinner ? { opacity: 0 } : undefined, children: trailingIcon }) : null] }));
144
+ };
145
+
146
+ export { Button };
147
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../../../src/components/atoms/Button/index.native.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flipdish/ui-library",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "description": "Flipdish Design System — atomic-design component library (Tailwind v4 + UntitledUI + React Aria).",
5
5
  "type": "module",
6
6
  "sideEffects": [