@streamplace/components 0.8.5 → 0.8.8

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 (29) hide show
  1. package/dist/components/content-metadata/content-metadata-form.js +6 -22
  2. package/dist/components/content-metadata/content-rights.js +18 -47
  3. package/dist/components/content-metadata/content-warning-badge.js +42 -0
  4. package/dist/components/content-metadata/content-warnings.js +15 -48
  5. package/dist/components/content-metadata/index.js +3 -1
  6. package/dist/components/mobile-player/ui/viewer-context-menu.js +17 -11
  7. package/dist/components/ui/checkbox.js +6 -18
  8. package/dist/components/ui/dropdown.js +16 -11
  9. package/dist/components/ui/index.js +2 -0
  10. package/dist/components/ui/primitives/text.js +19 -4
  11. package/dist/components/ui/select.js +44 -75
  12. package/dist/hooks/useLivestreamInfo.js +1 -1
  13. package/dist/streamplace-store/stream.js +11 -9
  14. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  15. package/package.json +2 -2
  16. package/src/components/content-metadata/content-metadata-form.tsx +9 -49
  17. package/src/components/content-metadata/content-rights.tsx +31 -56
  18. package/src/components/content-metadata/content-warning-badge.tsx +94 -0
  19. package/src/components/content-metadata/content-warnings.tsx +46 -58
  20. package/src/components/content-metadata/index.tsx +2 -0
  21. package/src/components/mobile-player/ui/viewer-context-menu.tsx +33 -1
  22. package/src/components/ui/checkbox.tsx +23 -21
  23. package/src/components/ui/dropdown.tsx +60 -40
  24. package/src/components/ui/index.ts +2 -0
  25. package/src/components/ui/primitives/text.tsx +24 -4
  26. package/src/components/ui/select.tsx +97 -125
  27. package/src/hooks/useLivestreamInfo.ts +1 -1
  28. package/src/streamplace-store/stream.tsx +16 -10
  29. package/tsconfig.tsbuildinfo +1 -1
@@ -242,27 +242,13 @@ exports.ContentMetadataForm = (0, react_1.forwardRef)(({ showUpdateButton = fals
242
242
  layout.flex.row,
243
243
  layout.flex.alignCenter,
244
244
  w.percent[100],
245
- ], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: [
246
- text.neutral[300],
247
- {
248
- minWidth: 100,
249
- textAlign: "left",
250
- paddingBottom: 8,
251
- fontSize: 14,
252
- },
253
- ], children: "Content Warnings" }), (0, jsx_runtime_1.jsx)(text_1.Text, { style: [text.gray[500], { fontSize: 12, paddingBottom: 8 }], children: "optional" })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [gap.all[2], w.percent[100]], children: metadata_constants_1.CONTENT_WARNINGS.map((warning) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [w.percent[100]], children: (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { content: warning.description, position: "top", children: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { checked: contentWarnings.includes(warning.value), onCheckedChange: (checked) => handleContentWarningChange(warning.value, checked), label: warning.label, style: [{ fontSize: 12 }] }) }) }, warning.value))) })] })), activeSection === "contentRights" && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [gap.all[3], w.percent[100]], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
245
+ gap.all[2],
246
+ ], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { children: "Content Warnings" }), (0, jsx_runtime_1.jsx)(text_1.Text, { muted: true, children: "(optional)" })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [gap.all[2], w.percent[100]], children: metadata_constants_1.CONTENT_WARNINGS.map((warning) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [w.percent[100]], children: (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { content: warning.description, position: "top", children: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { checked: contentWarnings.includes(warning.value), onCheckedChange: (checked) => handleContentWarningChange(warning.value, checked), label: warning.label, style: [{ fontSize: 12 }] }) }) }, warning.value))) })] })), activeSection === "contentRights" && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [gap.all[3], w.percent[100]], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
254
247
  layout.flex.row,
255
248
  layout.flex.alignCenter,
256
249
  w.percent[100],
257
- ], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: [
258
- text.neutral[300],
259
- {
260
- minWidth: 100,
261
- textAlign: "left",
262
- paddingBottom: 8,
263
- fontSize: 14,
264
- },
265
- ], children: "Content Rights" }), (0, jsx_runtime_1.jsx)(text_1.Text, { style: [text.gray[500], { fontSize: 12, paddingBottom: 8 }], children: "optional" })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [gap.all[3], w.percent[100]], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
250
+ gap.all[2],
251
+ ], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { children: "Content Rights" }), (0, jsx_runtime_1.jsx)(text_1.Text, { muted: true, children: "(optional)" })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [gap.all[3], w.percent[100]], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
266
252
  layout.flex.row,
267
253
  layout.flex.alignCenter,
268
254
  w.percent[100],
@@ -378,10 +364,8 @@ exports.ContentMetadataForm = (0, react_1.forwardRef)(({ showUpdateButton = fals
378
364
  layout.flex.row,
379
365
  layout.flex.alignCenter,
380
366
  w.percent[100],
381
- ], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: [
382
- text.neutral[300],
383
- { minWidth: 100, textAlign: "left", paddingBottom: 8 },
384
- ], children: "Distribution" }), (0, jsx_runtime_1.jsx)(text_1.Text, { style: [text.gray[500], { fontSize: 12, paddingBottom: 8 }], children: "optional" })] }), (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { content: "Distribution of your content is unlimited, but they still have to respect the deleteAfter policy below.", position: "top", children: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { checked: distributionPolicy.allowedBroadcasters?.includes("*") ||
367
+ gap.all[2],
368
+ ], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { children: "Distribution" }), (0, jsx_runtime_1.jsx)(text_1.Text, { muted: true, children: "(optional)" })] }), (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { content: "Distribution of your content is unlimited, but they still have to respect the deleteAfter policy below.", position: "top", children: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { checked: distributionPolicy.allowedBroadcasters?.includes("*") ||
385
369
  false, onCheckedChange: (checked) => handleDistributionPolicyChange({
386
370
  allowedBroadcasters: checked
387
371
  ? "*"
@@ -4,15 +4,15 @@ exports.ContentRights = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const react_native_1 = require("react-native");
7
+ const __1 = require("../..");
7
8
  const metadata_constants_1 = require("../../lib/metadata-constants");
8
- const theme_1 = require("../../lib/theme/theme");
9
9
  const text_1 = require("../ui/text");
10
- exports.ContentRights = (0, react_1.forwardRef)(({ contentRights }, ref) => {
11
- const { theme } = (0, theme_1.useTheme)();
10
+ const { layout, gap, mt, text: textStyles } = __1.zero;
11
+ exports.ContentRights = (0, react_1.forwardRef)(({ contentRights, compact, ...rest }, ref) => {
12
+ const { zero } = (0, __1.useTheme)();
12
13
  if (!contentRights || Object.keys(contentRights).length === 0) {
13
14
  return null;
14
15
  }
15
- const styles = createStyles(theme);
16
16
  const formatLicense = (license) => {
17
17
  return metadata_constants_1.LICENSE_URL_LABELS[license] || license;
18
18
  };
@@ -23,7 +23,10 @@ exports.ContentRights = (0, react_1.forwardRef)(({ contentRights }, ref) => {
23
23
  // elements.push(`Creator: ${contentRights.creator}`);
24
24
  // }
25
25
  if (contentRights.copyrightYear) {
26
- elements.push(`© ${contentRights.copyrightYear.toString()}`);
26
+ elements.push(`© ${contentRights.copyrightYear.toString()}${contentRights.creditLine ? " " + contentRights.creditLine : ""}`);
27
+ }
28
+ else if (contentRights.creditLine) {
29
+ elements.push(contentRights.creditLine);
27
30
  }
28
31
  if (contentRights.license) {
29
32
  elements.push(formatLicense(contentRights.license));
@@ -31,48 +34,16 @@ exports.ContentRights = (0, react_1.forwardRef)(({ contentRights }, ref) => {
31
34
  if (contentRights.copyrightNotice) {
32
35
  elements.push(contentRights.copyrightNotice);
33
36
  }
34
- if (contentRights.creditLine) {
35
- elements.push(contentRights.creditLine);
37
+ if (elements.length > 0) {
38
+ elements[0] = "Stream content is " + elements[0];
39
+ }
40
+ if (elements.length == 0) {
41
+ return null;
36
42
  }
37
- return ((0, jsx_runtime_1.jsx)(react_native_1.View, { ref: ref, style: styles.compactContainer, children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.compactText, children: elements.join(" • ") }) }));
43
+ return ((0, jsx_runtime_1.jsx)(text_1.Text, { ref: ref, style: [
44
+ zero.text.mutedForeground,
45
+ mt[1],
46
+ react_native_1.StyleSheet.flatten(rest.style),
47
+ ], ...rest, children: elements.join(" • ") }));
38
48
  });
39
49
  exports.ContentRights.displayName = "ContentRights";
40
- function createStyles(theme) {
41
- return react_native_1.StyleSheet.create({
42
- container: {
43
- paddingVertical: theme.spacing[3],
44
- },
45
- title: {
46
- fontSize: 14,
47
- fontWeight: "600",
48
- color: theme.colors.text,
49
- marginBottom: theme.spacing[2],
50
- },
51
- content: {
52
- gap: theme.spacing[2],
53
- },
54
- row: {
55
- flexDirection: "row",
56
- gap: theme.spacing[2],
57
- },
58
- label: {
59
- fontSize: 13,
60
- color: theme.colors.textMuted,
61
- },
62
- value: {
63
- fontSize: 13,
64
- color: theme.colors.text,
65
- },
66
- compactContainer: {
67
- flexDirection: "row",
68
- gap: theme.spacing[2],
69
- flexWrap: "wrap",
70
- marginTop: theme.spacing[1],
71
- },
72
- compactText: {
73
- fontSize: 14,
74
- fontWeight: "500",
75
- color: theme.colors.text,
76
- },
77
- });
78
- }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentWarningBadge = ContentWarningBadge;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const lucide_react_native_1 = require("lucide-react-native");
6
+ const react_native_1 = require("react-native");
7
+ const __1 = require("../..");
8
+ const metadata_constants_1 = require("../../lib/metadata-constants");
9
+ const theme_1 = require("../../lib/theme/theme");
10
+ const ui_1 = require("../../ui");
11
+ const dropdown_1 = require("../ui/dropdown");
12
+ const text_1 = require("../ui/text");
13
+ const { px, py, gap, layout } = __1.zero;
14
+ function ContentWarningBadge({ warnings }) {
15
+ const { theme } = (0, theme_1.useTheme)();
16
+ const getWarningLabel = (warning) => {
17
+ return metadata_constants_1.C2PA_WARNING_LABELS[warning] || warning;
18
+ };
19
+ if (!warnings || warnings.length === 0) {
20
+ return null;
21
+ }
22
+ return ((0, jsx_runtime_1.jsxs)(dropdown_1.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(dropdown_1.DropdownMenuTrigger, { children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
23
+ layout.flex.row,
24
+ layout.flex.align.center,
25
+ gap.all[2],
26
+ px[3],
27
+ py[2],
28
+ ui_1.r.md,
29
+ { backgroundColor: theme.colors.warning + "20" },
30
+ ], children: [(0, jsx_runtime_1.jsx)(lucide_react_native_1.AlertTriangle, { size: 14, color: theme.colors.warningForeground }), (0, jsx_runtime_1.jsx)(text_1.Text, { size: "sm", weight: "semibold", style: { color: theme.colors.warningForeground }, children: "Intended for certain audiences" }), (0, jsx_runtime_1.jsx)(lucide_react_native_1.ChevronDown, { size: 14, color: theme.colors.warningForeground })] }) }), (0, jsx_runtime_1.jsxs)(dropdown_1.ResponsiveDropdownMenuContent, { children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [layout.flex.column, px[2], ui_1.pt[2]], children: [(0, jsx_runtime_1.jsx)(text_1.Text, { children: "Heads up!" }), (0, jsx_runtime_1.jsx)(text_1.Text, { children: "This stream may contain:" })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
31
+ layout.flex.row,
32
+ { flexWrap: "wrap" },
33
+ gap.all[2],
34
+ px[2],
35
+ py[2],
36
+ ], children: warnings.map((warning, index) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
37
+ { backgroundColor: theme.colors.warning },
38
+ px[3],
39
+ py[1],
40
+ ui_1.r.full,
41
+ ], children: (0, jsx_runtime_1.jsx)(text_1.Text, { size: "sm", weight: "semibold", style: { color: theme.colors.warningForeground }, children: getWarningLabel(warning) }) }, index))) })] })] }));
42
+ }
@@ -4,65 +4,32 @@ exports.ContentWarnings = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const react_native_1 = require("react-native");
7
+ const __1 = require("../..");
7
8
  const metadata_constants_1 = require("../../lib/metadata-constants");
8
9
  const theme_1 = require("../../lib/theme/theme");
9
10
  const text_1 = require("../ui/text");
10
- exports.ContentWarnings = (0, react_1.forwardRef)(({ warnings, compact = false }, ref) => {
11
+ const { layout, gap, bg, r, p, px, py, text: textStyles, borders } = __1.zero;
12
+ exports.ContentWarnings = (0, react_1.forwardRef)(({ warnings, compact = false, ...rest }, ref) => {
11
13
  const { theme } = (0, theme_1.useTheme)();
12
14
  if (!warnings || warnings.length === 0) {
13
15
  return null;
14
16
  }
15
- const styles = createStyles(theme, compact);
16
17
  const getWarningLabel = (warning) => {
17
18
  return metadata_constants_1.C2PA_WARNING_LABELS[warning] || warning;
18
19
  };
19
20
  if (compact) {
20
- return ((0, jsx_runtime_1.jsx)(react_native_1.View, { ref: ref, style: styles.compactContainer, children: warnings.map((warning, index) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.compactWarning, children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.compactWarningText, children: getWarningLabel(warning) }) }, index))) }));
21
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { ref: ref, style: [layout.flex.row, layout.flex.wrap.wrap, gap.all[1]], ...rest, children: warnings.map((warning, index) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
22
+ { backgroundColor: theme.colors.warning },
23
+ r.full,
24
+ px[2],
25
+ { paddingVertical: 2 },
26
+ ], children: (0, jsx_runtime_1.jsx)(text_1.Text, { size: "sm", style: [{ color: theme.colors.warningForeground }], children: getWarningLabel(warning) }) }, index))) }));
21
27
  }
22
- return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { ref: ref, style: styles.container, children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.title, children: "Content Warnings" }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.warningsContainer, children: warnings.map((warning, index) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.warning, children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.warningText, children: getWarningLabel(warning) }) }, index))) })] }));
28
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { ref: ref, style: [layout.flex.column, gap.all[2]], ...rest, children: [(0, jsx_runtime_1.jsx)(text_1.Text, { style: [{ fontSize: 14, fontWeight: "600" }, textStyles.gray[900]], children: "Content Warnings" }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [layout.flex.row, layout.flex.wrap.wrap, gap.all[2]], children: warnings.map((warning, index) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
29
+ { backgroundColor: theme.colors.warning },
30
+ r.full,
31
+ px[3],
32
+ py[1],
33
+ ], children: (0, jsx_runtime_1.jsx)(text_1.Text, { size: "sm", style: [{ color: theme.colors.warningForeground }], children: getWarningLabel(warning) }) }, index))) })] }));
23
34
  });
24
35
  exports.ContentWarnings.displayName = "ContentWarnings";
25
- function createStyles(theme, compact) {
26
- return react_native_1.StyleSheet.create({
27
- container: {
28
- flexDirection: "column",
29
- gap: theme.spacing[2],
30
- },
31
- title: {
32
- fontSize: 14,
33
- fontWeight: "600",
34
- color: theme.colors.text,
35
- },
36
- warningsContainer: {
37
- flexDirection: "row",
38
- flexWrap: "wrap",
39
- gap: theme.spacing[2],
40
- },
41
- warning: {
42
- backgroundColor: theme.colors.warning,
43
- borderRadius: theme.borderRadius.md,
44
- padding: theme.spacing[2],
45
- },
46
- warningText: {
47
- color: theme.colors.warningForeground,
48
- fontSize: 12,
49
- fontWeight: "500",
50
- },
51
- compactContainer: {
52
- flexDirection: "row",
53
- flexWrap: "wrap",
54
- gap: theme.spacing[1],
55
- },
56
- compactWarning: {
57
- backgroundColor: theme.colors.warning,
58
- borderRadius: theme.borderRadius.full,
59
- paddingHorizontal: 10,
60
- paddingVertical: 4,
61
- },
62
- compactWarningText: {
63
- color: theme.colors.warningForeground,
64
- fontSize: 14,
65
- fontWeight: "600",
66
- },
67
- });
68
- }
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContentWarnings = exports.ContentRights = exports.ContentMetadataForm = void 0;
3
+ exports.ContentWarnings = exports.ContentWarningBadge = exports.ContentRights = exports.ContentMetadataForm = void 0;
4
4
  // Main form component
5
5
  var content_metadata_form_1 = require("./content-metadata-form");
6
6
  Object.defineProperty(exports, "ContentMetadataForm", { enumerable: true, get: function () { return content_metadata_form_1.ContentMetadataForm; } });
7
7
  // Display components
8
8
  var content_rights_1 = require("./content-rights");
9
9
  Object.defineProperty(exports, "ContentRights", { enumerable: true, get: function () { return content_rights_1.ContentRights; } });
10
+ var content_warning_badge_1 = require("./content-warning-badge");
11
+ Object.defineProperty(exports, "ContentWarningBadge", { enumerable: true, get: function () { return content_warning_badge_1.ContentWarningBadge; } });
10
12
  var content_warnings_1 = require("./content-warnings");
11
13
  Object.defineProperty(exports, "ContentWarnings", { enumerable: true, get: function () { return content_warnings_1.ContentWarnings; } });
@@ -11,7 +11,8 @@ const theme_1 = require("../../../lib/theme");
11
11
  const livestream_store_1 = require("../../../livestream-store");
12
12
  const player_store_1 = require("../../../player-store/");
13
13
  const graph_1 = require("../../../streamplace-store/graph");
14
- const ui_1 = require("../../ui");
14
+ const ui_1 = require("../../../ui");
15
+ const ui_2 = require("../../ui");
15
16
  function ContextMenu({ dropdownPortalContainer, }) {
16
17
  const quality = (0, player_store_1.usePlayerStore)((x) => x.selectedRendition);
17
18
  const setQuality = (0, player_store_1.usePlayerStore)((x) => x.setSelectedRendition);
@@ -24,8 +25,13 @@ function ContextMenu({ dropdownPortalContainer, }) {
24
25
  const setReportModalOpen = (0, player_store_1.usePlayerStore)((x) => x.setReportModalOpen);
25
26
  const setReportSubject = (0, player_store_1.usePlayerStore)((x) => x.setReportSubject);
26
27
  const { profile } = (0, __1.useLivestreamInfo)();
28
+ console.log("profile", profile);
27
29
  const avatars = (0, __1.useAvatars)(profile?.did ? [profile?.did] : []);
28
30
  const ls = (0, livestream_store_1.useLivestreamStore)((x) => x.livestream);
31
+ const segment = (0, livestream_store_1.useLivestreamStore)((x) => x.segment);
32
+ // Get content rights from the latest segment
33
+ const contentRights = segment?.contentRights;
34
+ const contentWarnings = segment?.contentWarnings?.warnings || [];
29
35
  let graphManager = (0, graph_1.useGraphManager)(profile?.did);
30
36
  const lowLatency = protocol === "webrtc";
31
37
  const setLowLatency = (value) => {
@@ -34,12 +40,12 @@ function ContextMenu({ dropdownPortalContainer, }) {
34
40
  // are we on mobile? then do dropdowns
35
41
  const isMobile = react_native_1.Platform.OS === "ios" || react_native_1.Platform.OS === "android";
36
42
  // dummy portal for mobile
37
- const Portal = isMobile ? react_native_1.View : ui_1.DropdownMenuPortal;
43
+ const Portal = isMobile ? react_native_1.View : ui_2.DropdownMenuPortal;
38
44
  // render the responsive version on mobile as we can't fullscreen there
39
45
  const DropdownMenuContent = isMobile
40
- ? ui_1.ResponsiveDropdownMenuContent
41
- : ui_1.DropdownMenuContentWithoutPortal;
42
- return ((0, jsx_runtime_1.jsxs)(ui_1.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuTrigger, { children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.Menu, { color: theme_1.colors.gray[200] }) }), (0, jsx_runtime_1.jsx)(Portal, { container: dropdownPortalContainer, children: (0, jsx_runtime_1.jsxs)(DropdownMenuContent, { side: "top", align: "end", children: [react_native_1.Platform.OS !== "web" && ((0, jsx_runtime_1.jsxs)(ui_1.DropdownMenuGroup, { title: "Streamer", children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
46
+ ? ui_2.ResponsiveDropdownMenuContent
47
+ : ui_2.DropdownMenuContentWithoutPortal;
48
+ return ((0, jsx_runtime_1.jsxs)(ui_2.DropdownMenu, { children: [(0, jsx_runtime_1.jsx)(ui_2.DropdownMenuTrigger, { children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.Menu, { color: theme_1.colors.gray[200] }) }), (0, jsx_runtime_1.jsx)(Portal, { container: dropdownPortalContainer, children: (0, jsx_runtime_1.jsxs)(DropdownMenuContent, { side: "top", align: "end", children: [react_native_1.Platform.OS !== "web" && ((0, jsx_runtime_1.jsxs)(ui_2.DropdownMenuGroup, { title: "Streamer", children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
43
49
  __1.zero.layout.flex.row,
44
50
  __1.zero.layout.flex.center,
45
51
  __1.zero.gap.all[3],
@@ -55,7 +61,7 @@ function ContextMenu({ dropdownPortalContainer, }) {
55
61
  const url = `https://bsky.app/profile/${profile.handle}`;
56
62
  react_native_1.Linking.openURL(url);
57
63
  }
58
- }, children: (0, jsx_runtime_1.jsxs)(ui_1.Text, { children: ["@", profile?.handle || "user"] }) }) }), (0, jsx_runtime_1.jsx)(ui_1.Text, { color: "muted", size: "sm", numberOfLines: 2, ellipsizeMode: "tail", children: ls?.record.title || "Stream Title" })] })] }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuSeparator, {}), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { disabled: graphManager.isLoading || !profile?.did, onPress: async () => {
64
+ }, children: (0, jsx_runtime_1.jsxs)(ui_2.Text, { children: ["@", profile?.handle || "user"] }) }) }), (0, jsx_runtime_1.jsx)(ui_2.Text, { color: "muted", size: "sm", numberOfLines: 2, ellipsizeMode: "tail", children: ls?.record.title || "Stream Title" })] })] }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuSeparator, {}), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuItem, { disabled: graphManager.isLoading || !profile?.did, onPress: async () => {
59
65
  try {
60
66
  if (graphManager.isFollowing) {
61
67
  await graphManager.unfollow();
@@ -67,20 +73,20 @@ function ContextMenu({ dropdownPortalContainer, }) {
67
73
  catch (err) {
68
74
  console.error("Follow/unfollow error:", err);
69
75
  }
70
- }, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { color: graphManager.isFollowing ? "destructive" : "default", children: graphManager.isLoading
76
+ }, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { color: graphManager.isFollowing ? "destructive" : "default", children: graphManager.isLoading
71
77
  ? "Loading..."
72
78
  : graphManager.isFollowing
73
79
  ? "Unfollow"
74
- : "Follow" }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuSeparator, {}), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { onPress: () => {
80
+ : "Follow" }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuSeparator, {}), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuItem, { onPress: () => {
75
81
  if (profile?.handle) {
76
82
  const url = `https://bsky.app/profile/${profile.handle}`;
77
83
  react_native_1.Linking.openURL(url);
78
84
  }
79
- }, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "View Profile on Bluesky" }) })] })), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { title: "Resolution", children: (0, jsx_runtime_1.jsxs)(ui_1.DropdownMenuRadioGroup, { value: quality, onValueChange: setQuality, children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuRadioItem, { value: "source", children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Source (Original Quality)" }) }), qualities.map((r) => ((0, jsx_runtime_1.jsx)(ui_1.DropdownMenuRadioItem, { value: r.name, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: r.name }) })))] }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { title: "Advanced", children: (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuCheckboxItem, { checked: lowLatency, onCheckedChange: () => setLowLatency(!lowLatency), children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Low Latency" }) }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuInfo, { description: "Reduces the delay between video and chat for a more real-time experience." }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { children: (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuCheckboxItem, { checked: debugInfo, onCheckedChange: () => setShowDebugInfo(!debugInfo), children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Show Debug Info" }) }) }), (0, jsx_runtime_1.jsx)(ui_1.DropdownMenuGroup, { title: "Report", children: (0, jsx_runtime_1.jsx)(ReportButton, { livestream: livestream, setReportModalOpen: setReportModalOpen, setReportSubject: setReportSubject }) })] }) })] }));
85
+ }, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "View Profile on Bluesky" }) })] })), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { title: "Resolution", children: (0, jsx_runtime_1.jsxs)(ui_2.DropdownMenuRadioGroup, { value: quality, onValueChange: setQuality, children: [(0, jsx_runtime_1.jsx)(ui_2.DropdownMenuRadioItem, { value: "source", children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Source (Original Quality)" }) }), qualities.map((r) => ((0, jsx_runtime_1.jsx)(ui_2.DropdownMenuRadioItem, { value: r.name, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: r.name }) })))] }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { title: "Advanced", children: (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuCheckboxItem, { checked: lowLatency, onCheckedChange: () => setLowLatency(!lowLatency), children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Low Latency" }) }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuInfo, { description: "Reduces the delay between video and chat for a more real-time experience." }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { children: (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuCheckboxItem, { checked: debugInfo, onCheckedChange: () => setShowDebugInfo(!debugInfo), children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Show Debug Info" }) }) }), (0, jsx_runtime_1.jsx)(ui_2.DropdownMenuGroup, { title: "Report", children: (0, jsx_runtime_1.jsx)(ReportButton, { livestream: livestream, setReportModalOpen: setReportModalOpen, setReportSubject: setReportSubject }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [ui_1.pt[3], ui_1.px[2], ui_1.gap.all[2]], children: [contentWarnings && contentWarnings.length > 0 && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [ui_1.gap.all[1]], children: [(0, jsx_runtime_1.jsx)(ui_2.Text, { size: "base", color: "muted", children: "Stream may contain" }), (0, jsx_runtime_1.jsx)(__1.ContentWarnings, { warnings: contentWarnings, compact: true })] })), contentRights && Object.keys(contentRights).length > 0 && ((0, jsx_runtime_1.jsx)(__1.ContentRights, { contentRights: contentRights, size: "xs", color: "muted" }))] })] }) })] }));
80
86
  }
81
87
  function ReportButton({ livestream, setReportModalOpen, setReportSubject, }) {
82
88
  const { onOpenChange } = (0, dropdown_menu_1.useRootContext)();
83
- return ((0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { onPress: () => {
89
+ return ((0, jsx_runtime_1.jsx)(ui_2.DropdownMenuItem, { onPress: () => {
84
90
  if (!livestream)
85
91
  return;
86
92
  onOpenChange?.(false);
@@ -90,5 +96,5 @@ function ReportButton({ livestream, setReportModalOpen, setReportSubject, }) {
90
96
  uri: livestream.uri,
91
97
  cid: livestream.cid,
92
98
  });
93
- }, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { children: "Report Livestream..." }) }));
99
+ }, children: (0, jsx_runtime_1.jsx)(ui_2.Text, { children: "Report Livestream..." }) }));
94
100
  }
@@ -17,7 +17,7 @@ exports.Checkbox = (0, react_1.forwardRef)(({ checked, onCheckedChange, disabled
17
17
  const styles = createStyles(theme, size, disabled, checked);
18
18
  return ((0, jsx_runtime_1.jsxs)(react_native_1.TouchableOpacity, { ref: ref, style: [styles.container, style], onPress: handlePress, disabled: disabled, ...props, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.checkbox, children: checked && ((0, jsx_runtime_1.jsx)(lucide_react_native_1.Check, { size: size === "sm" ? 12 : size === "lg" ? 18 : 14, color: disabled
19
19
  ? theme.colors.textDisabled
20
- : theme.colors.primaryForeground })) }), (label || description) && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.content, children: [label && (0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.label, children: label }), description && ((0, jsx_runtime_1.jsx)(text_1.Text, { style: styles.description, children: description }))] }))] }));
20
+ : theme.colors.primaryForeground })) }), (label || description) && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.content, children: [label && ((0, jsx_runtime_1.jsx)(text_1.Text, { size: size === "sm" ? "sm" : size === "lg" ? "lg" : "base", color: disabled ? "muted" : "default", leading: "snug", children: label })), description && ((0, jsx_runtime_1.jsx)(text_1.Text, { size: size === "sm" ? "xs" : size === "lg" ? "base" : "sm", color: disabled ? "muted" : "muted", style: { marginTop: theme.spacing[1] }, children: description }))] }))] }));
21
21
  });
22
22
  exports.Checkbox.displayName = "Checkbox";
23
23
  function createStyles(theme, size, disabled, checked) {
@@ -32,13 +32,13 @@ function createStyles(theme, size, disabled, checked) {
32
32
  checkboxSize: 20,
33
33
  borderRadius: 4,
34
34
  padding: theme.spacing[1],
35
- gap: theme.spacing[2],
35
+ gap: theme.spacing[1],
36
36
  },
37
37
  lg: {
38
38
  checkboxSize: 24,
39
39
  borderRadius: 6,
40
- padding: theme.spacing[2],
41
- gap: theme.spacing[3],
40
+ padding: theme.spacing[1],
41
+ gap: theme.spacing[2],
42
42
  },
43
43
  };
44
44
  const currentSize = sizeStyles[size];
@@ -56,7 +56,7 @@ function createStyles(theme, size, disabled, checked) {
56
56
  ? theme.colors.border
57
57
  : checked
58
58
  ? theme.colors.primary
59
- : theme.colors.border,
59
+ : theme.colors.textMuted,
60
60
  borderRadius: currentSize.borderRadius,
61
61
  backgroundColor: disabled
62
62
  ? theme.colors.muted
@@ -68,20 +68,8 @@ function createStyles(theme, size, disabled, checked) {
68
68
  },
69
69
  content: {
70
70
  flex: 1,
71
- paddingTop: currentSize.padding * 0.5,
71
+ paddingTop: currentSize.padding * 0.25,
72
72
  paddingLeft: theme.spacing[2],
73
73
  },
74
- label: {
75
- fontSize: size === "sm" ? 14 : size === "lg" ? 18 : 16,
76
- fontWeight: "500",
77
- color: disabled ? theme.colors.textDisabled : theme.colors.text,
78
- lineHeight: size === "sm" ? 18 : size === "lg" ? 22 : 20,
79
- },
80
- description: {
81
- fontSize: size === "sm" ? 12 : size === "lg" ? 16 : 14,
82
- color: disabled ? theme.colors.textDisabled : theme.colors.textMuted,
83
- marginTop: theme.spacing[1],
84
- lineHeight: size === "sm" ? 16 : size === "lg" ? 20 : 18,
85
- },
86
74
  });
87
75
  }
@@ -9,6 +9,7 @@ const DropdownMenuPrimitive = tslib_1.__importStar(require("@rn-primitives/dropd
9
9
  const lucide_react_native_1 = require("lucide-react-native");
10
10
  const react_1 = require("react");
11
11
  const react_native_1 = require("react-native");
12
+ const __1 = require("../..");
12
13
  const atoms_1 = require("../../lib/theme/atoms");
13
14
  const ui_1 = require("../../ui");
14
15
  const text_1 = require("./primitives/text");
@@ -18,15 +19,12 @@ exports.DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
18
19
  exports.DropdownMenuPortal = DropdownMenuPrimitive.Portal;
19
20
  exports.DropdownMenuSub = DropdownMenuPrimitive.Sub;
20
21
  exports.DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
21
- exports.DropdownMenuBottomSheet = (0, react_1.forwardRef)(function DropdownMenuBottomSheet({ overlayStyle, portalHost, children }, _ref) {
22
+ exports.DropdownMenuBottomSheet = (0, react_1.forwardRef)(function DropdownMenuBottomSheet({ overlayStyle, portalHost, children, ...rest }, _ref) {
22
23
  // Use the primitives' context to know if open
23
24
  const { open, onOpenChange } = DropdownMenuPrimitive.useRootContext();
24
25
  const { zero: zt } = (0, ui_1.useTheme)();
25
- const snapPoints = (0, react_1.useMemo)(() => ["25%", "50%", "80%"], []);
26
26
  const sheetRef = (0, react_1.useRef)(null);
27
- return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { hostName: portalHost, children: (0, jsx_runtime_1.jsx)(bottom_sheet_1.default, { ref: sheetRef,
28
- // why the heck is this 1-indexed
29
- index: open ? 3 : -1, snapPoints: snapPoints, enablePanDownToClose: true, enableDynamicSizing: true, enableContentPanningGesture: false, backdropComponent: ({ style }) => ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [style, react_native_1.StyleSheet.absoluteFill], onPress: () => onOpenChange?.(false) })), onClose: () => onOpenChange?.(false), style: [overlayStyle], backgroundStyle: [zt.bg.popover, atoms_1.a.radius.all.md, atoms_1.a.shadows.md, atoms_1.p[1]], handleIndicatorStyle: [
27
+ return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { hostName: portalHost, children: (0, jsx_runtime_1.jsx)(bottom_sheet_1.default, { ref: sheetRef, enablePanDownToClose: true, enableDynamicSizing: true, enableContentPanningGesture: false, backdropComponent: ({ style }) => ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [style, react_native_1.StyleSheet.absoluteFill], onPress: () => onOpenChange?.(false) })), onClose: () => onOpenChange?.(false), style: [overlayStyle, react_native_1.StyleSheet.flatten(rest.style)], backgroundStyle: [zt.bg.popover, atoms_1.a.radius.all.md, atoms_1.a.shadows.md, atoms_1.p[1]], handleIndicatorStyle: [
30
28
  atoms_1.a.sizes.width[12],
31
29
  atoms_1.a.sizes.height[1],
32
30
  zt.bg.mutedForeground,
@@ -65,7 +63,7 @@ exports.DropdownMenuSubContent = (0, react_1.forwardRef)((props, ref) => {
65
63
  atoms_1.a.shadows.md,
66
64
  ], ...props }));
67
65
  });
68
- exports.DropdownMenuContent = (0, react_1.forwardRef)(({ overlayStyle, portalHost, ...props }, ref) => {
66
+ exports.DropdownMenuContent = (0, react_1.forwardRef)(({ overlayStyle, portalHost, style, children, ...props }, ref) => {
69
67
  const { zero: zt } = (0, ui_1.useTheme)();
70
68
  return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Portal, { hostName: portalHost, children: (0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Overlay, { style: [
71
69
  react_native_1.Platform.OS !== "web" ? react_native_1.StyleSheet.absoluteFill : undefined,
@@ -81,10 +79,15 @@ exports.DropdownMenuContent = (0, react_1.forwardRef)(({ overlayStyle, portalHos
81
79
  zt.bg.popover,
82
80
  atoms_1.p[2],
83
81
  atoms_1.a.shadows.md,
84
- ], ...props }) }) }));
82
+ style,
83
+ ], ...props, children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { showsVerticalScrollIndicator: true, children: typeof children === "function"
84
+ ? children({ pressed: false })
85
+ : children }) }) }) }));
85
86
  });
86
- exports.DropdownMenuContentWithoutPortal = (0, react_1.forwardRef)(({ overlayStyle, ...props }, ref) => {
87
+ exports.DropdownMenuContentWithoutPortal = (0, react_1.forwardRef)(({ overlayStyle, maxHeightPercentage = 0.8, children, style, ...props }, ref) => {
87
88
  const { theme } = (0, ui_1.useTheme)();
89
+ const { height } = (0, react_native_1.useWindowDimensions)();
90
+ const maxHeight = height * maxHeightPercentage;
88
91
  return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Overlay, { style: [
89
92
  react_native_1.Platform.OS !== "web" ? react_native_1.StyleSheet.absoluteFill : undefined,
90
93
  overlayStyle,
@@ -92,14 +95,16 @@ exports.DropdownMenuContentWithoutPortal = (0, react_1.forwardRef)(({ overlaySty
92
95
  { zIndex: 999999 },
93
96
  atoms_1.a.sizes.minWidth[32],
94
97
  atoms_1.a.sizes.maxWidth[64],
95
- atoms_1.a.overflow.hidden,
96
98
  atoms_1.a.radius.all.md,
97
99
  atoms_1.a.borders.width.thin,
98
100
  { borderColor: theme.colors.border },
99
101
  { backgroundColor: theme.colors.popover },
100
102
  atoms_1.p[2],
101
103
  atoms_1.a.shadows.md,
102
- ], ...props }) }));
104
+ style,
105
+ ], ...props, children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { style: { maxHeight }, showsVerticalScrollIndicator: true, children: typeof children === "function"
106
+ ? children({ pressed: false })
107
+ : children }) }) }));
103
108
  });
104
109
  /// Responsive Dropdown Menu Content. On mobile this will render a *bottom sheet* that is **portaled to the root of the app**.
105
110
  /// Prefer passing scoped content in as **otherwise it may crash the app**.
@@ -108,7 +113,7 @@ exports.ResponsiveDropdownMenuContent = (0, react_1.forwardRef)(({ children, ...
108
113
  // On web, you might want to always use the normal dropdown
109
114
  const isBottomSheet = react_native_1.Platform.OS !== "web" && width < 800;
110
115
  if (isBottomSheet) {
111
- return ((0, jsx_runtime_1.jsx)(exports.DropdownMenuBottomSheet, { ref: ref, ...props, children: children }));
116
+ return ((0, jsx_runtime_1.jsx)(exports.DropdownMenuBottomSheet, { ref: ref, ...props, children: (0, jsx_runtime_1.jsx)(react_native_1.ScrollView, { style: [__1.zero.pb[12]], children: children }) }));
112
117
  }
113
118
  return ((0, jsx_runtime_1.jsx)(exports.DropdownMenuContent, { ref: ref, ...props, children: children }));
114
119
  });
@@ -9,6 +9,7 @@ tslib_1.__exportStar(require("./primitives/modal"), exports);
9
9
  tslib_1.__exportStar(require("./primitives/text"), exports);
10
10
  // Export styled components
11
11
  tslib_1.__exportStar(require("./button"), exports);
12
+ tslib_1.__exportStar(require("./checkbox"), exports);
12
13
  tslib_1.__exportStar(require("./dialog"), exports);
13
14
  tslib_1.__exportStar(require("./dropdown"), exports);
14
15
  tslib_1.__exportStar(require("./icons"), exports);
@@ -21,6 +22,7 @@ tslib_1.__exportStar(require("./slider"), exports);
21
22
  tslib_1.__exportStar(require("./text"), exports);
22
23
  tslib_1.__exportStar(require("./textarea"), exports);
23
24
  tslib_1.__exportStar(require("./toast"), exports);
25
+ tslib_1.__exportStar(require("./tooltip"), exports);
24
26
  tslib_1.__exportStar(require("./view"), exports);
25
27
  // Component collections for easy importing
26
28
  var button_1 = require("./primitives/button");
@@ -137,6 +137,18 @@ exports.TextRoot = (0, react_1.forwardRef)(({ variant, size, weight, color, alig
137
137
  const variantStyles = getVariantStyles();
138
138
  // Calculate inherited values
139
139
  const inheritedContext = inherit && !reset && parentContext ? parentContext : {};
140
+ // Calculate fontSize first for line height calculation
141
+ let calculatedFontSize = inheritedContext.fontSize;
142
+ // Apply variant font size
143
+ if (variant && variantStyles[variant]?.fontSize) {
144
+ calculatedFontSize = variantStyles[variant].fontSize;
145
+ }
146
+ // Apply size-based font size
147
+ if (size) {
148
+ calculatedFontSize = typeof size === "number" ? size : sizeMap[size];
149
+ }
150
+ // Use default if still undefined
151
+ calculatedFontSize = calculatedFontSize || 16;
140
152
  // Calculate final styles
141
153
  const finalStyles = {
142
154
  // Start with inherited values
@@ -194,7 +206,9 @@ exports.TextRoot = (0, react_1.forwardRef)(({ variant, size, weight, color, alig
194
206
  }),
195
207
  // Apply line height
196
208
  ...(leading && {
197
- lineHeight: typeof leading === "number" ? leading : leadingMap[leading],
209
+ lineHeight: typeof leading === "number"
210
+ ? leading
211
+ : leadingMap[leading] * calculatedFontSize,
198
212
  }),
199
213
  // Apply letter spacing
200
214
  ...(tracking && {
@@ -231,7 +245,7 @@ exports.TextRoot = (0, react_1.forwardRef)(({ variant, size, weight, color, alig
231
245
  if (typeof fontSize === "number" && !styleObj.lineHeight && !leading) {
232
246
  return {
233
247
  ...styleObj,
234
- lineHeight: fontSize * 1.2,
248
+ lineHeight: fontSize,
235
249
  };
236
250
  }
237
251
  }
@@ -285,7 +299,7 @@ function createTextStyle(props) {
285
299
  if (props.leading === undefined) {
286
300
  style.lineHeight =
287
301
  typeof props.size === "number"
288
- ? props.size * 1.2 // Auto line height for numeric sizes
302
+ ? props.size
289
303
  : sizeLineHeightMap[props.size];
290
304
  }
291
305
  }
@@ -296,10 +310,11 @@ function createTextStyle(props) {
296
310
  style.textAlign = props.align;
297
311
  }
298
312
  if (props.leading) {
313
+ const fontSize = style.fontSize || 16; // default font size
299
314
  style.lineHeight =
300
315
  typeof props.leading === "number"
301
316
  ? props.leading
302
- : leadingMap[props.leading];
317
+ : leadingMap[props.leading] * fontSize;
303
318
  }
304
319
  if (props.tracking) {
305
320
  style.letterSpacing =