@tamagui/button 1.15.13 → 1.15.15

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.
@@ -1,3 +1,4 @@
1
+ import { createContextScope } from "@tamagui/create-context";
1
2
  import { getFontSize } from "@tamagui/font-size";
2
3
  import { getButtonSized } from "@tamagui/get-button-sized";
3
4
  import { useGetThemedIcon } from "@tamagui/helpers-tamagui";
@@ -10,12 +11,19 @@ import {
10
11
  spacedChildren,
11
12
  styled,
12
13
  themeable,
13
- useMediaPropsActive
14
+ useMediaPropsActive,
15
+ withStaticProperties
14
16
  } from "@tamagui/web";
15
- import { forwardRef, useContext } from "react";
16
- const NAME = "Button";
17
+ import {
18
+ forwardRef,
19
+ useCallback,
20
+ useContext,
21
+ useEffect,
22
+ useState
23
+ } from "react";
24
+ const BUTTON_NAME = "Button";
17
25
  const ButtonFrame = styled(ThemeableStack, {
18
- name: NAME,
26
+ name: BUTTON_NAME,
19
27
  tag: "button",
20
28
  variants: {
21
29
  unstyled: {
@@ -66,8 +74,9 @@ const ButtonFrame = styled(ThemeableStack, {
66
74
  unstyled: false
67
75
  }
68
76
  });
69
- const ButtonText = styled(SizableText, {
70
- name: "ButtonText",
77
+ const BUTTON_TEXT_NAME = "ButtonText";
78
+ const ButtonTextFrame = styled(SizableText, {
79
+ name: BUTTON_TEXT_NAME,
71
80
  variants: {
72
81
  unstyled: {
73
82
  false: {
@@ -85,10 +94,48 @@ const ButtonText = styled(SizableText, {
85
94
  unstyled: false
86
95
  }
87
96
  });
88
- const ButtonComponent = forwardRef(function Button(props, ref) {
89
- const { props: buttonProps } = useButton(props);
90
- return <ButtonFrame {...buttonProps} ref={ref} />;
91
- });
97
+ const [createButtonContext, createButtonScope] = createContextScope("Button");
98
+ const [ButtonProvider, useButtonContext] = createButtonContext("Button");
99
+ const ButtonTextComponent = ButtonTextFrame.extractable(
100
+ forwardRef(
101
+ (props, ref) => {
102
+ const context = useButtonContext(BUTTON_TEXT_NAME, props.__scopeButton);
103
+ useEffect(() => {
104
+ const unregister = context.registerButtonText();
105
+ return () => unregister();
106
+ }, [context.registerButtonText]);
107
+ return <ButtonTextFrame size={props.size ?? context.size} {...props} ref={ref} />;
108
+ }
109
+ )
110
+ );
111
+ const BUTTON_ICON_NAME = "ButtonIcon";
112
+ const ButtonIcon = (props) => {
113
+ const { children, scaleIcon = 1 } = props;
114
+ const context = useButtonContext(BUTTON_ICON_NAME, props.__scopeButton);
115
+ const size = context.size;
116
+ const color = context.color;
117
+ const iconSize = (typeof size === "number" ? size * 0.5 : getFontSize(size)) * scaleIcon;
118
+ const getThemedIcon = useGetThemedIcon({ size: iconSize, color });
119
+ return getThemedIcon(children);
120
+ };
121
+ const ButtonComponent = forwardRef(
122
+ function Button(props, ref) {
123
+ const { props: buttonProps } = useButton(props);
124
+ const [buttonTextCount, setButtonTextCount] = useState(0);
125
+ const registerButtonText = useCallback(() => {
126
+ setButtonTextCount((prev) => prev + 1);
127
+ return () => setButtonTextCount((prev) => prev - 1);
128
+ }, [setButtonTextCount]);
129
+ const hasTextComponent = buttonTextCount > 0;
130
+ return <ButtonProvider
131
+ scope={props.__scopeButton}
132
+ color={props.color}
133
+ hasTextComponent={hasTextComponent}
134
+ size={props.size ?? "$true"}
135
+ registerButtonText={registerButtonText}
136
+ ><ButtonFrame {...hasTextComponent ? props : buttonProps} ref={ref} /></ButtonProvider>;
137
+ }
138
+ );
92
139
  const buttonStaticConfig = {
93
140
  inlineProps: /* @__PURE__ */ new Set([
94
141
  // text props go here (can't really optimize them, but we never fully extract button anyway)
@@ -103,11 +150,17 @@ const buttonStaticConfig = {
103
150
  "unstyled"
104
151
  ])
105
152
  };
106
- const Button2 = ButtonFrame.extractable(
107
- themeable(ButtonComponent, ButtonFrame.staticConfig),
108
- buttonStaticConfig
153
+ const Button2 = withStaticProperties(
154
+ ButtonFrame.extractable(
155
+ themeable(ButtonComponent, ButtonFrame.staticConfig),
156
+ buttonStaticConfig
157
+ ),
158
+ {
159
+ Text: ButtonTextComponent,
160
+ Icon: ButtonIcon
161
+ }
109
162
  );
110
- function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
163
+ function useButton(propsIn, { Text = ButtonTextFrame } = { Text: ButtonTextFrame }) {
111
164
  const {
112
165
  children,
113
166
  icon,
@@ -141,7 +194,7 @@ function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
141
194
  const contents = wrapChildrenInText(
142
195
  Text,
143
196
  propsActive,
144
- Text === ButtonText ? {
197
+ Text === ButtonTextFrame ? {
145
198
  unstyled
146
199
  } : void 0
147
200
  );
@@ -167,6 +220,7 @@ function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
167
220
  borderColor: "$background"
168
221
  }
169
222
  },
223
+ unstyled,
170
224
  tag,
171
225
  ...rest,
172
226
  children: isRSC ? inner : <ButtonNestingContext.Provider value={true}>{inner}</ButtonNestingContext.Provider>
@@ -180,8 +234,9 @@ function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
180
234
  export {
181
235
  Button2 as Button,
182
236
  ButtonFrame,
183
- ButtonText,
237
+ ButtonTextFrame as ButtonText,
184
238
  buttonStaticConfig,
239
+ createButtonScope,
185
240
  useButton
186
241
  };
187
242
  //# sourceMappingURL=Button.js.map
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/Button.tsx"],
4
- "sourcesContent": ["import { getFontSize } from '@tamagui/font-size'\nimport { getButtonSized } from '@tamagui/get-button-sized'\nimport { useGetThemedIcon } from '@tamagui/helpers-tamagui'\nimport { ThemeableStack } from '@tamagui/stacks'\nimport { SizableText, TextParentStyles, wrapChildrenInText } from '@tamagui/text'\nimport {\n ButtonNestingContext,\n GetProps,\n TamaguiElement,\n ThemeableProps,\n getVariableValue,\n isRSC,\n spacedChildren,\n styled,\n themeable,\n useMediaPropsActive,\n} from '@tamagui/web'\nimport { FunctionComponent, forwardRef, useContext } from 'react'\n\ntype ButtonIconProps = { color?: string; size?: number }\ntype IconProp = JSX.Element | FunctionComponent<ButtonIconProps> | null\n\nexport type ButtonProps = Omit<TextParentStyles, 'TextComponent'> &\n GetProps<typeof ButtonFrame> &\n ThemeableProps & {\n /**\n * add icon before, passes color and size automatically if Component\n */\n icon?: IconProp\n /**\n * add icon after, passes color and size automatically if Component\n */\n iconAfter?: IconProp\n /**\n * adjust icon relative to size\n *\n * @default 1\n */\n scaleIcon?: number\n /**\n * make the spacing elements flex\n */\n spaceFlex?: number | boolean\n /**\n * adjust internal space relative to icon size\n */\n scaleSpace?: number\n /**\n *\n */\n unstyled?: boolean\n }\n\nconst NAME = 'Button'\n\nexport const ButtonFrame = styled(ThemeableStack, {\n name: NAME,\n tag: 'button',\n\n variants: {\n unstyled: {\n false: {\n size: '$true',\n justifyContent: 'center',\n alignItems: 'center',\n flexWrap: 'nowrap',\n flexDirection: 'row',\n cursor: 'pointer',\n focusable: true,\n hoverTheme: true,\n pressTheme: true,\n backgrounded: true,\n borderWidth: 1,\n borderColor: 'transparent',\n\n pressStyle: {\n borderColor: 'transparent',\n },\n\n hoverStyle: {\n borderColor: 'transparent',\n },\n\n focusStyle: {\n borderColor: '$borderColorFocus',\n outlineColor: '$borderColorFocus',\n outlineStyle: 'solid',\n outlineWidth: 2,\n },\n },\n },\n\n size: {\n '...size': getButtonSized,\n },\n\n active: {\n true: {\n hoverStyle: {\n backgroundColor: '$background',\n },\n },\n },\n\n disabled: {\n true: {\n pointerEvents: 'none',\n },\n },\n } as const,\n\n defaultVariants: {\n unstyled: false,\n },\n})\n\nexport const ButtonText = styled(SizableText, {\n name: 'ButtonText',\n\n variants: {\n unstyled: {\n false: {\n userSelect: 'none',\n cursor: 'pointer',\n // flexGrow 1 leads to inconsistent native style where text pushes to start of view\n flexGrow: 0,\n flexShrink: 1,\n ellipse: true,\n color: '$color',\n },\n },\n } as const,\n\n defaultVariants: {\n unstyled: false,\n },\n})\n\nconst ButtonComponent = forwardRef<TamaguiElement, ButtonProps>(function Button(\n props,\n ref\n) {\n const { props: buttonProps } = useButton(props)\n return <ButtonFrame {...buttonProps} ref={ref} />\n})\n\nexport const buttonStaticConfig = {\n inlineProps: new Set([\n // text props go here (can't really optimize them, but we never fully extract button anyway)\n // may be able to remove this entirely, as the compiler / runtime have gotten better\n 'color',\n 'fontWeight',\n 'fontSize',\n 'fontFamily',\n 'fontStyle',\n 'letterSpacing',\n 'textAlign',\n 'unstyled',\n ]),\n}\n\nexport const Button = ButtonFrame.extractable(\n themeable(ButtonComponent, ButtonFrame.staticConfig),\n buttonStaticConfig\n)\n\nexport function useButton(\n propsIn: ButtonProps,\n { Text = ButtonText }: { Text: any } = { Text: ButtonText }\n) {\n // careful not to desctructure and re-order props, order is important\n const {\n children,\n icon,\n iconAfter,\n noTextWrap,\n theme: themeName,\n space,\n spaceFlex,\n scaleIcon = 1,\n scaleSpace = 0.66,\n separator,\n\n // text props\n color,\n fontWeight,\n letterSpacing,\n fontSize,\n fontFamily,\n fontStyle,\n textAlign,\n unstyled = false,\n textProps,\n\n ...rest\n } = propsIn\n\n const isNested = isRSC ? false : useContext(ButtonNestingContext)\n const propsActive = useMediaPropsActive(propsIn)\n const size = propsActive.size || '$true'\n const iconSize = (typeof size === 'number' ? size * 0.5 : getFontSize(size)) * scaleIcon\n const getThemedIcon = useGetThemedIcon({ size: iconSize, color })\n const [themedIcon, themedIconAfter] = [icon, iconAfter].map(getThemedIcon)\n const spaceSize = propsActive.space ?? getVariableValue(iconSize) * scaleSpace\n const contents = wrapChildrenInText(\n Text,\n propsActive,\n Text === ButtonText\n ? {\n unstyled,\n }\n : undefined\n )\n const inner = spacedChildren({\n // a bit arbitrary but scaling to font size is necessary so long as button does\n space: spaceSize,\n spaceFlex,\n separator,\n direction:\n propsActive.flexDirection === 'column' ||\n propsActive.flexDirection === 'column-reverse'\n ? 'vertical'\n : 'horizontal',\n children: [themedIcon, ...contents, themedIconAfter],\n })\n\n // fixes SSR issue + DOM nesting issue of not allowing button in button\n const tag = isNested\n ? 'span'\n : // defaults to <a /> when accessibilityRole = link\n // see https://github.com/tamagui/tamagui/issues/505\n propsIn.accessibilityRole === 'link'\n ? 'a'\n : undefined\n\n const props = {\n ...(propsActive.disabled && {\n // in rnw - false still has keyboard tabIndex, undefined = not actually focusable\n focusable: undefined,\n // even with tabIndex unset, it will keep focusStyle on web so disable it here\n focusStyle: {\n borderColor: '$background',\n },\n }),\n tag,\n ...rest,\n children: isRSC ? (\n inner\n ) : (\n <ButtonNestingContext.Provider value={true}>{inner}</ButtonNestingContext.Provider>\n ),\n }\n\n return {\n spaceSize,\n isNested,\n props,\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAC/B,SAAS,aAA+B,0BAA0B;AAClE;AAAA,EACE;AAAA,EAIA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAA4B,YAAY,kBAAkB;AAoC1D,MAAM,OAAO;AAEN,MAAM,cAAc,OAAO,gBAAgB;AAAA,EAChD,MAAM;AAAA,EACN,KAAK;AAAA,EAEL,UAAU;AAAA,IACR,UAAU;AAAA,MACR,OAAO;AAAA,QACL,MAAM;AAAA,QACN,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,aAAa;AAAA,QACb,aAAa;AAAA,QAEb,YAAY;AAAA,UACV,aAAa;AAAA,QACf;AAAA,QAEA,YAAY;AAAA,UACV,aAAa;AAAA,QACf;AAAA,QAEA,YAAY;AAAA,UACV,aAAa;AAAA,UACb,cAAc;AAAA,UACd,cAAc;AAAA,UACd,cAAc;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM;AAAA,MACJ,WAAW;AAAA,IACb;AAAA,IAEA,QAAQ;AAAA,MACN,MAAM;AAAA,QACJ,YAAY;AAAA,UACV,iBAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf,UAAU;AAAA,EACZ;AACF,CAAC;AAEM,MAAM,aAAa,OAAO,aAAa;AAAA,EAC5C,MAAM;AAAA,EAEN,UAAU;AAAA,IACR,UAAU;AAAA,MACR,OAAO;AAAA,QACL,YAAY;AAAA,QACZ,QAAQ;AAAA;AAAA,QAER,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB;AAAA,IACf,UAAU;AAAA,EACZ;AACF,CAAC;AAED,MAAM,kBAAkB,WAAwC,SAAS,OACvE,OACA,KACA;AACA,QAAM,EAAE,OAAO,YAAY,IAAI,UAAU,KAAK;AAC9C,SAAO,CAAC,gBAAgB,aAAa,KAAK,KAAK;AACjD,CAAC;AAEM,MAAM,qBAAqB;AAAA,EAChC,aAAa,oBAAI,IAAI;AAAA;AAAA;AAAA,IAGnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEO,MAAMA,UAAS,YAAY;AAAA,EAChC,UAAU,iBAAiB,YAAY,YAAY;AAAA,EACnD;AACF;AAEO,SAAS,UACd,SACA,EAAE,OAAO,WAAW,IAAmB,EAAE,MAAM,WAAW,GAC1D;AAEA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,aAAa;AAAA,IACb;AAAA;AAAA,IAGA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IAEA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,WAAW,QAAQ,QAAQ,WAAW,oBAAoB;AAChE,QAAM,cAAc,oBAAoB,OAAO;AAC/C,QAAM,OAAO,YAAY,QAAQ;AACjC,QAAM,YAAY,OAAO,SAAS,WAAW,OAAO,MAAM,YAAY,IAAI,KAAK;AAC/E,QAAM,gBAAgB,iBAAiB,EAAE,MAAM,UAAU,MAAM,CAAC;AAChE,QAAM,CAAC,YAAY,eAAe,IAAI,CAAC,MAAM,SAAS,EAAE,IAAI,aAAa;AACzE,QAAM,YAAY,YAAY,SAAS,iBAAiB,QAAQ,IAAI;AACpE,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,SAAS,aACL;AAAA,MACE;AAAA,IACF,IACA;AAAA,EACN;AACA,QAAM,QAAQ,eAAe;AAAA;AAAA,IAE3B,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,WACE,YAAY,kBAAkB,YAC9B,YAAY,kBAAkB,mBAC1B,aACA;AAAA,IACN,UAAU,CAAC,YAAY,GAAG,UAAU,eAAe;AAAA,EACrD,CAAC;AAGD,QAAM,MAAM,WACR;AAAA;AAAA;AAAA,IAGF,QAAQ,sBAAsB,SAC5B,MACA;AAAA;AAEJ,QAAM,QAAQ;AAAA,IACZ,GAAI,YAAY,YAAY;AAAA;AAAA,MAE1B,WAAW;AAAA;AAAA,MAEX,YAAY;AAAA,QACV,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,UAAU,QACR,QAEA,CAAC,qBAAqB,SAAS,OAAO,OAAO,MAAM,EAAlD,qBAAqB;AAAA,EAE1B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "mappings": "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",
6
5
  "names": ["Button"]
7
6
  }
@@ -1,3 +1,4 @@
1
+ import { createContextScope } from "@tamagui/create-context";
1
2
  import { getFontSize } from "@tamagui/font-size";
2
3
  import { getButtonSized } from "@tamagui/get-button-sized";
3
4
  import { useGetThemedIcon } from "@tamagui/helpers-tamagui";
@@ -10,12 +11,19 @@ import {
10
11
  spacedChildren,
11
12
  styled,
12
13
  themeable,
13
- useMediaPropsActive
14
+ useMediaPropsActive,
15
+ withStaticProperties
14
16
  } from "@tamagui/web";
15
- import { forwardRef, useContext } from "react";
16
- const NAME = "Button";
17
+ import {
18
+ forwardRef,
19
+ useCallback,
20
+ useContext,
21
+ useEffect,
22
+ useState
23
+ } from "react";
24
+ const BUTTON_NAME = "Button";
17
25
  const ButtonFrame = styled(ThemeableStack, {
18
- name: NAME,
26
+ name: BUTTON_NAME,
19
27
  tag: "button",
20
28
  variants: {
21
29
  unstyled: {
@@ -66,8 +74,9 @@ const ButtonFrame = styled(ThemeableStack, {
66
74
  unstyled: false
67
75
  }
68
76
  });
69
- const ButtonText = styled(SizableText, {
70
- name: "ButtonText",
77
+ const BUTTON_TEXT_NAME = "ButtonText";
78
+ const ButtonTextFrame = styled(SizableText, {
79
+ name: BUTTON_TEXT_NAME,
71
80
  variants: {
72
81
  unstyled: {
73
82
  false: {
@@ -85,10 +94,48 @@ const ButtonText = styled(SizableText, {
85
94
  unstyled: false
86
95
  }
87
96
  });
88
- const ButtonComponent = forwardRef(function Button(props, ref) {
89
- const { props: buttonProps } = useButton(props);
90
- return <ButtonFrame {...buttonProps} ref={ref} />;
91
- });
97
+ const [createButtonContext, createButtonScope] = createContextScope("Button");
98
+ const [ButtonProvider, useButtonContext] = createButtonContext("Button");
99
+ const ButtonTextComponent = ButtonTextFrame.extractable(
100
+ forwardRef(
101
+ (props, ref) => {
102
+ const context = useButtonContext(BUTTON_TEXT_NAME, props.__scopeButton);
103
+ useEffect(() => {
104
+ const unregister = context.registerButtonText();
105
+ return () => unregister();
106
+ }, [context.registerButtonText]);
107
+ return <ButtonTextFrame size={props.size ?? context.size} {...props} ref={ref} />;
108
+ }
109
+ )
110
+ );
111
+ const BUTTON_ICON_NAME = "ButtonIcon";
112
+ const ButtonIcon = (props) => {
113
+ const { children, scaleIcon = 1 } = props;
114
+ const context = useButtonContext(BUTTON_ICON_NAME, props.__scopeButton);
115
+ const size = context.size;
116
+ const color = context.color;
117
+ const iconSize = (typeof size === "number" ? size * 0.5 : getFontSize(size)) * scaleIcon;
118
+ const getThemedIcon = useGetThemedIcon({ size: iconSize, color });
119
+ return getThemedIcon(children);
120
+ };
121
+ const ButtonComponent = forwardRef(
122
+ function Button(props, ref) {
123
+ const { props: buttonProps } = useButton(props);
124
+ const [buttonTextCount, setButtonTextCount] = useState(0);
125
+ const registerButtonText = useCallback(() => {
126
+ setButtonTextCount((prev) => prev + 1);
127
+ return () => setButtonTextCount((prev) => prev - 1);
128
+ }, [setButtonTextCount]);
129
+ const hasTextComponent = buttonTextCount > 0;
130
+ return <ButtonProvider
131
+ scope={props.__scopeButton}
132
+ color={props.color}
133
+ hasTextComponent={hasTextComponent}
134
+ size={props.size ?? "$true"}
135
+ registerButtonText={registerButtonText}
136
+ ><ButtonFrame {...hasTextComponent ? props : buttonProps} ref={ref} /></ButtonProvider>;
137
+ }
138
+ );
92
139
  const buttonStaticConfig = {
93
140
  inlineProps: /* @__PURE__ */ new Set([
94
141
  // text props go here (can't really optimize them, but we never fully extract button anyway)
@@ -103,11 +150,17 @@ const buttonStaticConfig = {
103
150
  "unstyled"
104
151
  ])
105
152
  };
106
- const Button2 = ButtonFrame.extractable(
107
- themeable(ButtonComponent, ButtonFrame.staticConfig),
108
- buttonStaticConfig
153
+ const Button2 = withStaticProperties(
154
+ ButtonFrame.extractable(
155
+ themeable(ButtonComponent, ButtonFrame.staticConfig),
156
+ buttonStaticConfig
157
+ ),
158
+ {
159
+ Text: ButtonTextComponent,
160
+ Icon: ButtonIcon
161
+ }
109
162
  );
110
- function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
163
+ function useButton(propsIn, { Text = ButtonTextFrame } = { Text: ButtonTextFrame }) {
111
164
  const {
112
165
  children,
113
166
  icon,
@@ -141,7 +194,7 @@ function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
141
194
  const contents = wrapChildrenInText(
142
195
  Text,
143
196
  propsActive,
144
- Text === ButtonText ? {
197
+ Text === ButtonTextFrame ? {
145
198
  unstyled
146
199
  } : void 0
147
200
  );
@@ -167,6 +220,7 @@ function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
167
220
  borderColor: "$background"
168
221
  }
169
222
  },
223
+ unstyled,
170
224
  tag,
171
225
  ...rest,
172
226
  children: isRSC ? inner : <ButtonNestingContext.Provider value={true}>{inner}</ButtonNestingContext.Provider>
@@ -180,8 +234,9 @@ function useButton(propsIn, { Text = ButtonText } = { Text: ButtonText }) {
180
234
  export {
181
235
  Button2 as Button,
182
236
  ButtonFrame,
183
- ButtonText,
237
+ ButtonTextFrame as ButtonText,
184
238
  buttonStaticConfig,
239
+ createButtonScope,
185
240
  useButton
186
241
  };
187
242
  //# sourceMappingURL=Button.mjs.map
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/Button.tsx"],
4
- "sourcesContent": ["import { getFontSize } from '@tamagui/font-size'\nimport { getButtonSized } from '@tamagui/get-button-sized'\nimport { useGetThemedIcon } from '@tamagui/helpers-tamagui'\nimport { ThemeableStack } from '@tamagui/stacks'\nimport { SizableText, TextParentStyles, wrapChildrenInText } from '@tamagui/text'\nimport {\n ButtonNestingContext,\n GetProps,\n TamaguiElement,\n ThemeableProps,\n getVariableValue,\n isRSC,\n spacedChildren,\n styled,\n themeable,\n useMediaPropsActive,\n} from '@tamagui/web'\nimport { FunctionComponent, forwardRef, useContext } from 'react'\n\ntype ButtonIconProps = { color?: string; size?: number }\ntype IconProp = JSX.Element | FunctionComponent<ButtonIconProps> | null\n\nexport type ButtonProps = Omit<TextParentStyles, 'TextComponent'> &\n GetProps<typeof ButtonFrame> &\n ThemeableProps & {\n /**\n * add icon before, passes color and size automatically if Component\n */\n icon?: IconProp\n /**\n * add icon after, passes color and size automatically if Component\n */\n iconAfter?: IconProp\n /**\n * adjust icon relative to size\n *\n * @default 1\n */\n scaleIcon?: number\n /**\n * make the spacing elements flex\n */\n spaceFlex?: number | boolean\n /**\n * adjust internal space relative to icon size\n */\n scaleSpace?: number\n /**\n *\n */\n unstyled?: boolean\n }\n\nconst NAME = 'Button'\n\nexport const ButtonFrame = styled(ThemeableStack, {\n name: NAME,\n tag: 'button',\n\n variants: {\n unstyled: {\n false: {\n size: '$true',\n justifyContent: 'center',\n alignItems: 'center',\n flexWrap: 'nowrap',\n flexDirection: 'row',\n cursor: 'pointer',\n focusable: true,\n hoverTheme: true,\n pressTheme: true,\n backgrounded: true,\n borderWidth: 1,\n borderColor: 'transparent',\n\n pressStyle: {\n borderColor: 'transparent',\n },\n\n hoverStyle: {\n borderColor: 'transparent',\n },\n\n focusStyle: {\n borderColor: '$borderColorFocus',\n outlineColor: '$borderColorFocus',\n outlineStyle: 'solid',\n outlineWidth: 2,\n },\n },\n },\n\n size: {\n '...size': getButtonSized,\n },\n\n active: {\n true: {\n hoverStyle: {\n backgroundColor: '$background',\n },\n },\n },\n\n disabled: {\n true: {\n pointerEvents: 'none',\n },\n },\n } as const,\n\n defaultVariants: {\n unstyled: false,\n },\n})\n\nexport const ButtonText = styled(SizableText, {\n name: 'ButtonText',\n\n variants: {\n unstyled: {\n false: {\n userSelect: 'none',\n cursor: 'pointer',\n // flexGrow 1 leads to inconsistent native style where text pushes to start of view\n flexGrow: 0,\n flexShrink: 1,\n ellipse: true,\n color: '$color',\n },\n },\n } as const,\n\n defaultVariants: {\n unstyled: false,\n },\n})\n\nconst ButtonComponent = forwardRef<TamaguiElement, ButtonProps>(function Button(\n props,\n ref\n) {\n const { props: buttonProps } = useButton(props)\n return <ButtonFrame {...buttonProps} ref={ref} />\n})\n\nexport const buttonStaticConfig = {\n inlineProps: new Set([\n // text props go here (can't really optimize them, but we never fully extract button anyway)\n // may be able to remove this entirely, as the compiler / runtime have gotten better\n 'color',\n 'fontWeight',\n 'fontSize',\n 'fontFamily',\n 'fontStyle',\n 'letterSpacing',\n 'textAlign',\n 'unstyled',\n ]),\n}\n\nexport const Button = ButtonFrame.extractable(\n themeable(ButtonComponent, ButtonFrame.staticConfig),\n buttonStaticConfig\n)\n\nexport function useButton(\n propsIn: ButtonProps,\n { Text = ButtonText }: { Text: any } = { Text: ButtonText }\n) {\n // careful not to desctructure and re-order props, order is important\n const {\n children,\n icon,\n iconAfter,\n noTextWrap,\n theme: themeName,\n space,\n spaceFlex,\n scaleIcon = 1,\n scaleSpace = 0.66,\n separator,\n\n // text props\n color,\n fontWeight,\n letterSpacing,\n fontSize,\n fontFamily,\n fontStyle,\n textAlign,\n unstyled = false,\n textProps,\n\n ...rest\n } = propsIn\n\n const isNested = isRSC ? false : useContext(ButtonNestingContext)\n const propsActive = useMediaPropsActive(propsIn)\n const size = propsActive.size || '$true'\n const iconSize = (typeof size === 'number' ? size * 0.5 : getFontSize(size)) * scaleIcon\n const getThemedIcon = useGetThemedIcon({ size: iconSize, color })\n const [themedIcon, themedIconAfter] = [icon, iconAfter].map(getThemedIcon)\n const spaceSize = propsActive.space ?? getVariableValue(iconSize) * scaleSpace\n const contents = wrapChildrenInText(\n Text,\n propsActive,\n Text === ButtonText\n ? {\n unstyled,\n }\n : undefined\n )\n const inner = spacedChildren({\n // a bit arbitrary but scaling to font size is necessary so long as button does\n space: spaceSize,\n spaceFlex,\n separator,\n direction:\n propsActive.flexDirection === 'column' ||\n propsActive.flexDirection === 'column-reverse'\n ? 'vertical'\n : 'horizontal',\n children: [themedIcon, ...contents, themedIconAfter],\n })\n\n // fixes SSR issue + DOM nesting issue of not allowing button in button\n const tag = isNested\n ? 'span'\n : // defaults to <a /> when accessibilityRole = link\n // see https://github.com/tamagui/tamagui/issues/505\n propsIn.accessibilityRole === 'link'\n ? 'a'\n : undefined\n\n const props = {\n ...(propsActive.disabled && {\n // in rnw - false still has keyboard tabIndex, undefined = not actually focusable\n focusable: undefined,\n // even with tabIndex unset, it will keep focusStyle on web so disable it here\n focusStyle: {\n borderColor: '$background',\n },\n }),\n tag,\n ...rest,\n children: isRSC ? (\n inner\n ) : (\n <ButtonNestingContext.Provider value={true}>{inner}</ButtonNestingContext.Provider>\n ),\n }\n\n return {\n spaceSize,\n isNested,\n props,\n }\n}\n"],
5
- "mappings": "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",
4
+ "mappings": "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",
6
5
  "names": ["Button"]
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/Button.test.tsx"],
4
- "sourcesContent": ["process.env.TAMAGUI_TARGET = 'web'\n\nimport { getDefaultTamaguiConfig } from '@tamagui/config-default'\nimport { TamaguiProvider, createTamagui } from '@tamagui/core'\nimport { render } from '@testing-library/react'\nimport { describe, expect, test } from 'vitest'\n\nimport { Button } from '.'\n\nconst conf = createTamagui(getDefaultTamaguiConfig())\n\ndescribe('Button', () => {\n test(`123`, () => {\n expect(true).toBeTruthy()\n })\n\n // test(`Adapts to a when given accessibilityRole=\"link\"`, async () => {\n // const { container } = render(\n // <TamaguiProvider config={conf} defaultTheme=\"light\">\n // <Button href=\"http://google.com\" accessibilityRole=\"link\" />\n // </TamaguiProvider>\n // )\n\n // expect(container.firstChild).toMatchSnapshot()\n // })\n})\n"],
5
4
  "mappings": "AAAA,QAAQ,IAAI,iBAAiB;AAE7B,SAAS,+BAA+B;AACxC,SAA0B,qBAAqB;AAE/C,SAAS,UAAU,QAAQ,YAAY;AAIvC,MAAM,OAAO,cAAc,wBAAwB,CAAC;AAEpD,SAAS,UAAU,MAAM;AACvB,OAAK,OAAO,MAAM;AAChB,WAAO,IAAI,EAAE,WAAW;AAAA,EAC1B,CAAC;AAWH,CAAC;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/Button.test.tsx"],
4
- "sourcesContent": ["process.env.TAMAGUI_TARGET = 'web'\n\nimport { getDefaultTamaguiConfig } from '@tamagui/config-default'\nimport { TamaguiProvider, createTamagui } from '@tamagui/core'\nimport { render } from '@testing-library/react'\nimport { describe, expect, test } from 'vitest'\n\nimport { Button } from '.'\n\nconst conf = createTamagui(getDefaultTamaguiConfig())\n\ndescribe('Button', () => {\n test(`123`, () => {\n expect(true).toBeTruthy()\n })\n\n // test(`Adapts to a when given accessibilityRole=\"link\"`, async () => {\n // const { container } = render(\n // <TamaguiProvider config={conf} defaultTheme=\"light\">\n // <Button href=\"http://google.com\" accessibilityRole=\"link\" />\n // </TamaguiProvider>\n // )\n\n // expect(container.firstChild).toMatchSnapshot()\n // })\n})\n"],
5
4
  "mappings": "AAAA,QAAQ,IAAI,iBAAiB;AAE7B,SAAS,+BAA+B;AACxC,SAA0B,qBAAqB;AAE/C,SAAS,UAAU,QAAQ,YAAY;AAIvC,MAAM,OAAO,cAAc,wBAAwB,CAAC;AAEpD,SAAS,UAAU,MAAM;AACvB,OAAK,OAAO,MAAM;AAChB,WAAO,IAAI,EAAE,WAAW;AAAA,EAC1B,CAAC;AAWH,CAAC;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["export * from './Button'\n"],
5
4
  "mappings": "AAAA,cAAc;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["export * from './Button'\n"],
5
4
  "mappings": "AAAA,cAAc;",
6
5
  "names": []
7
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/button",
3
- "version": "1.15.13",
3
+ "version": "1.15.15",
4
4
  "sideEffects": [
5
5
  "*.css"
6
6
  ],
@@ -29,17 +29,17 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@tamagui/font-size": "1.15.13",
33
- "@tamagui/get-button-sized": "1.15.13",
34
- "@tamagui/helpers-tamagui": "1.15.13",
35
- "@tamagui/text": "1.15.13",
36
- "@tamagui/web": "1.15.13"
32
+ "@tamagui/font-size": "1.15.15",
33
+ "@tamagui/get-button-sized": "1.15.15",
34
+ "@tamagui/helpers-tamagui": "1.15.15",
35
+ "@tamagui/text": "1.15.15",
36
+ "@tamagui/web": "1.15.15"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "react": "*"
40
40
  },
41
41
  "devDependencies": {
42
- "@tamagui/build": "1.15.13",
42
+ "@tamagui/build": "1.15.15",
43
43
  "react": "^18.2.0",
44
44
  "vitest": "^0.26.3"
45
45
  },
package/src/Button.tsx CHANGED
@@ -1,11 +1,14 @@
1
+ import type { Scope } from '@tamagui/create-context'
2
+ import { createContextScope } from '@tamagui/create-context'
1
3
  import { getFontSize } from '@tamagui/font-size'
2
4
  import { getButtonSized } from '@tamagui/get-button-sized'
3
- import { useGetThemedIcon } from '@tamagui/helpers-tamagui'
5
+ import { ColorProp, useCurrentColor, useGetThemedIcon } from '@tamagui/helpers-tamagui'
4
6
  import { ThemeableStack } from '@tamagui/stacks'
5
7
  import { SizableText, TextParentStyles, wrapChildrenInText } from '@tamagui/text'
6
8
  import {
7
9
  ButtonNestingContext,
8
10
  GetProps,
11
+ SizeTokens,
9
12
  TamaguiElement,
10
13
  ThemeableProps,
11
14
  getVariableValue,
@@ -14,13 +17,23 @@ import {
14
17
  styled,
15
18
  themeable,
16
19
  useMediaPropsActive,
20
+ useThemeName,
21
+ withStaticProperties,
17
22
  } from '@tamagui/web'
18
- import { FunctionComponent, forwardRef, useContext } from 'react'
23
+ import {
24
+ FunctionComponent,
25
+ createContext,
26
+ forwardRef,
27
+ useCallback,
28
+ useContext,
29
+ useEffect,
30
+ useState,
31
+ } from 'react'
19
32
 
20
33
  type ButtonIconProps = { color?: string; size?: number }
21
34
  type IconProp = JSX.Element | FunctionComponent<ButtonIconProps> | null
22
35
 
23
- export type ButtonProps = Omit<TextParentStyles, 'TextComponent'> &
36
+ type ButtonProps = Omit<TextParentStyles, 'TextComponent'> &
24
37
  GetProps<typeof ButtonFrame> &
25
38
  ThemeableProps & {
26
39
  /**
@@ -51,10 +64,10 @@ export type ButtonProps = Omit<TextParentStyles, 'TextComponent'> &
51
64
  unstyled?: boolean
52
65
  }
53
66
 
54
- const NAME = 'Button'
67
+ const BUTTON_NAME = 'Button'
55
68
 
56
- export const ButtonFrame = styled(ThemeableStack, {
57
- name: NAME,
69
+ const ButtonFrame = styled(ThemeableStack, {
70
+ name: BUTTON_NAME,
58
71
  tag: 'button',
59
72
 
60
73
  variants: {
@@ -114,8 +127,9 @@ export const ButtonFrame = styled(ThemeableStack, {
114
127
  },
115
128
  })
116
129
 
117
- export const ButtonText = styled(SizableText, {
118
- name: 'ButtonText',
130
+ const BUTTON_TEXT_NAME = 'ButtonText'
131
+ const ButtonTextFrame = styled(SizableText, {
132
+ name: BUTTON_TEXT_NAME,
119
133
 
120
134
  variants: {
121
135
  unstyled: {
@@ -136,15 +150,82 @@ export const ButtonText = styled(SizableText, {
136
150
  },
137
151
  })
138
152
 
139
- const ButtonComponent = forwardRef<TamaguiElement, ButtonProps>(function Button(
140
- props,
141
- ref
142
- ) {
143
- const { props: buttonProps } = useButton(props)
144
- return <ButtonFrame {...buttonProps} ref={ref} />
145
- })
153
+ type ScopedProps<P> = P & { __scopeButton?: Scope }
154
+ const [createButtonContext, createButtonScope] = createContextScope('Button')
155
+
156
+ type ButtonContextValue = {
157
+ size: SizeTokens
158
+ color: ColorProp
159
+
160
+ // used to keep backward compat with the new Button.Text api
161
+ hasTextComponent: boolean
162
+ registerButtonText: () => () => void
163
+ }
164
+
165
+ const [ButtonProvider, useButtonContext] =
166
+ createButtonContext<ButtonContextValue>('Button')
167
+
168
+ const ButtonTextComponent = ButtonTextFrame.extractable(
169
+ forwardRef<TamaguiElement, ScopedProps<GetProps<typeof ButtonTextFrame>>>(
170
+ (props, ref) => {
171
+ const context = useButtonContext(BUTTON_TEXT_NAME, props.__scopeButton)
172
+
173
+ useEffect(() => {
174
+ const unregister = context.registerButtonText()
175
+ return () => unregister()
176
+ }, [context.registerButtonText])
177
+
178
+ return <ButtonTextFrame size={props.size ?? context.size} {...props} ref={ref} />
179
+ }
180
+ )
181
+ )
182
+
183
+ const BUTTON_ICON_NAME = 'ButtonIcon'
184
+
185
+ const ButtonIcon = (
186
+ props: ScopedProps<{
187
+ children: React.ReactNode
188
+ scaleIcon?: number
189
+ }>
190
+ ) => {
191
+ const { children, scaleIcon = 1 } = props
192
+ const context = useButtonContext(BUTTON_ICON_NAME, props.__scopeButton)
193
+
194
+ const size = context.size
195
+ const color = context.color
146
196
 
147
- export const buttonStaticConfig = {
197
+ const iconSize = (typeof size === 'number' ? size * 0.5 : getFontSize(size)) * scaleIcon
198
+ const getThemedIcon = useGetThemedIcon({ size: iconSize, color })
199
+ return getThemedIcon(children)
200
+ }
201
+
202
+ const ButtonComponent = forwardRef<TamaguiElement, ScopedProps<ButtonProps>>(
203
+ function Button(props, ref) {
204
+ const { props: buttonProps } = useButton(props)
205
+ const [buttonTextCount, setButtonTextCount] = useState(0)
206
+
207
+ const registerButtonText = useCallback(() => {
208
+ setButtonTextCount((prev) => prev + 1)
209
+ return () => setButtonTextCount((prev) => prev - 1)
210
+ }, [setButtonTextCount])
211
+
212
+ const hasTextComponent = buttonTextCount > 0
213
+
214
+ return (
215
+ <ButtonProvider
216
+ scope={props.__scopeButton}
217
+ color={props.color}
218
+ hasTextComponent={hasTextComponent}
219
+ size={props.size ?? '$true'}
220
+ registerButtonText={registerButtonText}
221
+ >
222
+ <ButtonFrame {...(hasTextComponent ? props : buttonProps)} ref={ref} />
223
+ </ButtonProvider>
224
+ )
225
+ }
226
+ )
227
+
228
+ const buttonStaticConfig = {
148
229
  inlineProps: new Set([
149
230
  // text props go here (can't really optimize them, but we never fully extract button anyway)
150
231
  // may be able to remove this entirely, as the compiler / runtime have gotten better
@@ -159,14 +240,20 @@ export const buttonStaticConfig = {
159
240
  ]),
160
241
  }
161
242
 
162
- export const Button = ButtonFrame.extractable(
163
- themeable(ButtonComponent, ButtonFrame.staticConfig),
164
- buttonStaticConfig
243
+ const Button = withStaticProperties(
244
+ ButtonFrame.extractable(
245
+ themeable(ButtonComponent, ButtonFrame.staticConfig),
246
+ buttonStaticConfig
247
+ ),
248
+ {
249
+ Text: ButtonTextComponent,
250
+ Icon: ButtonIcon,
251
+ }
165
252
  )
166
253
 
167
- export function useButton(
254
+ function useButton(
168
255
  propsIn: ButtonProps,
169
- { Text = ButtonText }: { Text: any } = { Text: ButtonText }
256
+ { Text = ButtonTextFrame }: { Text: any } = { Text: ButtonTextFrame }
170
257
  ) {
171
258
  // careful not to desctructure and re-order props, order is important
172
259
  const {
@@ -205,7 +292,7 @@ export function useButton(
205
292
  const contents = wrapChildrenInText(
206
293
  Text,
207
294
  propsActive,
208
- Text === ButtonText
295
+ Text === ButtonTextFrame
209
296
  ? {
210
297
  unstyled,
211
298
  }
@@ -242,6 +329,7 @@ export function useButton(
242
329
  borderColor: '$background',
243
330
  },
244
331
  }),
332
+ unstyled,
245
333
  tag,
246
334
  ...rest,
247
335
  children: isRSC ? (
@@ -257,3 +345,16 @@ export function useButton(
257
345
  props,
258
346
  }
259
347
  }
348
+
349
+ export {
350
+ createButtonScope,
351
+ Button,
352
+
353
+ // legacy api
354
+ useButton,
355
+ buttonStaticConfig,
356
+ ButtonFrame,
357
+ ButtonTextFrame as ButtonText,
358
+ }
359
+
360
+ export type { ButtonProps }