@hzblj/kara-components 0.13.2 → 0.14.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/lib/module/atoms/activity-indicator/activity-indicator.android.js +14 -7
  3. package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -1
  4. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +12 -2
  5. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -1
  6. package/lib/module/atoms/date-picker/date-picker.android.js +13 -1
  7. package/lib/module/atoms/date-picker/date-picker.android.js.map +1 -1
  8. package/lib/module/atoms/date-picker/date-picker.ios.js +19 -12
  9. package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
  10. package/lib/module/atoms/picker/picker.android.js +48 -41
  11. package/lib/module/atoms/picker/picker.android.js.map +1 -1
  12. package/lib/module/atoms/picker/picker.ios.js +25 -2
  13. package/lib/module/atoms/picker/picker.ios.js.map +1 -1
  14. package/lib/module/atoms/segmented-control/segmented-control.android.js +25 -18
  15. package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
  16. package/lib/module/atoms/segmented-control/segmented-control.ios.js +15 -2
  17. package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
  18. package/lib/module/atoms/text/spans.js +44 -0
  19. package/lib/module/atoms/text/spans.js.map +1 -0
  20. package/lib/module/atoms/text/text.android.js +68 -3
  21. package/lib/module/atoms/text/text.android.js.map +1 -1
  22. package/lib/module/atoms/text/text.ios.js +15 -4
  23. package/lib/module/atoms/text/text.ios.js.map +1 -1
  24. package/lib/module/host-context.js +8 -0
  25. package/lib/module/host-context.js.map +1 -1
  26. package/lib/typescript/atoms/activity-indicator/activity-indicator.ios.d.ts +4 -1
  27. package/lib/typescript/atoms/activity-indicator/activity-indicator.types.d.ts +2 -1
  28. package/lib/typescript/atoms/date-picker/date-picker.ios.d.ts +2 -1
  29. package/lib/typescript/atoms/date-picker/date-picker.types.d.ts +1 -1
  30. package/lib/typescript/atoms/picker/picker.ios.d.ts +5 -0
  31. package/lib/typescript/atoms/picker/picker.types.d.ts +6 -2
  32. package/lib/typescript/atoms/segmented-control/segmented-control.ios.d.ts +2 -1
  33. package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +2 -1
  34. package/lib/typescript/atoms/text/spans.d.ts +14 -0
  35. package/lib/typescript/atoms/text/text.types.d.ts +8 -1
  36. package/lib/typescript/host-context.d.ts +7 -0
  37. package/package.json +4 -4
  38. package/src/atoms/activity-indicator/activity-indicator.android.tsx +18 -5
  39. package/src/atoms/activity-indicator/activity-indicator.ios.tsx +10 -2
  40. package/src/atoms/activity-indicator/activity-indicator.types.ts +2 -1
  41. package/src/atoms/date-picker/date-picker.android.tsx +15 -1
  42. package/src/atoms/date-picker/date-picker.ios.tsx +20 -9
  43. package/src/atoms/date-picker/date-picker.types.ts +1 -1
  44. package/src/atoms/picker/picker.android.tsx +53 -44
  45. package/src/atoms/picker/picker.ios.tsx +28 -7
  46. package/src/atoms/picker/picker.types.ts +6 -2
  47. package/src/atoms/segmented-control/segmented-control.android.tsx +29 -18
  48. package/src/atoms/segmented-control/segmented-control.ios.tsx +10 -2
  49. package/src/atoms/segmented-control/segmented-control.types.ts +2 -1
  50. package/src/atoms/text/spans.ts +45 -0
  51. package/src/atoms/text/text.android.tsx +69 -3
  52. package/src/atoms/text/text.ios.tsx +36 -4
  53. package/src/atoms/text/text.types.ts +8 -1
  54. package/src/host-context.tsx +8 -0
@@ -1,13 +1,21 @@
1
1
  "use strict";
2
2
 
3
+ import { RNHostView } from '@expo/ui/swift-ui';
3
4
  import { SegmentedControl as NativeSegmentedControl } from '@hzblj/kara-native/swift-ui';
4
5
  import { useAppTheme } from '@hzblj/kara-theme';
5
6
  import { useCallback, useMemo } from 'react';
7
+ import { useInsideHost } from "../../host-context.js";
6
8
  import { jsx as _jsx } from "react/jsx-runtime";
9
+ const MATCH_HEIGHT_ONLY = {
10
+ horizontal: false,
11
+ vertical: true
12
+ };
13
+
7
14
  /**
8
15
  * kara's own `UISegmentedControl`, with the pill in the accent and white text on it — the system control tints only the
9
16
  * pill, and SwiftUI's segmented picker not even that. A plain `UIView` in React Native layout: it needs no host, takes
10
- * the width it is given and UIKit's height. UIKit plays the control's own feedback.
17
+ * the width it is given and UIKit's height. Inside a `Screen` or `Island` it is carried into that host by `RNHostView`
18
+ * (see `useInsideHost`), the host's width and its own height. UIKit plays the control's own feedback.
11
19
  */
12
20
  export const SegmentedControl = ({
13
21
  disabled = false,
@@ -17,6 +25,7 @@ export const SegmentedControl = ({
17
25
  values
18
26
  }) => {
19
27
  const theme = useAppTheme();
28
+ const insideHost = useInsideHost();
20
29
  const face = theme.typography.footnoteMedium;
21
30
 
22
31
  // The native control speaks in values; a segment's is its index, so two labels may read the same.
@@ -28,7 +37,7 @@ export const SegmentedControl = ({
28
37
  const index = Number(value);
29
38
  onChange?.(index, values[index] ?? '');
30
39
  }, [onChange, values]);
31
- return /*#__PURE__*/_jsx(NativeSegmentedControl, {
40
+ const control = /*#__PURE__*/_jsx(NativeSegmentedControl, {
32
41
  activePillColor: theme.color('fill.accent.primary'),
33
42
  activeTextColor: theme.color('content.on.accent'),
34
43
  disabled: disabled,
@@ -40,5 +49,9 @@ export const SegmentedControl = ({
40
49
  selectedValue: String(selectedIndex),
41
50
  testID: testID
42
51
  });
52
+ return insideHost ? /*#__PURE__*/_jsx(RNHostView, {
53
+ matchContents: MATCH_HEIGHT_ONLY,
54
+ children: control
55
+ }) : control;
43
56
  };
44
57
  //# sourceMappingURL=segmented-control.ios.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["SegmentedControl","NativeSegmentedControl","useAppTheme","useCallback","useMemo","jsx","_jsx","disabled","onChange","selectedIndex","testID","values","theme","face","typography","footnoteMedium","options","map","label","index","value","String","onValueChange","Number","activePillColor","color","activeTextColor","fontFamily","fontSize","inactiveTextColor","selectedValue"],"sourceRoot":"../../../../src","sources":["atoms/segmented-control/segmented-control.ios.tsx"],"mappings":";;AAAA,SAAQA,gBAAgB,IAAIC,sBAAsB,QAAO,6BAA6B;AACtF,SAAQC,WAAW,QAAO,mBAAmB;AAC7C,SAAQC,WAAW,EAAEC,OAAO,QAAO,OAAO;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAI1C;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMN,gBAA2C,GAAGA,CAAC;EAC1DO,QAAQ,GAAG,KAAK;EAChBC,QAAQ;EACRC,aAAa;EACbC,MAAM;EACNC;AACqB,CAAC,KAAK;EAC3B,MAAMC,KAAK,GAAGV,WAAW,CAAC,CAAC;EAC3B,MAAMW,IAAI,GAAGD,KAAK,CAACE,UAAU,CAACC,cAAc;;EAE5C;EACA,MAAMC,OAAO,GAAGZ,OAAO,CAAC,MAAMO,MAAM,CAACM,GAAG,CAAC,CAACC,KAAK,EAAEC,KAAK,MAAM;IAACD,KAAK;IAAEE,KAAK,EAAEC,MAAM,CAACF,KAAK;EAAC,CAAC,CAAC,CAAC,EAAE,CAACR,MAAM,CAAC,CAAC;EAEtG,MAAMW,aAAa,GAAGnB,WAAW,CAC9BiB,KAAa,IAAK;IACjB,MAAMD,KAAK,GAAGI,MAAM,CAACH,KAAK,CAAC;IAC3BZ,QAAQ,GAAGW,KAAK,EAAER,MAAM,CAACQ,KAAK,CAAC,IAAI,EAAE,CAAC;EACxC,CAAC,EACD,CAACX,QAAQ,EAAEG,MAAM,CACnB,CAAC;EAED,oBACEL,IAAA,CAACL,sBAAsB;IACrBuB,eAAe,EAAEZ,KAAK,CAACa,KAAK,CAAC,qBAAqB,CAAE;IACpDC,eAAe,EAAEd,KAAK,CAACa,KAAK,CAAC,mBAAmB,CAAE;IAClDlB,QAAQ,EAAEA,QAAS;IACnBoB,UAAU,EAAEd,IAAI,CAACc,UAAW;IAC5BC,QAAQ,EAAEf,IAAI,CAACe,QAAS;IACxBC,iBAAiB,EAAEjB,KAAK,CAACa,KAAK,CAAC,iBAAiB,CAAE;IAClDH,aAAa,EAAEA,aAAc;IAC7BN,OAAO,EAAEA,OAAQ;IACjBc,aAAa,EAAET,MAAM,CAACZ,aAAa,CAAE;IACrCC,MAAM,EAAEA;EAAO,CAChB,CAAC;AAEN,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["RNHostView","SegmentedControl","NativeSegmentedControl","useAppTheme","useCallback","useMemo","useInsideHost","jsx","_jsx","MATCH_HEIGHT_ONLY","horizontal","vertical","disabled","onChange","selectedIndex","testID","values","theme","insideHost","face","typography","footnoteMedium","options","map","label","index","value","String","onValueChange","Number","control","activePillColor","color","activeTextColor","fontFamily","fontSize","inactiveTextColor","selectedValue","matchContents","children"],"sourceRoot":"../../../../src","sources":["atoms/segmented-control/segmented-control.ios.tsx"],"mappings":";;AAAA,SAAQA,UAAU,QAAO,mBAAmB;AAC5C,SAAQC,gBAAgB,IAAIC,sBAAsB,QAAO,6BAA6B;AACtF,SAAQC,WAAW,QAAO,mBAAmB;AAC7C,SAAQC,WAAW,EAAEC,OAAO,QAAO,OAAO;AAE1C,SAAQC,aAAa,QAAO,uBAAoB;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAGhD,MAAMC,iBAAiB,GAAG;EAACC,UAAU,EAAE,KAAK;EAAEC,QAAQ,EAAE;AAAI,CAAC;;AAE7D;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMV,gBAA2C,GAAGA,CAAC;EAC1DW,QAAQ,GAAG,KAAK;EAChBC,QAAQ;EACRC,aAAa;EACbC,MAAM;EACNC;AACqB,CAAC,KAAK;EAC3B,MAAMC,KAAK,GAAGd,WAAW,CAAC,CAAC;EAC3B,MAAMe,UAAU,GAAGZ,aAAa,CAAC,CAAC;EAClC,MAAMa,IAAI,GAAGF,KAAK,CAACG,UAAU,CAACC,cAAc;;EAE5C;EACA,MAAMC,OAAO,GAAGjB,OAAO,CAAC,MAAMW,MAAM,CAACO,GAAG,CAAC,CAACC,KAAK,EAAEC,KAAK,MAAM;IAACD,KAAK;IAAEE,KAAK,EAAEC,MAAM,CAACF,KAAK;EAAC,CAAC,CAAC,CAAC,EAAE,CAACT,MAAM,CAAC,CAAC;EAEtG,MAAMY,aAAa,GAAGxB,WAAW,CAC9BsB,KAAa,IAAK;IACjB,MAAMD,KAAK,GAAGI,MAAM,CAACH,KAAK,CAAC;IAC3Bb,QAAQ,GAAGY,KAAK,EAAET,MAAM,CAACS,KAAK,CAAC,IAAI,EAAE,CAAC;EACxC,CAAC,EACD,CAACZ,QAAQ,EAAEG,MAAM,CACnB,CAAC;EAED,MAAMc,OAAO,gBACXtB,IAAA,CAACN,sBAAsB;IACrB6B,eAAe,EAAEd,KAAK,CAACe,KAAK,CAAC,qBAAqB,CAAE;IACpDC,eAAe,EAAEhB,KAAK,CAACe,KAAK,CAAC,mBAAmB,CAAE;IAClDpB,QAAQ,EAAEA,QAAS;IACnBsB,UAAU,EAAEf,IAAI,CAACe,UAAW;IAC5BC,QAAQ,EAAEhB,IAAI,CAACgB,QAAS;IACxBC,iBAAiB,EAAEnB,KAAK,CAACe,KAAK,CAAC,iBAAiB,CAAE;IAClDJ,aAAa,EAAEA,aAAc;IAC7BN,OAAO,EAAEA,OAAQ;IACjBe,aAAa,EAAEV,MAAM,CAACb,aAAa,CAAE;IACrCC,MAAM,EAAEA;EAAO,CAChB,CACF;EAED,OAAOG,UAAU,gBAAGV,IAAA,CAACR,UAAU;IAACsC,aAAa,EAAE7B,iBAAkB;IAAA8B,QAAA,EAAET;EAAO,CAAa,CAAC,GAAGA,OAAO;AACpG,CAAC","ignoreList":[]}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ import { Children, isValidElement } from 'react';
4
+
5
+ /** A run of the text: a string child, or a nested `Text` with a colour of its own. */
6
+
7
+ const textOf = children => Children.toArray(children).map(child => typeof child === 'string' || typeof child === 'number' ? String(child) : '').join('');
8
+
9
+ /**
10
+ * The children as runs when any of them is a nested `Text` (`type`, the platform's own component), `null` when they
11
+ * are only text. A span keeps its `color`; anything else it sets is the outer text's.
12
+ */
13
+ export const spansOf = (children, type) => {
14
+ const items = Children.toArray(children);
15
+ if (!items.some(child => /*#__PURE__*/isValidElement(child) && child.type === type)) {
16
+ return null;
17
+ }
18
+ return items.flatMap(child => {
19
+ if (typeof child === 'string' || typeof child === 'number') {
20
+ return [{
21
+ text: String(child)
22
+ }];
23
+ }
24
+ if (/*#__PURE__*/isValidElement(child) && child.type === type) {
25
+ return [{
26
+ color: child.props.color,
27
+ text: textOf(child.props.children)
28
+ }];
29
+ }
30
+ return [];
31
+ });
32
+ };
33
+
34
+ /** The colours and the text folded into one number, for SwiftUI's `.animation(_:value:)`. A 32-bit FNV-1a hash. */
35
+ export const spansKey = (color, spans, text) => {
36
+ let hash = 0x811c9dc5;
37
+ const value = spans ? spans.map(span => `${span.color ?? ''}:${span.text}`).join('|') : text;
38
+ for (const character of `${color}|${value}`) {
39
+ hash ^= character.charCodeAt(0);
40
+ hash = Math.imul(hash, 0x01000193);
41
+ }
42
+ return hash >>> 0;
43
+ };
44
+ //# sourceMappingURL=spans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Children","isValidElement","textOf","children","toArray","map","child","String","join","spansOf","type","items","some","flatMap","text","color","props","spansKey","spans","hash","value","span","character","charCodeAt","Math","imul"],"sourceRoot":"../../../../src","sources":["atoms/text/spans.ts"],"mappings":";;AACA,SAAQA,QAAQ,EAAEC,cAAc,QAAuB,OAAO;;AAE9D;;AAGA,MAAMC,MAAM,GAAIC,QAAmB,IACjCH,QAAQ,CAACI,OAAO,CAACD,QAAQ,CAAC,CACvBE,GAAG,CAACC,KAAK,IAAK,OAAOA,KAAK,KAAK,QAAQ,IAAI,OAAOA,KAAK,KAAK,QAAQ,GAAGC,MAAM,CAACD,KAAK,CAAC,GAAG,EAAG,CAAC,CAC3FE,IAAI,CAAC,EAAE,CAAC;;AAEb;AACA;AACA;AACA;AACA,OAAO,MAAMC,OAAO,GAAGA,CAACN,QAAmB,EAAEO,IAAa,KAAwB;EAChF,MAAMC,KAAK,GAAGX,QAAQ,CAACI,OAAO,CAACD,QAAQ,CAAC;EAExC,IAAI,CAACQ,KAAK,CAACC,IAAI,CAACN,KAAK,IAAI,aAAAL,cAAc,CAACK,KAAK,CAAC,IAAIA,KAAK,CAACI,IAAI,KAAKA,IAAI,CAAC,EAAE;IACtE,OAAO,IAAI;EACb;EAEA,OAAOC,KAAK,CAACE,OAAO,CAAWP,KAAK,IAAI;IACtC,IAAI,OAAOA,KAAK,KAAK,QAAQ,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE;MAC1D,OAAO,CAAC;QAACQ,IAAI,EAAEP,MAAM,CAACD,KAAK;MAAC,CAAC,CAAC;IAChC;IACA,IAAI,aAAAL,cAAc,CAA6CK,KAAK,CAAC,IAAIA,KAAK,CAACI,IAAI,KAAKA,IAAI,EAAE;MAC5F,OAAO,CAAC;QAACK,KAAK,EAAET,KAAK,CAACU,KAAK,CAACD,KAAK;QAAED,IAAI,EAAEZ,MAAM,CAACI,KAAK,CAACU,KAAK,CAACb,QAAQ;MAAC,CAAC,CAAC;IACzE;IACA,OAAO,EAAE;EACX,CAAC,CAAC;AACJ,CAAC;;AAED;AACA,OAAO,MAAMc,QAAQ,GAAGA,CAACF,KAAiB,EAAEG,KAAiC,EAAEJ,IAAY,KAAa;EACtG,IAAIK,IAAI,GAAG,UAAU;EACrB,MAAMC,KAAK,GAAGF,KAAK,GAAGA,KAAK,CAACb,GAAG,CAACgB,IAAI,IAAI,GAAGA,IAAI,CAACN,KAAK,IAAI,EAAE,IAAIM,IAAI,CAACP,IAAI,EAAE,CAAC,CAACN,IAAI,CAAC,GAAG,CAAC,GAAGM,IAAI;EAE5F,KAAK,MAAMQ,SAAS,IAAI,GAAGP,KAAK,IAAIK,KAAK,EAAE,EAAE;IAC3CD,IAAI,IAAIG,SAAS,CAACC,UAAU,CAAC,CAAC,CAAC;IAC/BJ,IAAI,GAAGK,IAAI,CAACC,IAAI,CAACN,IAAI,EAAE,UAAU,CAAC;EACpC;EAEA,OAAOA,IAAI,KAAK,CAAC;AACnB,CAAC","ignoreList":[]}
@@ -2,10 +2,12 @@
2
2
 
3
3
  import { Text as ComposeText } from '@expo/ui/jetpack-compose';
4
4
  import { testID as testIdModifier } from '@expo/ui/jetpack-compose/modifiers';
5
- import { splitAccessibility, useAccessibilityModifiers, useSxModifiers } from '@hzblj/kara-sx';
6
- import { useAppTheme } from '@hzblj/kara-theme';
5
+ import { AnimatedText } from '@hzblj/kara-native/jetpack-compose';
6
+ import { animateConfig, splitAccessibility, useAccessibilityModifiers, useSxModifiers } from '@hzblj/kara-sx';
7
+ import { duration, easing, springPhysics, useAppTheme } from '@hzblj/kara-theme';
7
8
  import { useMemo } from 'react';
8
9
  import { useHostGuard } from "../../host-context.js";
10
+ import { spansOf } from "./spans.js";
9
11
  import { jsx as _jsx } from "react/jsx-runtime";
10
12
  const DECORATION = {
11
13
  none: 'none',
@@ -19,6 +21,41 @@ const applyTransform = (children, transform) => {
19
21
  return transform === 'uppercase' ? children.toUpperCase() : children.toLowerCase();
20
22
  };
21
23
  const decorationOf = decoration => decoration === undefined ? undefined : DECORATION[decoration];
24
+ const transformed = (text, transform) => transform === 'uppercase' ? text.toUpperCase() : transform === 'lowercase' ? text.toLowerCase() : text;
25
+
26
+ /**
27
+ * `animate` for the spans' colours, as `@hzblj/kara-sx` animates the rest on Compose: a spring token's physics, and
28
+ * `smoothOut` as a no-bounce spring over its duration; the other curves as tweens along their béziers.
29
+ */
30
+ const textAnimation = config => {
31
+ if (config.spring) {
32
+ const {
33
+ dampingRatio,
34
+ stiffness
35
+ } = springPhysics(config.spring);
36
+ return {
37
+ dampingRatio,
38
+ stiffness,
39
+ type: 'spring'
40
+ };
41
+ }
42
+ const milliseconds = duration[config.duration ?? 'fast'];
43
+ const curve = config.easing ?? 'smoothOut';
44
+ if (curve === 'smoothOut') {
45
+ const omega = 2 * Math.PI * 1000 / milliseconds;
46
+ return {
47
+ dampingRatio: 1,
48
+ stiffness: omega * omega,
49
+ type: 'spring'
50
+ };
51
+ }
52
+ return {
53
+ delayMillis: config.delay,
54
+ durationMillis: milliseconds,
55
+ easing: easing[curve],
56
+ type: 'tween'
57
+ };
58
+ };
22
59
  export const Text = ({
23
60
  align,
24
61
  children,
@@ -37,6 +74,31 @@ export const Text = ({
37
74
  const sxModifiers = useSxModifiers(sx);
38
75
  const accessibilityModifiers = useAccessibilityModifiers(accessibility);
39
76
  const modifiers = useMemo(() => [...sxModifiers, ...(testID ? [testIdModifier(testID)] : []), ...accessibilityModifiers], [accessibilityModifiers, sxModifiers, testID]);
77
+ const spans = useMemo(() => spansOf(children, Text), [children]);
78
+ const animate = sx.animate;
79
+
80
+ // `@expo/ui`'s text takes spans but animates none: with `animate`, kara's own Compose text eases each span's colour.
81
+ if (animate !== undefined && animate !== false) {
82
+ const runs = spans ?? [{
83
+ text: typeof children === 'string' || typeof children === 'number' ? String(children) : ''
84
+ }];
85
+ return /*#__PURE__*/_jsx(AnimatedText, {
86
+ animation: textAnimation(animateConfig(animate)),
87
+ fontFamily: style.fontFamily,
88
+ fontSize: style.fontSize,
89
+ fontWeight: style.fontWeight,
90
+ lineHeight: style.lineHeight,
91
+ maxLines: numberOfLines,
92
+ modifiers: modifiers,
93
+ spans: runs.map(span => ({
94
+ color: span.color ? theme.colorString(span.color) : undefined,
95
+ text: transformed(span.text, transform)
96
+ })),
97
+ textAlign: align,
98
+ textColor: theme.colorString(color),
99
+ textDecoration: decorationOf(decoration)
100
+ });
101
+ }
40
102
  return /*#__PURE__*/_jsx(ComposeText, {
41
103
  color: theme.colorString(color),
42
104
  maxLines: numberOfLines,
@@ -49,7 +111,10 @@ export const Text = ({
49
111
  textAlign: align,
50
112
  textDecoration: decorationOf(decoration)
51
113
  },
52
- children: applyTransform(children, transform)
114
+ children: spans ? spans.map((span, index) => /*#__PURE__*/_jsx(ComposeText, {
115
+ color: span.color ? theme.colorString(span.color) : undefined,
116
+ children: transformed(span.text, transform)
117
+ }, index)) : applyTransform(children, transform)
53
118
  });
54
119
  };
55
120
  //# sourceMappingURL=text.android.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Text","ComposeText","testID","testIdModifier","splitAccessibility","useAccessibilityModifiers","useSxModifiers","useAppTheme","useMemo","useHostGuard","jsx","_jsx","DECORATION","none","strikethrough","underline","applyTransform","children","transform","toUpperCase","toLowerCase","decorationOf","decoration","undefined","align","color","numberOfLines","variant","props","theme","style","typography","accessibility","sx","sxModifiers","accessibilityModifiers","modifiers","colorString","maxLines","fontFamily","fontSize","fontWeight","lineHeight","textAlign","textDecoration"],"sourceRoot":"../../../../src","sources":["atoms/text/text.android.tsx"],"mappings":";;AAAA,SAAQA,IAAI,IAAIC,WAAW,QAAO,0BAA0B;AAC5D,SAAQC,MAAM,IAAIC,cAAc,QAAO,oCAAoC;AAC3E,SAAQC,kBAAkB,EAAEC,yBAAyB,EAAEC,cAAc,QAAO,gBAAgB;AAC5F,SAAQC,WAAW,QAAO,mBAAmB;AAC7C,SAAQC,OAAO,QAAO,OAAO;AAE7B,SAAQC,YAAY,QAAO,uBAAoB;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAG/C,MAAMC,UAAU,GAAG;EACjBC,IAAI,EAAE,MAAM;EACZC,aAAa,EAAE,aAAa;EAC5BC,SAAS,EAAE;AACb,CAAU;AAEV,MAAMC,cAAc,GAAGA,CAACC,QAA+B,EAAEC,SAAoC,KAAK;EAChG,IAAI,CAACA,SAAS,IAAI,OAAOD,QAAQ,KAAK,QAAQ,EAAE;IAC9C,OAAOA,QAAQ;EACjB;EAEA,OAAOC,SAAS,KAAK,WAAW,GAAGD,QAAQ,CAACE,WAAW,CAAC,CAAC,GAAGF,QAAQ,CAACG,WAAW,CAAC,CAAC;AACpF,CAAC;AAED,MAAMC,YAAY,GAAIC,UAAsC,IAC1DA,UAAU,KAAKC,SAAS,GAAGA,SAAS,GAAGX,UAAU,CAACU,UAAU,CAAC;AAE/D,OAAO,MAAMtB,IAAmB,GAAGA,CAAC;EAClCwB,KAAK;EACLP,QAAQ;EACRQ,KAAK,GAAG,iBAAiB;EACzBH,UAAU;EACVI,aAAa;EACbxB,MAAM;EACNgB,SAAS;EACTS,OAAO,GAAG,MAAM;EAChB,GAAGC;AACM,CAAC,KAAK;EACfnB,YAAY,CAAC,MAAM,CAAC;EACpB,MAAMoB,KAAK,GAAGtB,WAAW,CAAC,CAAC;EAC3B,MAAMuB,KAAK,GAAGD,KAAK,CAACE,UAAU,CAACJ,OAAO,CAAC;EACvC,MAAM,CAACK,aAAa,EAAEC,EAAE,CAAC,GAAG7B,kBAAkB,CAACwB,KAAK,CAAC;EACrD,MAAMM,WAAW,GAAG5B,cAAc,CAAC2B,EAAE,CAAC;EACtC,MAAME,sBAAsB,GAAG9B,yBAAyB,CAAC2B,aAAa,CAAC;EACvE,MAAMI,SAAS,GAAG5B,OAAO,CACvB,MAAM,CAAC,GAAG0B,WAAW,EAAE,IAAIhC,MAAM,GAAG,CAACC,cAAc,CAACD,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,GAAGiC,sBAAsB,CAAC,EAC9F,CAACA,sBAAsB,EAAED,WAAW,EAAEhC,MAAM,CAC9C,CAAC;EAED,oBACES,IAAA,CAACV,WAAW;IACVwB,KAAK,EAAEI,KAAK,CAACQ,WAAW,CAACZ,KAAK,CAAE;IAChCa,QAAQ,EAAEZ,aAAc;IACxBU,SAAS,EAAEA,SAAU;IACrBN,KAAK,EAAE;MACLS,UAAU,EAAET,KAAK,CAACS,UAAU;MAC5BC,QAAQ,EAAEV,KAAK,CAACU,QAAQ;MACxBC,UAAU,EAAEX,KAAK,CAACW,UAAU;MAC5BC,UAAU,EAAEZ,KAAK,CAACY,UAAU;MAC5BC,SAAS,EAAEnB,KAAK;MAChBoB,cAAc,EAAEvB,YAAY,CAACC,UAAU;IACzC,CAAE;IAAAL,QAAA,EAEDD,cAAc,CAACC,QAAQ,EAAEC,SAAS;EAAC,CACzB,CAAC;AAElB,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["Text","ComposeText","testID","testIdModifier","AnimatedText","animateConfig","splitAccessibility","useAccessibilityModifiers","useSxModifiers","duration","easing","springPhysics","useAppTheme","useMemo","useHostGuard","spansOf","jsx","_jsx","DECORATION","none","strikethrough","underline","applyTransform","children","transform","toUpperCase","toLowerCase","decorationOf","decoration","undefined","transformed","text","textAnimation","config","spring","dampingRatio","stiffness","type","milliseconds","curve","omega","Math","PI","delayMillis","delay","durationMillis","align","color","numberOfLines","variant","props","theme","style","typography","accessibility","sx","sxModifiers","accessibilityModifiers","modifiers","spans","animate","runs","String","animation","fontFamily","fontSize","fontWeight","lineHeight","maxLines","map","span","colorString","textAlign","textColor","textDecoration","index"],"sourceRoot":"../../../../src","sources":["atoms/text/text.android.tsx"],"mappings":"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","ignoreList":[]}
@@ -3,10 +3,11 @@
3
3
  import { Text as SwiftUIText } from '@expo/ui/swift-ui';
4
4
  import { foregroundStyle, lineLimit, lineSpacing, multilineTextAlignment, strikethrough, textCase, underline } from '@expo/ui/swift-ui/modifiers';
5
5
  import { scaledFont } from '@hzblj/kara-native/swift-ui';
6
- import { splitAccessibility, useAccessibilityModifiers, useSxModifiers } from '@hzblj/kara-sx';
6
+ import { splitAccessibility, textColorTransition, useAccessibilityModifiers, useSxModifiers } from '@hzblj/kara-sx';
7
7
  import { useAppTheme } from '@hzblj/kara-theme';
8
8
  import { useMemo } from 'react';
9
9
  import { useHostGuard } from "../../host-context.js";
10
+ import { spansKey, spansOf } from "./spans.js";
10
11
  import { jsx as _jsx } from "react/jsx-runtime";
11
12
  const ALIGNMENT = {
12
13
  center: 'center',
@@ -38,6 +39,10 @@ export const Text = ({
38
39
  const [accessibility, sx] = splitAccessibility(props);
39
40
  const sxModifiers = useSxModifiers(sx, FRAME_ALIGNMENT[align ?? 'left']);
40
41
  const accessibilityModifiers = useAccessibilityModifiers(accessibility);
42
+ const spans = useMemo(() => spansOf(children, Text), [children]);
43
+ const animate = sx.animate;
44
+ // With `animate`, SwiftUI interpolates the text when a colour changes: the text's own or a span's.
45
+ const transition = useMemo(() => textColorTransition(animate, spansKey(color, spans, typeof children === 'string' ? children : '')), [animate, children, color, spans]);
41
46
  const modifiers = useMemo(() => {
42
47
  const list = [
43
48
  // Not `@expo/ui`'s `font()`: for the platform's face it gives either Apple's size or a size that ignores Dynamic
@@ -69,12 +74,18 @@ export const Text = ({
69
74
  if (numberOfLines !== undefined) {
70
75
  list.push(lineLimit(numberOfLines));
71
76
  }
72
- return [...list, ...sxModifiers, ...accessibilityModifiers];
73
- }, [accessibilityModifiers, align, color, decoration, numberOfLines, style, sxModifiers, theme, transform]);
77
+ return [...list, ...transition, ...sxModifiers, ...accessibilityModifiers];
78
+ }, [accessibilityModifiers, align, color, decoration, numberOfLines, style, sxModifiers, theme, transform, transition]);
74
79
  return /*#__PURE__*/_jsx(SwiftUIText, {
75
80
  modifiers: modifiers,
76
81
  testID: testID,
77
- children: children
82
+ children: spans ? spans.map((span, index) =>
83
+ /*#__PURE__*/
84
+ // Concatenated into one SwiftUI `Text`: a span takes its colour, the font and the rest are the outer text's.
85
+ _jsx(SwiftUIText, {
86
+ modifiers: span.color ? [foregroundStyle(theme.color(span.color))] : undefined,
87
+ children: span.text
88
+ }, index)) : children
78
89
  });
79
90
  };
80
91
  //# sourceMappingURL=text.ios.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Text","SwiftUIText","foregroundStyle","lineLimit","lineSpacing","multilineTextAlignment","strikethrough","textCase","underline","scaledFont","splitAccessibility","useAccessibilityModifiers","useSxModifiers","useAppTheme","useMemo","useHostGuard","jsx","_jsx","ALIGNMENT","center","left","right","FRAME_ALIGNMENT","align","children","color","decoration","numberOfLines","testID","transform","variant","props","theme","style","typography","accessibility","sx","sxModifiers","accessibilityModifiers","modifiers","list","family","fontFamily","size","fontSize","textStyle","scalesWith","weight","Math","max","lineHeight","push","isActive","pattern","undefined"],"sourceRoot":"../../../../src","sources":["atoms/text/text.ios.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["Text","SwiftUIText","foregroundStyle","lineLimit","lineSpacing","multilineTextAlignment","strikethrough","textCase","underline","scaledFont","splitAccessibility","textColorTransition","useAccessibilityModifiers","useSxModifiers","useAppTheme","useMemo","useHostGuard","spansKey","spansOf","jsx","_jsx","ALIGNMENT","center","left","right","FRAME_ALIGNMENT","align","children","color","decoration","numberOfLines","testID","transform","variant","props","theme","style","typography","accessibility","sx","sxModifiers","accessibilityModifiers","spans","animate","transition","modifiers","list","family","fontFamily","size","fontSize","textStyle","scalesWith","weight","Math","max","lineHeight","push","isActive","pattern","undefined","map","span","index","text"],"sourceRoot":"../../../../src","sources":["atoms/text/text.ios.tsx"],"mappings":"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","ignoreList":[]}
@@ -9,6 +9,14 @@ export const HostBoundary = ({
9
9
  value: true,
10
10
  children: children
11
11
  });
12
+
13
+ /**
14
+ * Whether this renders inside a `Screen` or `Island` already. An atom that brings its own host renders straight into
15
+ * that one instead: on Android a host nested in another is a real view beside the outer host's own `ComposeView`, and
16
+ * the outer host (a horizontal `LinearLayout`) measures its `ComposeView` in the width the nested one leaves over,
17
+ * which for a full-width nested host is none.
18
+ */
19
+ export const useInsideHost = () => useContext(HostContext);
12
20
  export const useHostGuard = atom => {
13
21
  const insideHost = useContext(HostContext);
14
22
  if (__DEV__ && !insideHost) {
@@ -1 +1 @@
1
- {"version":3,"names":["createContext","useContext","jsx","_jsx","HostContext","HostBoundary","children","Provider","value","useHostGuard","atom","insideHost","__DEV__","console","warn"],"sourceRoot":"../../src","sources":["host-context.tsx"],"mappings":";;AAAA,SAAQA,aAAa,EAAkBC,UAAU,QAAO,OAAO;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAE/D,MAAMC,WAAW,gBAAGJ,aAAa,CAAC,KAAK,CAAC;AAExC,OAAO,MAAMK,YAAY,GAAGA,CAAC;EAACC;AAA+B,CAAC,kBAC5DH,IAAA,CAACC,WAAW,CAACG,QAAQ;EAACC,KAAK;EAAAF,QAAA,EAAEA;AAAQ,CAAuB,CAC7D;AAED,OAAO,MAAMG,YAAY,GAAIC,IAAY,IAAK;EAC5C,MAAMC,UAAU,GAAGV,UAAU,CAACG,WAAW,CAAC;EAE1C,IAAIQ,OAAO,IAAI,CAACD,UAAU,EAAE;IAC1BE,OAAO,CAACC,IAAI,CACV,cAAcJ,IAAI,mGACpB,CAAC;EACH;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["createContext","useContext","jsx","_jsx","HostContext","HostBoundary","children","Provider","value","useInsideHost","useHostGuard","atom","insideHost","__DEV__","console","warn"],"sourceRoot":"../../src","sources":["host-context.tsx"],"mappings":";;AAAA,SAAQA,aAAa,EAAkBC,UAAU,QAAO,OAAO;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAE/D,MAAMC,WAAW,gBAAGJ,aAAa,CAAC,KAAK,CAAC;AAExC,OAAO,MAAMK,YAAY,GAAGA,CAAC;EAACC;AAA+B,CAAC,kBAC5DH,IAAA,CAACC,WAAW,CAACG,QAAQ;EAACC,KAAK;EAAAF,QAAA,EAAEA;AAAQ,CAAuB,CAC7D;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMG,aAAa,GAAGA,CAAA,KAAMR,UAAU,CAACG,WAAW,CAAC;AAE1D,OAAO,MAAMM,YAAY,GAAIC,IAAY,IAAK;EAC5C,MAAMC,UAAU,GAAGX,UAAU,CAACG,WAAW,CAAC;EAE1C,IAAIS,OAAO,IAAI,CAACD,UAAU,EAAE;IAC1BE,OAAO,CAACC,IAAI,CACV,cAAcJ,IAAI,mGACpB,CAAC;EACH;AACF,CAAC","ignoreList":[]}
@@ -1,3 +1,6 @@
1
1
  import type { ActivityIndicatorComponent } from './activity-indicator.types';
2
- /** React Native's spinner, which on iOS is UIKit's own `UIActivityIndicatorView`. */
2
+ /**
3
+ * React Native's spinner, which on iOS is UIKit's own `UIActivityIndicatorView`. Inside a `Screen` or `Island` it is
4
+ * carried into that host by `RNHostView`, sized to the spinner (see `useInsideHost`).
5
+ */
3
6
  export declare const ActivityIndicator: ActivityIndicatorComponent;
@@ -4,7 +4,8 @@ export type ActivityIndicatorSize = 'large' | 'small';
4
4
  /**
5
5
  * That something is loading, for as long as it takes: UIKit's spinner on iOS, Material 3's expressive
6
6
  * `LoadingIndicator` on Android, its shapes morphing as it turns. A React Native view on iOS and a host of its own on
7
- * Android, so it renders anywhere. For how far along something is, use `Progress`.
7
+ * Android, so it renders anywhere; inside a `Screen` or `Island` it joins that host, beside other native views. For how
8
+ * far along something is, use `Progress`.
8
9
  */
9
10
  export type ActivityIndicatorProps = {
10
11
  /** What is loading, for VoiceOver and TalkBack: "Loading photos". Unset, the platform's own word ("In progress"). */
@@ -3,6 +3,7 @@ import type { DatePickerComponent } from './date-picker.types';
3
3
  * The system's compact date picker: the value in a pill, and a tap on it opens the calendar popover UIKit owns. Its own
4
4
  * host, sized to the pill on both axes — a host that took the width it was given would lay the pill out against the
5
5
  * far edge of it, or past its container's — so it drops into a row the way a `Text` does. The label is the caller's,
6
- * beside it; the calendar is in the accent.
6
+ * beside it; the calendar is in the accent. Inside a `Screen` or `Island` it renders into that host instead (see
7
+ * `useInsideHost`).
7
8
  */
8
9
  export declare const DatePicker: DatePickerComponent;
@@ -3,7 +3,7 @@ export type DatePickerMode = 'date' | 'datetime' | 'time';
3
3
  /**
4
4
  * A date or time picker: SwiftUI's compact `DatePicker` on iOS, a field that opens Material's picker dialog on a tap
5
5
  * on Android. The React Native tier: it brings its own host and sizes itself to the value, so it sits in a row beside
6
- * its label like a `Text`.
6
+ * its label like a `Text`. Inside a `Screen` or `Island` it joins that host instead, beside a native `Text` label.
7
7
  */
8
8
  export type DatePickerProps = {
9
9
  /** The latest date that can be picked. Android ignores it in `time` mode. */
@@ -1,2 +1,7 @@
1
1
  import type { PickerComponent } from './picker.types';
2
+ /**
3
+ * A SwiftUI wheel. On its own it is `@expo/ui`'s community picker, which brings a host of its own. Inside a `Screen` or
4
+ * `Island` it is the same wheel rendered into that host instead (see `useInsideHost`): a host nested in the island's
5
+ * stack is given no height there, and the wheel spills over the views around it.
6
+ */
2
7
  export declare const Picker: PickerComponent;
@@ -8,7 +8,8 @@ export type PickerOption = {
8
8
  };
9
9
  /**
10
10
  * A single choice from a list: a SwiftUI wheel picker on iOS, Material's exposed dropdown on Android. It brings its
11
- * own host, so it renders outside `Screen` and `Island` as well.
11
+ * own host, so it renders outside `Screen` and `Island` as well. Inside one it renders into that host instead, so it
12
+ * can share a stack with other native views.
12
13
  */
13
14
  export type PickerProps = {
14
15
  /** Stops the user changing the selection, and dims the picker. */
@@ -17,7 +18,10 @@ export type PickerProps = {
17
18
  onValueChange?: (value: string) => void;
18
19
  /** The choices, in order. */
19
20
  options: PickerOption[];
20
- /** Android: the dropdown field's test tag. iOS: `@expo/ui`'s wheel picker drops it. */
21
+ /**
22
+ * Android: the dropdown field's test tag. iOS: the wheel's, inside a `Screen` or `Island` only; on its own,
23
+ * `@expo/ui`'s wheel picker drops it.
24
+ */
21
25
  testID?: string;
22
26
  /** The selected option's `value`. Controlled: pass back what `onValueChange` reports. */
23
27
  value: string;
@@ -2,6 +2,7 @@ import type { SegmentedControlComponent } from './segmented-control.types';
2
2
  /**
3
3
  * kara's own `UISegmentedControl`, with the pill in the accent and white text on it — the system control tints only the
4
4
  * pill, and SwiftUI's segmented picker not even that. A plain `UIView` in React Native layout: it needs no host, takes
5
- * the width it is given and UIKit's height. UIKit plays the control's own feedback.
5
+ * the width it is given and UIKit's height. Inside a `Screen` or `Island` it is carried into that host by `RNHostView`
6
+ * (see `useInsideHost`), the host's width and its own height. UIKit plays the control's own feedback.
6
7
  */
7
8
  export declare const SegmentedControl: SegmentedControlComponent;
@@ -2,7 +2,8 @@ import type { ReactNode } from 'react';
2
2
  /**
3
3
  * A row of mutually exclusive segments, the selected one in the accent: kara's own `UISegmentedControl` on iOS,
4
4
  * Material's single-choice segmented buttons on Android. It needs no `Screen` or `Island`: iOS is a plain view, Android
5
- * brings its own host. It takes the width it is given; the height is the platform's.
5
+ * brings its own host. Inside one it joins that host, so it can share a stack with other native views. It takes the
6
+ * width it is given; the height is the platform's.
6
7
  */
7
8
  export type SegmentedControlProps = {
8
9
  /** Stops the user changing the segment, and dims the control. */
@@ -0,0 +1,14 @@
1
+ import type { ColorToken } from '@hzblj/kara-theme';
2
+ import { type ReactNode } from 'react';
3
+ /** A run of the text: a string child, or a nested `Text` with a colour of its own. */
4
+ export type TextSpan = {
5
+ color?: ColorToken;
6
+ text: string;
7
+ };
8
+ /**
9
+ * The children as runs when any of them is a nested `Text` (`type`, the platform's own component), `null` when they
10
+ * are only text. A span keeps its `color`; anything else it sets is the outer text's.
11
+ */
12
+ export declare const spansOf: (children: ReactNode, type: unknown) => TextSpan[] | null;
13
+ /** The colours and the text folded into one number, for SwiftUI's `.animation(_:value:)`. A 32-bit FNV-1a hash. */
14
+ export declare const spansKey: (color: ColorToken, spans: readonly TextSpan[] | null, text: string) => number;
@@ -8,6 +8,10 @@ export type TextTransform = 'lowercase' | 'uppercase';
8
8
  * SwiftUI `Text` / Compose `Text` in a theme typography variant. A native atom: outside a host, use `RnText`. It takes
9
9
  * the native `sx` surface too: padding, a frame (`wide` lines it up by `align`), a background, and the `ios` bucket's
10
10
  * `contentTransition`, `animation` and `monospacedDigit` for a number that changes in place.
11
+ *
12
+ * Nested `Text` children are spans: runs of the same text in a colour of their own, one paragraph that wraps as one.
13
+ * With `animate`, a colour that changes (the text's or a span's) eases to its new value instead of switching: a word lit
14
+ * as it is said, a phrase brought forward. SwiftUI interpolates the text, Compose animates each span's colour.
11
15
  */
12
16
  export type TextProps = ResponsiveSx & AccessibilityProps & {
13
17
  /**
@@ -15,7 +19,10 @@ export type TextProps = ResponsiveSx & AccessibilityProps & {
15
19
  * layout; Android keeps them as they are.
16
20
  */
17
21
  align?: TextAlign;
18
- /** The text. */
22
+ /**
23
+ * The text: a string, or strings and nested `Text` spans, each in a `color` of its own. A span takes only its
24
+ * colour; the variant, alignment and everything else are the outer text's.
25
+ */
19
26
  children?: ReactNode;
20
27
  /** The text's colour. `content.primary` by default. */
21
28
  color?: ColorToken;
@@ -2,4 +2,11 @@ import { type ReactNode } from 'react';
2
2
  export declare const HostBoundary: ({ children }: {
3
3
  children: ReactNode;
4
4
  }) => import("react").JSX.Element;
5
+ /**
6
+ * Whether this renders inside a `Screen` or `Island` already. An atom that brings its own host renders straight into
7
+ * that one instead: on Android a host nested in another is a real view beside the outer host's own `ComposeView`, and
8
+ * the outer host (a horizontal `LinearLayout`) measures its `ComposeView` in the width the nested one leaves over,
9
+ * which for a full-width nested host is none.
10
+ */
11
+ export declare const useInsideHost: () => boolean;
5
12
  export declare const useHostGuard: (atom: string) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzblj/kara-components",
3
- "version": "0.13.2",
3
+ "version": "0.14.1",
4
4
  "description": "The atoms, molecules, organisms and templates of @hzblj/kara-ui — real SwiftUI and Jetpack Compose views through @expo/ui.",
5
5
  "author": "Jan Blazej",
6
6
  "license": "MIT",
@@ -61,9 +61,9 @@
61
61
  "dependencies": {
62
62
  "@hzblj/expo-native-icons": "^0.1.0",
63
63
  "@hzblj/expo-rich-haptics": "^0.1.0",
64
- "@hzblj/kara-native": "^0.13.2",
65
- "@hzblj/kara-sx": "^0.13.2",
66
- "@hzblj/kara-theme": "^0.13.2",
64
+ "@hzblj/kara-native": "^0.14.1",
65
+ "@hzblj/kara-sx": "^0.14.1",
66
+ "@hzblj/kara-theme": "^0.14.1",
67
67
  "sf-symbols-typescript": "^2.2.0"
68
68
  },
69
69
  "peerDependencies": {
@@ -1,10 +1,11 @@
1
1
  import {Host, LoadingIndicator} from '@expo/ui/jetpack-compose'
2
- import {fillMaxSize, testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
2
+ import {fillMaxSize, size as sizeModifier, testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
3
3
  import {useAccessibilityModifiers} from '@hzblj/kara-sx'
4
4
  import {useAppTheme} from '@hzblj/kara-theme'
5
5
  import {useMemo} from 'react'
6
6
  import {StyleSheet} from 'react-native'
7
7
 
8
+ import {useInsideHost} from '../../host-context'
8
9
  import type {ActivityIndicatorComponent, ActivityIndicatorProps} from './activity-indicator.types'
9
10
 
10
11
  /** Material's 48dp for a screen; half of it in a row, about where React Native's small spinner sat. */
@@ -15,7 +16,8 @@ const styles = StyleSheet.create({
15
16
 
16
17
  /**
17
18
  * Material 3's expressive `LoadingIndicator` in a host of its own, sized here rather than measured, so it renders
18
- * anywhere and takes its place in the first frame.
19
+ * anywhere and takes its place in the first frame. Inside a `Screen` or `Island` it renders into that host instead (see
20
+ * `useInsideHost`), the same size.
19
21
  */
20
22
  export const ActivityIndicator: ActivityIndicatorComponent = ({
21
23
  accessibilityLabel,
@@ -24,15 +26,26 @@ export const ActivityIndicator: ActivityIndicatorComponent = ({
24
26
  testID,
25
27
  }: ActivityIndicatorProps) => {
26
28
  const theme = useAppTheme()
29
+ const insideHost = useInsideHost()
27
30
  const accessibilityModifiers = useAccessibilityModifiers({accessibilityLabel})
28
31
  const modifiers = useMemo(
29
- () => [fillMaxSize(), ...(testID ? [testIdModifier(testID)] : []), ...accessibilityModifiers],
30
- [accessibilityModifiers, testID]
32
+ () => [
33
+ insideHost ? sizeModifier(styles[size].width, styles[size].height) : fillMaxSize(),
34
+ ...(testID ? [testIdModifier(testID)] : []),
35
+ ...accessibilityModifiers,
36
+ ],
37
+ [accessibilityModifiers, insideHost, size, testID]
31
38
  )
32
39
 
40
+ const indicator = <LoadingIndicator color={theme.color(color)} modifiers={modifiers} />
41
+
42
+ if (insideHost) {
43
+ return indicator
44
+ }
45
+
33
46
  return (
34
47
  <Host colorScheme={theme.dark ? 'dark' : 'light'} seedColor={theme.materialSeed} style={styles[size]}>
35
- <LoadingIndicator color={theme.color(color)} modifiers={modifiers} />
48
+ {indicator}
36
49
  </Host>
37
50
  )
38
51
  }
@@ -1,9 +1,14 @@
1
+ import {RNHostView} from '@expo/ui/swift-ui'
1
2
  import {useAppTheme} from '@hzblj/kara-theme'
2
3
  import {ActivityIndicator as RNActivityIndicator} from 'react-native'
3
4
 
5
+ import {useInsideHost} from '../../host-context'
4
6
  import type {ActivityIndicatorComponent, ActivityIndicatorProps} from './activity-indicator.types'
5
7
 
6
- /** React Native's spinner, which on iOS is UIKit's own `UIActivityIndicatorView`. */
8
+ /**
9
+ * React Native's spinner, which on iOS is UIKit's own `UIActivityIndicatorView`. Inside a `Screen` or `Island` it is
10
+ * carried into that host by `RNHostView`, sized to the spinner (see `useInsideHost`).
11
+ */
7
12
  export const ActivityIndicator: ActivityIndicatorComponent = ({
8
13
  accessibilityLabel,
9
14
  color = 'fill.accent.primary',
@@ -11,8 +16,9 @@ export const ActivityIndicator: ActivityIndicatorComponent = ({
11
16
  testID,
12
17
  }: ActivityIndicatorProps) => {
13
18
  const theme = useAppTheme()
19
+ const insideHost = useInsideHost()
14
20
 
15
- return (
21
+ const spinner = (
16
22
  <RNActivityIndicator
17
23
  accessibilityLabel={accessibilityLabel}
18
24
  color={theme.color(color)}
@@ -20,4 +26,6 @@ export const ActivityIndicator: ActivityIndicatorComponent = ({
20
26
  testID={testID}
21
27
  />
22
28
  )
29
+
30
+ return insideHost ? <RNHostView matchContents>{spinner}</RNHostView> : spinner
23
31
  }
@@ -6,7 +6,8 @@ export type ActivityIndicatorSize = 'large' | 'small'
6
6
  /**
7
7
  * That something is loading, for as long as it takes: UIKit's spinner on iOS, Material 3's expressive
8
8
  * `LoadingIndicator` on Android, its shapes morphing as it turns. A React Native view on iOS and a host of its own on
9
- * Android, so it renders anywhere. For how far along something is, use `Progress`.
9
+ * Android, so it renders anywhere; inside a `Screen` or `Island` it joins that host, beside other native views. For how
10
+ * far along something is, use `Progress`.
10
11
  */
11
12
  export type ActivityIndicatorProps = {
12
13
  /** What is loading, for VoiceOver and TalkBack: "Loading photos". Unset, the platform's own word ("In progress"). */