@butternutbox/pawprint-native 0.23.2 → 0.24.1

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.
@@ -7,12 +7,12 @@
7
7
  CJS Build start
8
8
  ESM Build start
9
9
  DTS Build start
10
- ESM dist/index.js 521.48 KB
11
- ESM dist/index.js.map 1.07 MB
12
- ESM ⚡️ Build success in 7873ms
13
- CJS dist/index.cjs 551.89 KB
14
- CJS dist/index.cjs.map 1.07 MB
15
- CJS ⚡️ Build success in 7873ms
16
- DTS ⚡️ Build success in 21420ms
17
- DTS dist/index.d.cts 102.54 KB
18
- DTS dist/index.d.ts 102.54 KB
10
+ CJS dist/index.cjs 554.55 KB
11
+ CJS dist/index.cjs.map 1.08 MB
12
+ CJS ⚡️ Build success in 6712ms
13
+ ESM dist/index.js 523.91 KB
14
+ ESM dist/index.js.map 1.08 MB
15
+ ESM ⚡️ Build success in 6712ms
16
+ DTS ⚡️ Build success in 18388ms
17
+ DTS dist/index.d.cts 103.07 KB
18
+ DTS dist/index.d.ts 103.07 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @butternutbox/pawprint-native
2
2
 
3
+ ## 0.24.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d75e132: Fix two `StackedNotifications` animation glitches: dismissing from a deep queue would briefly show only one peek before the next popped in, and a brand-new notification would jump into place instead of sliding and fading in smoothly.
8
+
9
+ ## 0.24.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 85a6f8e: `Progress` now eases its indicator to the new width instead of snapping to it. Add `animate={false}` to opt out; the animation is also skipped when the OS has reduced motion enabled. The timing is exported as `PROGRESS_ANIMATION` so anything overlaid on the track can stay pinned to the leading edge of the fill.
14
+
15
+ ## 0.23.3
16
+
17
+ ### Patch Changes
18
+
19
+ - 1a54efb: Let component labels wrap to multiple lines instead of clipping/overflowing — surfaced by long translated (e.g. German) copy.
20
+ - **Typography**: token-mode text now sets `flexShrink`, so component-owned labels that live in flex rows (buttons, tags, tabs) can shrink and wrap instead of laying out on one line and overflowing.
21
+ - **Button**: the label is now a direct child of the button row (no intermediate wrapper `View`) so it actually re-wraps under the button's `minHeight`; loading state dims the label via a transparent colour instead of a wrapper opacity.
22
+ - **Tag**: the label can shrink so a long tag wraps within its container instead of growing past it (e.g. overflowing a card's padding).
23
+ - **TabNavigation**: tabs use `minHeight` (not a fixed `height`) so a tab whose label wraps to more than one line grows to fit and stays aligned with its neighbours; tab labels are now centre-aligned.
24
+
3
25
  ## 0.23.2
4
26
 
5
27
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -253,7 +253,14 @@ var Typography = React66__default.default.forwardRef(
253
253
  "textTransform",
254
254
  "textDecoration"
255
255
  ]);
256
- const style = __spreadValues(__spreadValues(__spreadValues({}, nativeTokenToStyle(token)), color2 ? { color: color2 } : {}), getCommonStyles(align2, textTransform2, textDecoration2));
256
+ const style = __spreadValues(__spreadValues(__spreadValues({
257
+ // Token-mode text is component-owned and lives inside flex rows
258
+ // (buttons, tabs, tags). `flexShrink` lets it wrap to multiple lines
259
+ // when the row is narrower than the text, instead of laying out on one
260
+ // line and overflowing/clipping — a `<Text>` nested in a flex `<View>`
261
+ // won't re-wrap on its own after the parent's width is resolved.
262
+ flexShrink: 1
263
+ }, nativeTokenToStyle(token)), color2 ? { color: color2 } : {}), getCommonStyles(align2, textTransform2, textDecoration2));
257
264
  return /* @__PURE__ */ jsxRuntime.jsx(
258
265
  StyledText,
259
266
  __spreadValues({
@@ -1227,9 +1234,6 @@ var StyledButton = styled51__default.default(reactNative.Pressable)(
1227
1234
  opacity: buttonOpacity
1228
1235
  }, buttonBorderWidth ? { borderWidth: buttonBorderWidth, borderColor: buttonBorderColor } : {}), buttonFullWidth ? { width: "100%" } : {})
1229
1236
  );
1230
- var StyledTextWrapper = styled51__default.default(reactNative.View)(({ textOpacity, fullWidth }) => __spreadValues({
1231
- opacity: textOpacity
1232
- }, fullWidth ? { flexShrink: 1 } : {}));
1233
1237
  var StyledSpinnerWrapper = styled51__default.default(reactNative.View)({
1234
1238
  position: "absolute",
1235
1239
  alignItems: "center",
@@ -1320,7 +1324,7 @@ var Button = React66__default.default.forwardRef(
1320
1324
  }, rest), {
1321
1325
  children: [
1322
1326
  startIcon && /* @__PURE__ */ jsxRuntime.jsx(IconWrapper, { children: startIcon }),
1323
- /* @__PURE__ */ jsxRuntime.jsx(StyledTextWrapper, { textOpacity: loading ? 0 : 1, fullWidth, children: /* @__PURE__ */ jsxRuntime.jsx(
1327
+ /* @__PURE__ */ jsxRuntime.jsx(
1324
1328
  Typography,
1325
1329
  {
1326
1330
  token: {
@@ -1330,11 +1334,11 @@ var Button = React66__default.default.forwardRef(
1330
1334
  lineHeight: typography.lineHeight,
1331
1335
  letterSpacing: typography.letterSpacing
1332
1336
  },
1333
- color: variantStyles.textColor,
1337
+ color: loading ? "transparent" : variantStyles.textColor,
1334
1338
  align: "center",
1335
1339
  children
1336
1340
  }
1337
- ) }),
1341
+ ),
1338
1342
  endIcon && /* @__PURE__ */ jsxRuntime.jsx(IconWrapper, { children: endIcon }),
1339
1343
  loading && /* @__PURE__ */ jsxRuntime.jsx(StyledSpinnerWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(
1340
1344
  Spinner,
@@ -4929,6 +4933,9 @@ var StyledTag = styled51__default.default(reactNative.View)(({ theme: theme2, ta
4929
4933
  backgroundColor: backgroundColorMap[tagVariant]
4930
4934
  };
4931
4935
  });
4936
+ var StyledLabel = styled51__default.default(reactNative.View)({
4937
+ flexShrink: 1
4938
+ });
4932
4939
  var Tag = React66__default.default.forwardRef(
4933
4940
  (_a, ref) => {
4934
4941
  var _b = _a, { variant = "primary", size = "medium", icon, children } = _b, rest = __objRest(_b, ["variant", "size", "icon", "children"]);
@@ -4966,14 +4973,14 @@ var Tag = React66__default.default.forwardRef(
4966
4973
  colour: iconColourMap[variant]
4967
4974
  }
4968
4975
  ),
4969
- /* @__PURE__ */ jsxRuntime.jsx(
4976
+ /* @__PURE__ */ jsxRuntime.jsx(StyledLabel, { children: /* @__PURE__ */ jsxRuntime.jsx(
4970
4977
  Typography,
4971
4978
  {
4972
4979
  token: typography[size].default,
4973
4980
  color: textColorMap[variant],
4974
4981
  children
4975
4982
  }
4976
- )
4983
+ ) })
4977
4984
  ] }));
4978
4985
  }
4979
4986
  );
@@ -5222,7 +5229,7 @@ var StyledLabelWrapper = styled51__default.default(reactNative.View)({
5222
5229
  justifyContent: "space-between",
5223
5230
  width: "100%"
5224
5231
  });
5225
- var StyledLabel = styled51__default.default(reactNative.View)(({ labelGap }) => ({
5232
+ var StyledLabel2 = styled51__default.default(reactNative.View)(({ labelGap }) => ({
5226
5233
  flexDirection: "row",
5227
5234
  alignItems: "center",
5228
5235
  gap: labelGap
@@ -5253,7 +5260,7 @@ var TextAreaLabel = React66__default.default.forwardRef(
5253
5260
  const showCharacterCount = maxLength !== void 0 && currentLength !== void 0;
5254
5261
  const isOverLimit = currentLength !== void 0 && maxLength !== void 0 && currentLength > maxLength;
5255
5262
  return /* @__PURE__ */ jsxRuntime.jsxs(StyledLabelWrapper, __spreadProps(__spreadValues({ ref }, rest), { children: [
5256
- /* @__PURE__ */ jsxRuntime.jsxs(StyledLabel, { labelGap: parseTokenValue20(spacing.label.gap), children: [
5263
+ /* @__PURE__ */ jsxRuntime.jsxs(StyledLabel2, { labelGap: parseTokenValue20(spacing.label.gap), children: [
5257
5264
  /* @__PURE__ */ jsxRuntime.jsx(
5258
5265
  Typography,
5259
5266
  {
@@ -7894,6 +7901,10 @@ function usePasswordField({
7894
7901
  showRequirements
7895
7902
  });
7896
7903
  }
7904
+ var PROGRESS_ANIMATION = {
7905
+ duration: 450,
7906
+ easing: Animated10.Easing.inOut(Animated10.Easing.cubic)
7907
+ };
7897
7908
  var parseTokenValue40 = (value) => parseFloat(value);
7898
7909
  var StyledRoot12 = styled51__default.default(reactNative.View)(({ rootGap }) => ({
7899
7910
  flexDirection: "column",
@@ -7916,7 +7927,7 @@ var StyledTrack = styled51__default.default(reactNative.View)(({ trackHeight, tr
7916
7927
  backgroundColor: trackBgColor,
7917
7928
  overflow: "hidden"
7918
7929
  }));
7919
- var StyledIndicator = styled51__default.default(reactNative.View)(({ indicatorBorderRadius, indicatorBgColor }) => ({
7930
+ var StyledIndicator = styled51__default.default(Animated10__default.default.View)(({ indicatorBorderRadius, indicatorBgColor }) => ({
7920
7931
  height: "100%",
7921
7932
  borderRadius: indicatorBorderRadius,
7922
7933
  backgroundColor: indicatorBgColor
@@ -7931,7 +7942,8 @@ var Progress = React66__default.default.forwardRef(
7931
7942
  colour = "primary",
7932
7943
  label,
7933
7944
  description,
7934
- getValueLabel
7945
+ getValueLabel,
7946
+ animate = true
7935
7947
  } = _b, rest = __objRest(_b, [
7936
7948
  "value",
7937
7949
  "max",
@@ -7939,7 +7951,8 @@ var Progress = React66__default.default.forwardRef(
7939
7951
  "colour",
7940
7952
  "label",
7941
7953
  "description",
7942
- "getValueLabel"
7954
+ "getValueLabel",
7955
+ "animate"
7943
7956
  ]);
7944
7957
  const theme2 = react.useTheme();
7945
7958
  const {
@@ -7955,6 +7968,15 @@ var Progress = React66__default.default.forwardRef(
7955
7968
  const percent = clampedValue / safeMax * 100;
7956
7969
  const showHeader = Boolean(label) || Boolean(description);
7957
7970
  const typographyForSize = size === "lg" ? typography.large : typography.small;
7971
+ const reducedMotion = Animated10.useReducedMotion();
7972
+ const shouldAnimate = animate && !reducedMotion;
7973
+ const fill = Animated10.useSharedValue(percent);
7974
+ React66__default.default.useEffect(() => {
7975
+ fill.value = shouldAnimate ? Animated10.withTiming(percent, PROGRESS_ANIMATION) : percent;
7976
+ }, [fill, percent, shouldAnimate]);
7977
+ const indicatorStyle = Animated10.useAnimatedStyle(() => ({
7978
+ width: `${fill.value}%`
7979
+ }));
7958
7980
  const trackSize = { lg: track.size.large, sm: track.size.small }[size];
7959
7981
  const trackHeight = parseTokenValue40(trackSize.height);
7960
7982
  const trackMinWidth = parseTokenValue40(trackSize.mindWidth);
@@ -8001,7 +8023,7 @@ var Progress = React66__default.default.forwardRef(
8001
8023
  {
8002
8024
  indicatorBorderRadius,
8003
8025
  indicatorBgColor,
8004
- style: { width: `${percent}%` }
8026
+ style: indicatorStyle
8005
8027
  }
8006
8028
  ) })
8007
8029
  }
@@ -8968,7 +8990,7 @@ var StyledContentWrapper = styled51__default.default(reactNative.View)(({ theme:
8968
8990
  minWidth: 0
8969
8991
  };
8970
8992
  });
8971
- var StyledTextWrapper2 = styled51__default.default(reactNative.View)({
8993
+ var StyledTextWrapper = styled51__default.default(reactNative.View)({
8972
8994
  flex: 1,
8973
8995
  minWidth: 0
8974
8996
  });
@@ -9003,7 +9025,7 @@ var SelectFieldItem = React66__default.default.forwardRef(
9003
9025
  children: /* @__PURE__ */ jsxRuntime.jsx(slot.Slot, { ref, children: /* @__PURE__ */ jsxRuntime.jsxs(StyledItem3, __spreadProps(__spreadValues({ disabled, style: { backgroundColor } }, rest), { children: [
9004
9026
  leadingIcon && /* @__PURE__ */ jsxRuntime.jsx(StyledIconWrapper3, { children: leadingIcon }),
9005
9027
  /* @__PURE__ */ jsxRuntime.jsxs(StyledContentWrapper, { children: [
9006
- /* @__PURE__ */ jsxRuntime.jsx(StyledTextWrapper2, { children: /* @__PURE__ */ jsxRuntime.jsx(StyledItemText, { style: { color: textColor }, children: itemText }) }),
9028
+ /* @__PURE__ */ jsxRuntime.jsx(StyledTextWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(StyledItemText, { style: { color: textColor }, children: itemText }) }),
9007
9029
  hintText && /* @__PURE__ */ jsxRuntime.jsx(
9008
9030
  StyledHintText,
9009
9031
  {
@@ -10264,8 +10286,11 @@ var CARD_BASE_STYLE = {
10264
10286
  position: "absolute",
10265
10287
  bottom: 0,
10266
10288
  left: 0,
10267
- right: 0,
10268
- transformOrigin: "50% 100%"
10289
+ right: 0
10290
+ };
10291
+ var DEPTH_LAYER_STYLE = {
10292
+ transformOrigin: "50% 100%",
10293
+ width: "100%"
10269
10294
  };
10270
10295
  var StyledRoot15 = styled51__default.default(reactNative.View)(({ rootBottom, rootPaddingH }) => ({
10271
10296
  position: "absolute",
@@ -10279,17 +10304,34 @@ var StyledStack = styled51__default.default(reactNative.View)({
10279
10304
  position: "relative",
10280
10305
  width: "100%"
10281
10306
  });
10307
+ var computeBackfillIds = (layers, prevActiveIds, prevLayerIds) => {
10308
+ const ids = /* @__PURE__ */ new Set();
10309
+ layers.forEach((item) => {
10310
+ if (prevActiveIds.has(item.id) && !prevLayerIds.has(item.id)) {
10311
+ ids.add(item.id);
10312
+ }
10313
+ });
10314
+ return ids;
10315
+ };
10282
10316
  var StackedCard = ({
10283
10317
  item,
10284
10318
  depth,
10285
10319
  revealStep,
10286
- onDismiss
10320
+ onDismiss,
10321
+ isBackfill
10287
10322
  }) => {
10288
- const depthSV = Animated10.useSharedValue(depth);
10323
+ const isBackfillRef = React66__default.default.useRef(isBackfill);
10324
+ const wasBackfill = isBackfillRef.current;
10325
+ const depthSV = Animated10.useSharedValue(wasBackfill ? depth + 1 : depth);
10326
+ const opacitySV = Animated10.useSharedValue(wasBackfill ? 0 : 1);
10289
10327
  React66__default.default.useEffect(() => {
10290
10328
  depthSV.value = Animated10.withSpring(depth, SPRING_CONFIG);
10291
10329
  }, [depth, depthSV]);
10292
- const animatedStyle = Animated10.useAnimatedStyle(() => ({
10330
+ React66__default.default.useEffect(() => {
10331
+ if (wasBackfill) opacitySV.value = Animated10.withSpring(1, SPRING_CONFIG);
10332
+ }, []);
10333
+ const depthStyle = Animated10.useAnimatedStyle(() => ({
10334
+ opacity: opacitySV.value,
10293
10335
  transform: [
10294
10336
  { translateY: depthSV.value * revealStep },
10295
10337
  { scale: 1 - depthSV.value * SCALE_STEP }
@@ -10299,13 +10341,13 @@ var StackedCard = ({
10299
10341
  return /* @__PURE__ */ jsxRuntime.jsx(
10300
10342
  Animated10__default.default.View,
10301
10343
  {
10302
- entering: CARD_ENTERING,
10344
+ entering: wasBackfill ? void 0 : CARD_ENTERING,
10303
10345
  exiting: CARD_EXITING,
10304
10346
  pointerEvents: isFront ? "auto" : "none",
10305
10347
  accessibilityElementsHidden: !isFront,
10306
10348
  importantForAccessibility: isFront ? "auto" : "no-hide-descendants",
10307
- style: [CARD_BASE_STYLE, { zIndex: -depth }, animatedStyle],
10308
- children: /* @__PURE__ */ jsxRuntime.jsx(
10349
+ style: [CARD_BASE_STYLE, { zIndex: -depth }],
10350
+ children: /* @__PURE__ */ jsxRuntime.jsx(Animated10__default.default.View, { style: [DEPTH_LAYER_STYLE, depthStyle], children: /* @__PURE__ */ jsxRuntime.jsx(
10309
10351
  Notification,
10310
10352
  {
10311
10353
  variant: "toast",
@@ -10313,7 +10355,7 @@ var StackedCard = ({
10313
10355
  onClose: isFront ? () => onDismiss == null ? void 0 : onDismiss(item.id) : void 0,
10314
10356
  children: item.message
10315
10357
  }
10316
- )
10358
+ ) })
10317
10359
  }
10318
10360
  );
10319
10361
  };
@@ -10344,6 +10386,20 @@ var StackedNotifications = React66__default.default.forwardRef((_a, ref) => {
10344
10386
  () => activeItems.slice(-3).reverse(),
10345
10387
  [activeItems]
10346
10388
  );
10389
+ const prevActiveIdsRef = React66__default.default.useRef(/* @__PURE__ */ new Set());
10390
+ const prevLayerIdsRef = React66__default.default.useRef(/* @__PURE__ */ new Set());
10391
+ const backfillIds = React66__default.default.useMemo(
10392
+ () => computeBackfillIds(
10393
+ layers,
10394
+ prevActiveIdsRef.current,
10395
+ prevLayerIdsRef.current
10396
+ ),
10397
+ [layers]
10398
+ );
10399
+ React66__default.default.useEffect(() => {
10400
+ prevActiveIdsRef.current = new Set(activeItems.map((item) => item.id));
10401
+ prevLayerIdsRef.current = new Set(layers.map((item) => item.id));
10402
+ }, [activeItems, layers]);
10347
10403
  const handleDismiss = (id) => {
10348
10404
  setDismissed((prev) => {
10349
10405
  const next = new Set(prev);
@@ -10368,7 +10424,8 @@ var StackedNotifications = React66__default.default.forwardRef((_a, ref) => {
10368
10424
  item,
10369
10425
  depth,
10370
10426
  revealStep: peekStep,
10371
- onDismiss: handleDismiss
10427
+ onDismiss: handleDismiss,
10428
+ isBackfill: backfillIds.has(item.id)
10372
10429
  },
10373
10430
  item.id
10374
10431
  )) })
@@ -12381,7 +12438,11 @@ var StyledTab = styled51__default.default(reactNative.Pressable)(
12381
12438
  alignItems: "center",
12382
12439
  justifyContent: "center",
12383
12440
  gap: tabGap,
12384
- height: tabHeight,
12441
+ // `minHeight` (not a fixed `height`) so a tab whose label wraps to more
12442
+ // than one line — e.g. long translated copy in `fixed` layout — grows to
12443
+ // fit instead of clipping/overflowing. The list's `alignItems: "stretch"`
12444
+ // then matches every tab to the tallest, keeping labels vertically aligned.
12445
+ minHeight: tabHeight,
12385
12446
  minWidth: tabMinWidth,
12386
12447
  paddingTop: tabPaddingTop,
12387
12448
  paddingBottom: tabPaddingBottom,
@@ -12438,7 +12499,7 @@ var TabNavigationTab = React66__default.default.forwardRef(
12438
12499
  children: [
12439
12500
  avatar && /* @__PURE__ */ jsxRuntime.jsx(Avatar, __spreadValues({}, avatar)),
12440
12501
  icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, customColour: textColour, "aria-hidden": true }),
12441
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { token, color: textColour, children })
12502
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { token, color: textColour, align: "center", children })
12442
12503
  ]
12443
12504
  })
12444
12505
  ) });
@@ -12561,6 +12622,7 @@ exports.Notification = Notification;
12561
12622
  exports.NumberField = NumberField;
12562
12623
  exports.NumberInput = NumberInput;
12563
12624
  exports.NumberInputField = NumberInputField;
12625
+ exports.PROGRESS_ANIMATION = PROGRESS_ANIMATION;
12564
12626
  exports.PasswordField = PasswordField;
12565
12627
  exports.PasswordFieldInput = PasswordFieldInput;
12566
12628
  exports.PasswordFieldRequirements = PasswordFieldRequirements;