@kjun-ui/native 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -26,9 +26,23 @@ function mergeIcons(parent, own) {
26
26
  function withFallbackIcons(registry, fallbacks) {
27
27
  return mergeIcons(fallbacks, registry);
28
28
  }
29
+ var warnedIcons = /* @__PURE__ */ new Set();
30
+ function developmentMode() {
31
+ try {
32
+ return true;
33
+ } catch (e) {
34
+ return false;
35
+ }
36
+ }
37
+ function warnMissingIcon(name) {
38
+ if (!developmentMode() || warnedIcons.has(name)) return;
39
+ warnedIcons.add(name);
40
+ console.warn(`KJUN Icon: "${name}" \uC544\uC774\uCF58\uC774 \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC544 help-circle\uB85C \uB300\uC2E0\uD569\uB2C8\uB2E4. @kjun-ui/icons/icons/${name}\uB97C import\uD574 KjunProvider\uC758 icons\uC5D0 \uB4F1\uB85D\uD558\uC138\uC694.`);
41
+ }
29
42
  function resolveIcon(registry, name, filled = false) {
30
43
  var _a;
31
44
  const definition = Object.prototype.hasOwnProperty.call(registry, name) ? registry[name] : void 0;
45
+ if (!definition && name) warnMissingIcon(name);
32
46
  const filledNodes = filled && (definition == null ? void 0 : definition.filled);
33
47
  return { nodes: filledNodes || (definition == null ? void 0 : definition.outline) || ((_a = registry["help-circle"]) == null ? void 0 : _a.outline) || defaultIcons["help-circle"].outline, filled: !!filledNodes };
34
48
  }
@@ -693,6 +707,7 @@ function KjunButton(input) {
693
707
  size: declaredSize = "md",
694
708
  loading = false,
695
709
  block = false,
710
+ wrap = false,
696
711
  disabled = false,
697
712
  prefixIcon,
698
713
  suffixIcon,
@@ -713,7 +728,7 @@ function KjunButton(input) {
713
728
  const formActions = useContext5(FormActionsContext);
714
729
  const cardActions = useContext5(CardActionsContext);
715
730
  const stateContent = useContext5(DataStateContentContext);
716
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
731
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
717
732
  const height = Math.max(tokens3.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
718
733
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : tokens3.button.iconSizes[size];
719
734
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -811,7 +826,7 @@ function KjunButton(input) {
811
826
  opacity: overlayLoading ? 0 : 1,
812
827
  ...multiline || navigation ? { flexShrink: 1, minWidth: 0, textAlign: "center" } : {}
813
828
  }, children }),
814
- suffixIcon && /* @__PURE__ */ jsx5(View2, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ jsx5(
829
+ !!suffixIcon && /* @__PURE__ */ jsx5(View2, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ jsx5(
815
830
  KjunIcon,
816
831
  {
817
832
  name: busy && !prefixIcon ? "loader-2" : suffixIcon,
@@ -874,6 +889,16 @@ function FieldClear({ size, disabled, label, onPress }) {
874
889
  );
875
890
  }
876
891
 
892
+ // shared/package-runtime/aria-ids.ts
893
+ function mergeIds(...lists) {
894
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
895
+ return ids.length ? ids.join(" ") : void 0;
896
+ }
897
+ function joinHints(...parts) {
898
+ const text = parts.filter(Boolean);
899
+ return text.length ? text.join(". ") : void 0;
900
+ }
901
+
877
902
  // packages/native/src/field-surface.ts
878
903
  import { useState as useState6 } from "react";
879
904
  import { Animated as Animated3, Platform as Platform5, TextInput } from "react-native";
@@ -1061,6 +1086,7 @@ function KjunFormGroup({
1061
1086
  required = false,
1062
1087
  error = "",
1063
1088
  hint = "",
1089
+ labelHidden = false,
1064
1090
  children,
1065
1091
  style
1066
1092
  }) {
@@ -1070,9 +1096,17 @@ function KjunFormGroup({
1070
1096
  return /* @__PURE__ */ jsx8(
1071
1097
  FieldContext.Provider,
1072
1098
  {
1073
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1099
+ value: {
1100
+ id: fieldId,
1101
+ label,
1102
+ error,
1103
+ hint,
1104
+ required,
1105
+ labelHidden,
1106
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1107
+ },
1074
1108
  children: /* @__PURE__ */ jsxs2(View4, { style: [{ minWidth: 0, marginBottom: tokens7.extensions.form.itemGap }, style], children: [
1075
- label && /* @__PURE__ */ jsxs2(
1109
+ !!label && !labelHidden && /* @__PURE__ */ jsxs2(
1076
1110
  Text3,
1077
1111
  {
1078
1112
  nativeID: fieldId + "-label",
@@ -1089,7 +1123,15 @@ function KjunFormGroup({
1089
1123
  }
1090
1124
  ),
1091
1125
  children,
1092
- (error || hint) && /* @__PURE__ */ jsxs2(
1126
+ !!hint && /* @__PURE__ */ jsx8(
1127
+ Text3,
1128
+ {
1129
+ nativeID: fieldId + "-hint",
1130
+ style: { fontFamily, ...typeStyle("caption"), marginTop: tokens7.extensions.form.messageGap, color: c.textSecondary },
1131
+ children: hint
1132
+ }
1133
+ ),
1134
+ !!error && /* @__PURE__ */ jsxs2(
1093
1135
  View4,
1094
1136
  {
1095
1137
  style: {
@@ -1099,19 +1141,14 @@ function KjunFormGroup({
1099
1141
  marginTop: tokens7.extensions.form.messageGap
1100
1142
  },
1101
1143
  children: [
1102
- !!error && /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1144
+ /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1103
1145
  /* @__PURE__ */ jsx8(
1104
1146
  Text3,
1105
1147
  {
1106
- nativeID: fieldId + (error ? "-error" : "-hint"),
1107
- accessibilityRole: error ? "alert" : void 0,
1108
- style: {
1109
- fontFamily,
1110
- ...typeStyle("caption"),
1111
- flexShrink: 1,
1112
- color: error ? c.danger : c.textSecondary
1113
- },
1114
- children: error || hint
1148
+ nativeID: fieldId + "-error",
1149
+ accessibilityRole: "alert",
1150
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1151
+ children: error
1115
1152
  }
1116
1153
  )
1117
1154
  ]
@@ -1161,6 +1198,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1161
1198
  const invalid = !!(error || errorMessage || (field == null ? void 0 : field.error));
1162
1199
  const canEdit = !disabled && !readOnly && editable;
1163
1200
  const surface = useFieldSurface(size, { disabled, readOnly: !!readOnly || !editable, invalid, open: props["aria-expanded"] === true });
1201
+ const affixText = { ...inputTypeStyle(size), color: surface.icon };
1164
1202
  const showClear = clearable && !!value;
1165
1203
  const [prefixWidth, setPrefixWidth] = useState7(0);
1166
1204
  const [affixWidth, setAffixWidth] = useState7(0);
@@ -1185,8 +1223,8 @@ var KjunInput = forwardRef2(function KjunInput2({
1185
1223
  editable: canEdit,
1186
1224
  readOnly,
1187
1225
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1188
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1189
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1226
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1227
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1190
1228
  accessibilityState: { disabled },
1191
1229
  ...Platform6.OS === "web" ? {
1192
1230
  // Native Web TextInput does not translate accessibilityState.
@@ -1222,7 +1260,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1222
1260
  ]
1223
1261
  }
1224
1262
  ),
1225
- (prefix || prefixIcon) && /* @__PURE__ */ jsx8(
1263
+ !!(prefix || prefixIcon) && /* @__PURE__ */ jsx8(
1226
1264
  View4,
1227
1265
  {
1228
1266
  onLayout: (event) => setPrefixWidth(event.nativeEvent.layout.width),
@@ -1234,7 +1272,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1234
1272
  bottom: 0,
1235
1273
  justifyContent: "center"
1236
1274
  },
1237
- children: prefix || /* @__PURE__ */ jsx8(
1275
+ children: prefix ? content(prefix, affixText) : /* @__PURE__ */ jsx8(
1238
1276
  KjunIcon,
1239
1277
  {
1240
1278
  name: prefixIcon,
@@ -1244,7 +1282,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1244
1282
  )
1245
1283
  }
1246
1284
  ),
1247
- (showClear || suffix || suffixIcon) && /* @__PURE__ */ jsxs2(
1285
+ !!(showClear || suffix || suffixIcon) && /* @__PURE__ */ jsxs2(
1248
1286
  View4,
1249
1287
  {
1250
1288
  onLayout: (event) => setAffixWidth(event.nativeEvent.layout.width),
@@ -1265,7 +1303,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1265
1303
  onClear == null ? void 0 : onClear();
1266
1304
  (_a = input.current) == null ? void 0 : _a.focus();
1267
1305
  } }),
1268
- suffix || suffixIcon && /* @__PURE__ */ jsx8(
1306
+ suffix ? content(suffix, affixText) : !!suffixIcon && /* @__PURE__ */ jsx8(
1269
1307
  KjunIcon,
1270
1308
  {
1271
1309
  name: suffixIcon,
@@ -1305,6 +1343,17 @@ function KjunFormLayout({
1305
1343
  ) });
1306
1344
  }
1307
1345
 
1346
+ // shared/package-runtime/content-presence.ts
1347
+ import { Fragment as Fragment2, isValidElement as isValidElement2 } from "react";
1348
+ function hasContent(node) {
1349
+ if (node == null || typeof node === "boolean") return false;
1350
+ if (typeof node === "string") return node.trim().length > 0;
1351
+ if (Array.isArray(node)) return node.some(hasContent);
1352
+ if (isValidElement2(node) && node.type === Fragment2)
1353
+ return hasContent(node.props.children);
1354
+ return true;
1355
+ }
1356
+
1308
1357
  // packages/native/src/layer-backdrop.tsx
1309
1358
  import { Platform as Platform7, Pressable as Pressable2, View as View5 } from "react-native";
1310
1359
  import { jsx as jsx9 } from "react/jsx-runtime";
@@ -1601,7 +1650,7 @@ function KjunModal({
1601
1650
  borderBottomColor: c.border
1602
1651
  },
1603
1652
  children: [
1604
- /* @__PURE__ */ jsx12(View7, { style: { flex: 1, paddingRight: tokens8.modal.titleActionGap }, children: header || /* @__PURE__ */ jsx12(
1653
+ /* @__PURE__ */ jsx12(View7, { style: { flex: 1, paddingRight: tokens8.modal.titleActionGap }, children: hasContent(header) ? content(header) : /* @__PURE__ */ jsx12(
1605
1654
  Text4,
1606
1655
  {
1607
1656
  accessibilityRole: "header",
@@ -1641,7 +1690,7 @@ function KjunModal({
1641
1690
  children
1642
1691
  }
1643
1692
  ),
1644
- (showFooter || footer) && /* @__PURE__ */ jsx12(
1693
+ (showFooter || hasContent(footer)) && /* @__PURE__ */ jsx12(
1645
1694
  View7,
1646
1695
  {
1647
1696
  style: {
@@ -1654,7 +1703,7 @@ function KjunModal({
1654
1703
  borderTopWidth: tokens8.border.defaultWidth,
1655
1704
  borderTopColor: c.border
1656
1705
  },
1657
- children: footer || /* @__PURE__ */ jsx12(
1706
+ children: hasContent(footer) ? content(footer) : /* @__PURE__ */ jsx12(
1658
1707
  KjunFormActions,
1659
1708
  {
1660
1709
  size: footerSize,
@@ -1690,19 +1739,6 @@ import { tokens as tokens9 } from "@kjun-ui/tokens";
1690
1739
  import { useId as useId4 } from "react";
1691
1740
  import { Platform as Platform10, View as View8 } from "react-native";
1692
1741
  import Svg2, { Defs, LinearGradient, Rect as Rect2, Stop } from "react-native-svg";
1693
-
1694
- // shared/package-runtime/content-presence.ts
1695
- import { Fragment as Fragment2, isValidElement as isValidElement2 } from "react";
1696
- function hasContent(node) {
1697
- if (node == null || typeof node === "boolean") return false;
1698
- if (typeof node === "string") return node.trim().length > 0;
1699
- if (Array.isArray(node)) return node.some(hasContent);
1700
- if (isValidElement2(node) && node.type === Fragment2)
1701
- return hasContent(node.props.children);
1702
- return true;
1703
- }
1704
-
1705
- // packages/native/src/card.tsx
1706
1742
  import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
1707
1743
  function KjunCard({
1708
1744
  title,
@@ -1763,8 +1799,8 @@ function KjunCard({
1763
1799
  borderBottomColor: borderColor
1764
1800
  }, children: [
1765
1801
  hasHeading && /* @__PURE__ */ jsx13(View8, { style: { flexGrow: 1, flexShrink: 1, minWidth: 0, maxWidth: "100%" }, children: hasContent(header) ? content(header, { color: foreground }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
1766
- title && /* @__PURE__ */ jsx13(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1767
- subtitle && /* @__PURE__ */ jsx13(KText, { style: { ...typeStyle("caption"), marginTop: tokens9.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1802
+ !!title && /* @__PURE__ */ jsx13(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1803
+ !!subtitle && /* @__PURE__ */ jsx13(KText, { style: { ...typeStyle("caption"), marginTop: tokens9.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1768
1804
  ] }) }),
1769
1805
  hasContent(headerActions) && /* @__PURE__ */ jsx13(CardActionsContext.Provider, { value: true, children: /* @__PURE__ */ jsx13(View8, { style: { flexDirection: "row", flexWrap: "wrap", minWidth: 0, maxWidth: "100%", marginStart: "auto", alignItems: "center", justifyContent: "flex-end", gap: tokens9.card.titleGap }, children: content(headerActions, { color: foreground }) }) })
1770
1806
  ] }),
@@ -1784,6 +1820,13 @@ function KjunCard({
1784
1820
  ] });
1785
1821
  }
1786
1822
 
1823
+ // shared/package-runtime/alert-live.ts
1824
+ function alertRole(tone, live) {
1825
+ if (live === "off") return void 0;
1826
+ if (live) return live === "assertive" ? "alert" : "status";
1827
+ return tone === "danger" ? "alert" : "status";
1828
+ }
1829
+
1787
1830
  // packages/native/src/display-primitives.tsx
1788
1831
  import { tokens as tokens10 } from "@kjun-ui/tokens";
1789
1832
  import { useContext as useContext8, useEffect as useEffect5, useRef as useRef6, useState as useState9 } from "react";
@@ -1875,9 +1918,9 @@ function KjunEmpty({
1875
1918
  paddingVertical: stateContent === "empty" ? 0 : tokens10.dimension.value32
1876
1919
  },
1877
1920
  children: [
1878
- icon && /* @__PURE__ */ jsx14(View9, { style: { marginBottom: spec.iconGap }, children: /* @__PURE__ */ jsx14(KjunIcon, { name: icon, size: spec.iconSize, color: colors.textSecondary }) }),
1921
+ !!icon && /* @__PURE__ */ jsx14(View9, { style: { marginBottom: spec.iconGap }, children: /* @__PURE__ */ jsx14(KjunIcon, { name: icon, size: spec.iconSize, color: colors.textSecondary }) }),
1879
1922
  /* @__PURE__ */ jsx14(KText, { style: { ...typeStyle("cardTitle"), color: colors.text, fontSize: spec.titleSize, lineHeight: spec.titleLineHeight, textAlign: "center", maxWidth: spec.textMaxWidth }, children: text }),
1880
- description && /* @__PURE__ */ jsx14(
1923
+ !!description && /* @__PURE__ */ jsx14(
1881
1924
  KText,
1882
1925
  {
1883
1926
  style: {
@@ -1892,7 +1935,7 @@ function KjunEmpty({
1892
1935
  children: description
1893
1936
  }
1894
1937
  ),
1895
- children && /* @__PURE__ */ jsx14(View9, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
1938
+ hasContent(children) && /* @__PURE__ */ jsx14(View9, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
1896
1939
  ]
1897
1940
  }
1898
1941
  );
@@ -1970,7 +2013,7 @@ function KjunSpinner({ size = "md", text }) {
1970
2013
  )
1971
2014
  }
1972
2015
  ),
1973
- text && /* @__PURE__ */ jsx14(KText, { style: { marginLeft: tokens10.dimension.value8, color: colors.textSecondary }, children: text })
2016
+ !!text && /* @__PURE__ */ jsx14(KText, { style: { marginLeft: tokens10.dimension.value8, color: colors.textSecondary }, children: text })
1974
2017
  ]
1975
2018
  }
1976
2019
  );
@@ -1981,6 +2024,7 @@ function KjunAlert({
1981
2024
  size = "md",
1982
2025
  title,
1983
2026
  closable = false,
2027
+ live,
1984
2028
  children,
1985
2029
  actions,
1986
2030
  onClose
@@ -1997,7 +2041,7 @@ function KjunAlert({
1997
2041
  return /* @__PURE__ */ jsxs6(
1998
2042
  View9,
1999
2043
  {
2000
- role: tone === "danger" ? "alert" : "status",
2044
+ role: alertRole(tone, live),
2001
2045
  style: {
2002
2046
  flexDirection: "row",
2003
2047
  alignItems: "flex-start",
@@ -2030,7 +2074,7 @@ function KjunAlert({
2030
2074
  ...typeStyle(small ? "caption" : "body"),
2031
2075
  color: colors.textSecondary
2032
2076
  }) }),
2033
- hasContent(actions) && /* @__PURE__ */ jsx14(ActionSizeContext.Provider, { value: alertActionSizes[small ? "sm" : "md"], children: /* @__PURE__ */ jsx14(AlertActionsContext.Provider, { value: true, children: /* @__PURE__ */ jsx14(View9, { style: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: geometry2.actionsGap, marginTop: geometry2[small ? "sm" : "md"].actionGap }, children: actions }) }) })
2077
+ hasContent(actions) && /* @__PURE__ */ jsx14(ActionSizeContext.Provider, { value: alertActionSizes[small ? "sm" : "md"], children: /* @__PURE__ */ jsx14(AlertActionsContext.Provider, { value: true, children: /* @__PURE__ */ jsx14(View9, { style: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: geometry2.actionsGap, marginTop: geometry2[small ? "sm" : "md"].actionGap }, children: content(actions) }) }) })
2034
2078
  ] }),
2035
2079
  closable && /* @__PURE__ */ jsx14(
2036
2080
  AccessiblePressable,
@@ -2080,7 +2124,7 @@ function KjunProgress({
2080
2124
  const { colors } = useKjunStyles();
2081
2125
  const percentage = Number.isFinite(value) && max > 0 ? Math.min(100, Math.max(0, Math.round(value / max * 100))) : 0;
2082
2126
  return /* @__PURE__ */ jsxs6(View9, { children: [
2083
- (label || showLabel) && /* @__PURE__ */ jsxs6(
2127
+ !!(label || showLabel) && /* @__PURE__ */ jsxs6(
2084
2128
  View9,
2085
2129
  {
2086
2130
  style: {
@@ -2498,7 +2542,7 @@ function ToastView({
2498
2542
  children: [
2499
2543
  /* @__PURE__ */ jsx16(KjunIcon, { name: icon, color: colors[`${tone}Accent`] }),
2500
2544
  /* @__PURE__ */ jsxs8(View11, { style: { flex: 1, minWidth: 0 }, children: [
2501
- toast.title && /* @__PURE__ */ jsx16(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: tokens13.extensions.toast.titleGap }, children: toast.title }),
2545
+ !!toast.title && /* @__PURE__ */ jsx16(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: tokens13.extensions.toast.titleGap }, children: toast.title }),
2502
2546
  /* @__PURE__ */ jsx16(KText, { style: { color: colors.textSecondary }, children: toast.message }),
2503
2547
  toast.action && /* @__PURE__ */ jsx16(
2504
2548
  AccessiblePressable,
@@ -2685,7 +2729,7 @@ function FeedbackDialog({
2685
2729
  onConfirm: submit,
2686
2730
  confirmVariant: options.type === "danger" ? "danger" : "primary",
2687
2731
  children: [
2688
- options.message && /* @__PURE__ */ jsx18(KText, { style: { marginBottom: tokens15.dimension.value16 }, children: options.message }),
2732
+ !!options.message && /* @__PURE__ */ jsx18(KText, { style: { marginBottom: tokens15.dimension.value16 }, children: options.message }),
2689
2733
  request.kind === "prompt" && /* @__PURE__ */ jsx18(
2690
2734
  KjunInput,
2691
2735
  {
@@ -2702,7 +2746,7 @@ function FeedbackDialog({
2702
2746
  }
2703
2747
  );
2704
2748
  }
2705
- function KjunFeedbackProvider({ children }) {
2749
+ function KjunFeedbackProvider({ children, style }) {
2706
2750
  const [controller] = useState12(createFeedbackController);
2707
2751
  const life = useRef12(0);
2708
2752
  const snapshot = useSyncExternalStore2(
@@ -2732,7 +2776,7 @@ function KjunFeedbackProvider({ children }) {
2732
2776
  {
2733
2777
  value: { top: layers[layers.length - 1], register, render: renderToasts },
2734
2778
  children: /* @__PURE__ */ jsxs9(KjunFeedbackContext.Provider, { value: controller.api, children: [
2735
- /* @__PURE__ */ jsxs9(View13, { style: { position: "relative" }, children: [
2779
+ /* @__PURE__ */ jsxs9(View13, { style: [{ position: "relative", flexGrow: 1, flexShrink: 1, alignSelf: "stretch" }, style], children: [
2736
2780
  children,
2737
2781
  layers.length === 0 && renderToasts()
2738
2782
  ] }),
@@ -2822,7 +2866,7 @@ function KjunCheckbox({
2822
2866
  )
2823
2867
  }
2824
2868
  ),
2825
- (children || label) && content(children || label, {
2869
+ !!(children || label) && content(children || label, {
2826
2870
  marginLeft: tokens16.extensions.checkbox.labelGap,
2827
2871
  ...typeStyle("label")
2828
2872
  })
@@ -2933,7 +2977,7 @@ function KjunSwitch({
2933
2977
  ]
2934
2978
  }
2935
2979
  ),
2936
- (children || label) && content(children || label, {
2980
+ !!(children || label) && content(children || label, {
2937
2981
  marginLeft: tokens16.extensions.switch.labelGap,
2938
2982
  ...typeStyle("label")
2939
2983
  })
@@ -3010,7 +3054,7 @@ function KjunRadio({
3010
3054
  )
3011
3055
  }
3012
3056
  ),
3013
- (children || label) && content(children || label, {
3057
+ !!(children || label) && content(children || label, {
3014
3058
  marginLeft: geometry2.labelGap,
3015
3059
  ...typeStyle("label")
3016
3060
  })
@@ -3258,7 +3302,7 @@ function KjunButtonGroup({
3258
3302
  opacity: blocked ? tokens18.states.opacity.disabled : 1
3259
3303
  }),
3260
3304
  children: [
3261
- option.icon && /* @__PURE__ */ jsx21(
3305
+ !!option.icon && /* @__PURE__ */ jsx21(
3262
3306
  KjunIcon,
3263
3307
  {
3264
3308
  name: option.icon,
@@ -3355,7 +3399,7 @@ function KjunFilterGroup({
3355
3399
  }
3356
3400
  }
3357
3401
  ),
3358
- option.icon && /* @__PURE__ */ jsx21(
3402
+ !!option.icon && /* @__PURE__ */ jsx21(
3359
3403
  KjunIcon,
3360
3404
  {
3361
3405
  name: option.icon,
@@ -3447,13 +3491,13 @@ var KjunTextarea = forwardRef3(
3447
3491
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3448
3492
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3449
3493
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3450
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3494
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3451
3495
  accessibilityState: { disabled },
3452
3496
  ...Platform13.OS === "web" ? {
3453
3497
  "aria-disabled": disabled,
3454
3498
  "aria-invalid": invalid || void 0,
3455
3499
  "aria-describedby": field == null ? void 0 : field.describedBy,
3456
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3500
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3457
3501
  "aria-required": (field == null ? void 0 : field.required) || void 0
3458
3502
  } : {},
3459
3503
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3488,8 +3532,32 @@ var KjunTextarea = forwardRef3(
3488
3532
 
3489
3533
  // packages/native/src/drawer.tsx
3490
3534
  import { tokens as tokens20 } from "@kjun-ui/tokens";
3491
- import { useState as useState15 } from "react";
3492
- import { Animated as Animated12 } from "react-native";
3535
+ import { useEffect as useEffect11, useState as useState15 } from "react";
3536
+ import { Animated as Animated12, Keyboard } from "react-native";
3537
+
3538
+ // shared/package-runtime/drawer-geometry.ts
3539
+ function keyboardEvents(os) {
3540
+ return os === "ios" ? { show: "keyboardWillShow", hide: "keyboardWillHide" } : null;
3541
+ }
3542
+ function keyboardOverlap(windowHeight, keyboardScreenY) {
3543
+ return Math.max(0, windowHeight - keyboardScreenY);
3544
+ }
3545
+ function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap, keyboard, maxHeight }) {
3546
+ const visible = windowHeight - keyboard;
3547
+ const bottomInset = keyboard > 0 ? 0 : safeAreaBottom;
3548
+ if (position === "left" || position === "right")
3549
+ return { maxHeight: visible, height: visible, paddingTop: safeAreaTop, paddingBottom: bottomInset, lift: 0 };
3550
+ const sheet = maxHeight != null ? maxHeight : visible - (position === "bottom" ? safeAreaTop : bottomInset) - gap;
3551
+ return {
3552
+ maxHeight: Math.max(0, Math.min(sheet, visible)),
3553
+ height: void 0,
3554
+ paddingTop: position === "top" ? safeAreaTop : 0,
3555
+ paddingBottom: position === "bottom" ? bottomInset : 0,
3556
+ lift: position === "bottom" ? keyboard : 0
3557
+ };
3558
+ }
3559
+
3560
+ // packages/native/src/drawer.tsx
3493
3561
  import {
3494
3562
  Platform as Platform14,
3495
3563
  ScrollView as ScrollView3,
@@ -3497,6 +3565,24 @@ import {
3497
3565
  useWindowDimensions as useWindowDimensions3
3498
3566
  } from "react-native";
3499
3567
  import { jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
3568
+ function useKeyboardOverlap(enabled, windowHeight) {
3569
+ const [overlap, setOverlap] = useState15(0);
3570
+ useEffect11(() => {
3571
+ var _a, _b;
3572
+ setOverlap(0);
3573
+ const events = keyboardEvents(Platform14.OS);
3574
+ if (!enabled || !events) return;
3575
+ const metrics = (_b = (_a = Keyboard).metrics) == null ? void 0 : _b.call(_a);
3576
+ if (metrics) setOverlap(keyboardOverlap(windowHeight, metrics.screenY));
3577
+ const show = Keyboard.addListener(events.show, (event) => setOverlap(keyboardOverlap(windowHeight, event.endCoordinates.screenY)));
3578
+ const hide = Keyboard.addListener(events.hide, () => setOverlap(0));
3579
+ return () => {
3580
+ show.remove();
3581
+ hide.remove();
3582
+ };
3583
+ }, [enabled, windowHeight]);
3584
+ return overlap;
3585
+ }
3500
3586
  function KjunDrawer({
3501
3587
  open,
3502
3588
  onOpenChange,
@@ -3511,9 +3597,24 @@ function KjunDrawer({
3511
3597
  footer,
3512
3598
  children,
3513
3599
  onClose,
3514
- ariaLabel
3600
+ ariaLabel,
3601
+ safeAreaTop = 0,
3602
+ safeAreaBottom = 0,
3603
+ maxHeight,
3604
+ avoidKeyboard = true
3515
3605
  }) {
3516
3606
  const { colors } = useKjunStyles(), window2 = useWindowDimensions3(), vertical = position === "left" || position === "right";
3607
+ const [rootHeight, setRootHeight] = useState15(0);
3608
+ const keyboard = useKeyboardOverlap(open && avoidKeyboard, window2.height);
3609
+ const frame = drawerFrame({
3610
+ position,
3611
+ windowHeight: rootHeight || window2.height,
3612
+ safeAreaTop: Math.max(0, safeAreaTop),
3613
+ safeAreaBottom: Math.max(0, safeAreaBottom),
3614
+ gap: tokens20.extensions.drawer.sheetGap,
3615
+ keyboard,
3616
+ maxHeight
3617
+ });
3517
3618
  const [panelHeight, setPanelHeight] = useState15(0);
3518
3619
  const motion = useNativePresence(open, tokens20.motion.layerEnter, tokens20.motion.layerExit, vertical || panelHeight > 0, 0, { enterEasing: motionEmphasized });
3519
3620
  const backdrop = useNativePresence(open, tokens20.motion.backdrop, tokens20.motion.backdropExit, true, 0, { fade: tokens20.motion.backdropExit });
@@ -3537,6 +3638,7 @@ function KjunDrawer({
3537
3638
  children: /* @__PURE__ */ jsxs13(
3538
3639
  View17,
3539
3640
  {
3641
+ onLayout: (event) => setRootHeight(event.nativeEvent.layout.height),
3540
3642
  style: {
3541
3643
  flex: 1,
3542
3644
  justifyContent: position === "bottom" ? "flex-end" : "flex-start",
@@ -3575,8 +3677,11 @@ function KjunDrawer({
3575
3677
  opacity: vertical || panelHeight > 0 ? 1 : 0,
3576
3678
  transform: vertical ? [{ translateX: motion.progress.interpolate({ inputRange: [0, 1], outputRange: [(position === "left" ? -1 : 1) * Math.min(width, window2.width), 0] }) }] : [{ translateY: motion.progress.interpolate({ inputRange: [0, 1], outputRange: [(position === "top" ? -1 : 1) * panelHeight, 0] }) }],
3577
3679
  width: vertical ? Math.min(width, window2.width) : window2.width,
3578
- height: vertical ? window2.height : void 0,
3579
- maxHeight: window2.height,
3680
+ height: frame.height,
3681
+ maxHeight: frame.maxHeight,
3682
+ paddingTop: frame.paddingTop,
3683
+ paddingBottom: frame.paddingBottom,
3684
+ marginBottom: frame.lift,
3580
3685
  borderRadius: tokens20.extensions.drawer.radius,
3581
3686
  borderTopLeftRadius: position === "bottom" ? tokens20.extensions.drawer.bottomRadius : tokens20.extensions.drawer.radius,
3582
3687
  borderTopRightRadius: position === "bottom" ? tokens20.extensions.drawer.bottomRadius : tokens20.extensions.drawer.radius,
@@ -3584,7 +3689,7 @@ function KjunDrawer({
3584
3689
  backgroundColor: colors.surface
3585
3690
  },
3586
3691
  children: [
3587
- (title || header || closable) && /* @__PURE__ */ jsxs13(
3692
+ !!(title || header || closable) && /* @__PURE__ */ jsxs13(
3588
3693
  View17,
3589
3694
  {
3590
3695
  style: {
@@ -3597,7 +3702,7 @@ function KjunDrawer({
3597
3702
  borderBottomColor: colors.border
3598
3703
  },
3599
3704
  children: [
3600
- /* @__PURE__ */ jsx23(View17, { style: { flex: 1 }, children: header || /* @__PURE__ */ jsx23(
3705
+ /* @__PURE__ */ jsx23(View17, { style: { flex: 1 }, children: hasContent(header) ? content(header) : /* @__PURE__ */ jsx23(
3601
3706
  KText,
3602
3707
  {
3603
3708
  style: { ...typeStyle("sectionTitle") },
@@ -3622,12 +3727,12 @@ function KjunDrawer({
3622
3727
  ScrollView3,
3623
3728
  {
3624
3729
  keyboardShouldPersistTaps: "handled",
3625
- style: vertical ? { flex: 1 } : { flexGrow: 0 },
3730
+ style: vertical ? { flex: 1 } : { flexGrow: 0, flexShrink: 1 },
3626
3731
  contentContainerStyle: { padding: noPadding ? 0 : tokens20.extensions.drawer.bodyPadding },
3627
3732
  children: content(children)
3628
3733
  }
3629
3734
  ),
3630
- footer && /* @__PURE__ */ jsx23(
3735
+ hasContent(footer) && /* @__PURE__ */ jsx23(
3631
3736
  View17,
3632
3737
  {
3633
3738
  style: {
@@ -3657,7 +3762,7 @@ import {
3657
3762
  createContext as createContext15,
3658
3763
  isValidElement as isValidElement5,
3659
3764
  useContext as useContext11,
3660
- useEffect as useEffect13,
3765
+ useEffect as useEffect14,
3661
3766
  useRef as useRef18,
3662
3767
  useState as useState18
3663
3768
  } from "react";
@@ -3671,7 +3776,7 @@ import { tokens as tokens21 } from "@kjun-ui/tokens";
3671
3776
  import {
3672
3777
  cloneElement as cloneElement2,
3673
3778
  isValidElement as isValidElement3,
3674
- useEffect as useEffect11,
3779
+ useEffect as useEffect12,
3675
3780
  useLayoutEffect as useLayoutEffect8,
3676
3781
  useRef as useRef16,
3677
3782
  useState as useState16
@@ -3723,8 +3828,11 @@ function FloatingPanel({
3723
3828
  flip = true,
3724
3829
  minimumWidth = 0,
3725
3830
  focusOnOpen = false,
3726
- reveal
3831
+ reveal,
3832
+ header,
3833
+ nonModal = false
3727
3834
  }) {
3835
+ const [headerHeight, setHeaderHeight] = useState16(0);
3728
3836
  const panelRef = useRef16(null);
3729
3837
  const scroll = useRef16(null), viewport = useRef16(0);
3730
3838
  const { colors } = useKjunStyles(), window2 = useWindowDimensions4(), [anchored, setAnchored] = useState16(false), [sized, setSized] = useState16(false), [anchor, setAnchor] = useState16({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = useState16({ width: 0, height: 0 });
@@ -3750,14 +3858,14 @@ function FloatingPanel({
3750
3858
  setAnchored(false);
3751
3859
  }
3752
3860
  }, [motion.present]);
3753
- useEffect11(() => {
3861
+ useEffect12(() => {
3754
3862
  var _a;
3755
3863
  const row = reveal == null ? void 0 : reveal.current;
3756
3864
  if (!open || !anchored || !sized || !row || !viewport.current) return;
3757
3865
  const offset = row.y + row.height + (menu || field ? tokens21.extensions.menu.padding : 0) - viewport.current;
3758
3866
  if (offset > 0) (_a = scroll.current) == null ? void 0 : _a.scrollTo({ y: offset, animated: false });
3759
3867
  }, [open, anchored, sized, reveal, menu, field]);
3760
- useEffect11(() => {
3868
+ useEffect12(() => {
3761
3869
  if (!open || !anchored || !sized || !focusOnOpen || !panelRef.current) return;
3762
3870
  if (Platform15.OS === "web") {
3763
3871
  const node = panelRef.current;
@@ -3779,7 +3887,7 @@ function FloatingPanel({
3779
3887
  maximumWidth,
3780
3888
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : tokens21.extensions.popover.width)
3781
3889
  );
3782
- const available = Math.min(maxHeight != null ? maxHeight : field ? tokens21.extensions.menu.listMaxHeight : tokens21.extensions.floating.maxHeight, window2.height - 2 * tokens21.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - tokens21.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens21.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3890
+ const available = Math.min(maxHeight != null ? maxHeight : field ? tokens21.extensions.menu.listMaxHeight : tokens21.extensions.floating.maxHeight, window2.height - 2 * tokens21.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - tokens21.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens21.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3783
3891
  let left = placement.endsWith("end") ? anchor.x + anchor.width - width : placement === "top" || placement === "bottom" ? anchor.x + (anchor.width - width) / 2 : anchor.x, top = above ? anchor.y - height - gap : anchor.y + anchor.height + gap;
3784
3892
  if (placement === "left" || placement === "right") {
3785
3893
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -3863,7 +3971,7 @@ function FloatingPanel({
3863
3971
  }
3864
3972
  }
3865
3973
  ),
3866
- /* @__PURE__ */ jsx24(
3974
+ /* @__PURE__ */ jsxs14(
3867
3975
  Animated13.View,
3868
3976
  {
3869
3977
  ref: panelRef,
@@ -3874,7 +3982,7 @@ function FloatingPanel({
3874
3982
  pointerEvents: open && anchored && sized ? "auto" : "none",
3875
3983
  accessibilityElementsHidden: !open || !anchored || !sized,
3876
3984
  ...!open || !anchored || !sized ? { inert: true } : {},
3877
- accessibilityViewIsModal: true,
3985
+ accessibilityViewIsModal: !nonModal,
3878
3986
  accessibilityLabel: ariaLabel,
3879
3987
  style: {
3880
3988
  opacity: anchored && sized ? motion.opacity : 0,
@@ -3896,26 +4004,42 @@ function FloatingPanel({
3896
4004
  boxShadow: (tooltip ? tokens21.extensions.tooltip.elevation : popover ? tokens21.extensions.popover.elevation : tokens21.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
3897
4005
  overflow: "hidden"
3898
4006
  },
3899
- children: /* @__PURE__ */ jsx24(
3900
- ScrollView4,
3901
- {
3902
- ref: scroll,
3903
- onLayout: (event) => {
3904
- viewport.current = event.nativeEvent.layout.height;
3905
- },
3906
- keyboardShouldPersistTaps: "handled",
3907
- style: { maxHeight: available },
3908
- contentContainerStyle: { padding: menu || field ? tokens21.extensions.menu.padding : 0 },
3909
- onContentSizeChange: (width2, height2) => {
3910
- if (contentSizedMenu) return;
3911
- setSized(true);
3912
- setPanel(
3913
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
3914
- );
3915
- },
3916
- children
3917
- }
3918
- )
4007
+ children: [
4008
+ header != null && /* @__PURE__ */ jsx24(
4009
+ View18,
4010
+ {
4011
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
4012
+ style: {
4013
+ paddingTop: tokens21.extensions.popover.padding,
4014
+ paddingHorizontal: tokens21.extensions.popover.padding,
4015
+ paddingBottom: tokens21.extensions.popover.headerPaddingBottom,
4016
+ borderBottomWidth: tokens21.border.defaultWidth,
4017
+ borderBottomColor: colors.divider
4018
+ },
4019
+ children: content(header, typeStyle("cardTitle"))
4020
+ }
4021
+ ),
4022
+ /* @__PURE__ */ jsx24(
4023
+ ScrollView4,
4024
+ {
4025
+ ref: scroll,
4026
+ onLayout: (event) => {
4027
+ viewport.current = event.nativeEvent.layout.height;
4028
+ },
4029
+ keyboardShouldPersistTaps: "handled",
4030
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
4031
+ contentContainerStyle: { padding: menu || field ? tokens21.extensions.menu.padding : 0 },
4032
+ onContentSizeChange: (width2, height2) => {
4033
+ if (contentSizedMenu) return;
4034
+ setSized(true);
4035
+ setPanel(
4036
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4037
+ );
4038
+ },
4039
+ children
4040
+ }
4041
+ )
4042
+ ]
3919
4043
  }
3920
4044
  )
3921
4045
  ]
@@ -3929,7 +4053,7 @@ function FloatingPanel({
3929
4053
  import {
3930
4054
  cloneElement as cloneElement3,
3931
4055
  isValidElement as isValidElement4,
3932
- useEffect as useEffect12,
4056
+ useEffect as useEffect13,
3933
4057
  useRef as useRef17,
3934
4058
  useState as useState17
3935
4059
  } from "react";
@@ -3950,9 +4074,12 @@ function KjunPopover({
3950
4074
  focusOnOpen = false,
3951
4075
  ariaLabel,
3952
4076
  placement = "bottom",
4077
+ header,
4078
+ modal = true,
3953
4079
  trigger,
3954
4080
  children
3955
4081
  }) {
4082
+ const { colors } = useKjunStyles();
3956
4083
  const [internal, setInternal] = useState17(false), open = controlled != null ? controlled : internal, ref = useRef17(null);
3957
4084
  const change = (v) => {
3958
4085
  setInternal(v);
@@ -3981,6 +4108,8 @@ function KjunPopover({
3981
4108
  popover: true,
3982
4109
  focusOnOpen,
3983
4110
  ariaLabel,
4111
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4112
+ nonModal: !modal,
3984
4113
  children: /* @__PURE__ */ jsx25(View19, { style: { padding: noPadding ? 0 : tokens22.extensions.popover.padding, gap: noPadding ? 0 : tokens22.extensions.popover.gap }, children: content(
3985
4114
  typeof children === "function" ? children(() => change(false)) : children,
3986
4115
  Platform16.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -3995,9 +4124,18 @@ function KjunTooltip({
3995
4124
  delay = 0,
3996
4125
  children,
3997
4126
  onPress,
3998
- disabled
4127
+ disabled,
4128
+ open: controlled,
4129
+ onOpenChange
3999
4130
  }) {
4000
- const { colors } = useKjunStyles(), [open, setOpen] = useState17(false), ref = useRef17(null), timer = useRef17(void 0);
4131
+ const { colors } = useKjunStyles(), [internal, setInternal] = useState17(false), open = controlled != null ? controlled : internal, latest = useRef17({ open, onOpenChange }), setOpen = (value) => {
4132
+ var _a, _b;
4133
+ if (latest.current.open === value) return;
4134
+ latest.current.open = value;
4135
+ setInternal(value);
4136
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4137
+ }, ref = useRef17(null), timer = useRef17(void 0);
4138
+ latest.current = { open, onOpenChange };
4001
4139
  const scopeActive = useActiveLayerScope();
4002
4140
  const hide = () => {
4003
4141
  clearTimeout(timer.current);
@@ -4008,11 +4146,11 @@ function KjunTooltip({
4008
4146
  clearTimeout(timer.current);
4009
4147
  timer.current = setTimeout(() => setOpen(true), delay);
4010
4148
  };
4011
- useEffect12(() => {
4149
+ useEffect13(() => {
4012
4150
  if (!scopeActive) hide();
4013
4151
  }, [scopeActive]);
4014
- useEffect12(() => () => clearTimeout(timer.current), []);
4015
- useEffect12(() => {
4152
+ useEffect13(() => () => clearTimeout(timer.current), []);
4153
+ useEffect13(() => {
4016
4154
  if (Platform16.OS !== "web" || !open) return;
4017
4155
  const escape = (event) => {
4018
4156
  if (event.key === "Escape") hide();
@@ -4128,7 +4266,7 @@ function KjunDropdown({
4128
4266
  if (v) onOpen == null ? void 0 : onOpen();
4129
4267
  else onClose == null ? void 0 : onClose();
4130
4268
  };
4131
- useEffect13(() => {
4269
+ useEffect14(() => {
4132
4270
  if (unavailable) change(false);
4133
4271
  }, [unavailable]);
4134
4272
  return /* @__PURE__ */ jsxs16(DropdownContext.Provider, { value: () => change(false), children: [
@@ -4263,7 +4401,7 @@ function KjunMenuButton({
4263
4401
  }
4264
4402
 
4265
4403
  // shared/package-runtime/combobox.ts
4266
- import { useEffect as useEffect14, useState as useState19 } from "react";
4404
+ import { useEffect as useEffect15, useState as useState19 } from "react";
4267
4405
 
4268
4406
  // shared/package-runtime/options.ts
4269
4407
  var optionValue = (option, key = "value") => typeof option === "object" ? option[key] : option;
@@ -4342,8 +4480,8 @@ function useComboboxModel({
4342
4480
  const selectedLabel = selected !== void 0 ? optionLabel(selected, labelKey) : "";
4343
4481
  const [query, setQuery] = useState19(selectedLabel);
4344
4482
  const [open, setOpen] = useState19(false);
4345
- useEffect14(() => setQuery(selectedLabel), [value, selectedLabel]);
4346
- useEffect14(() => {
4483
+ useEffect15(() => setQuery(selectedLabel), [value, selectedLabel]);
4484
+ useEffect15(() => {
4347
4485
  if (disabled) setOpen(false);
4348
4486
  }, [disabled]);
4349
4487
  const change = (next) => {
@@ -4800,7 +4938,7 @@ function KjunDatePicker({
4800
4938
  import { useState as useState22 } from "react";
4801
4939
 
4802
4940
  // shared/package-runtime/search-input.ts
4803
- import { useEffect as useEffect15, useLayoutEffect as useLayoutEffect9, useRef as useRef21, useState as useState21 } from "react";
4941
+ import { useEffect as useEffect16, useLayoutEffect as useLayoutEffect9, useRef as useRef21, useState as useState21 } from "react";
4804
4942
  function useSearchInputModel({
4805
4943
  value,
4806
4944
  disabled = false,
@@ -4823,7 +4961,7 @@ function useSearchInputModel({
4823
4961
  clearTimeout(timer.current);
4824
4962
  return () => clearTimeout(timer.current);
4825
4963
  }, [disabled, debounce, !!loadOptions]);
4826
- useEffect15(() => {
4964
+ useEffect16(() => {
4827
4965
  const current = ++version.current, abort = new AbortController();
4828
4966
  setFailed(false);
4829
4967
  if (!loadOptions || !open || disabled || query.length < minChars) {
@@ -4972,7 +5110,7 @@ function KjunSearchInput({
4972
5110
 
4973
5111
  // packages/native/src/select-control.tsx
4974
5112
  import { tokens as tokens28 } from "@kjun-ui/tokens";
4975
- import { useContext as useContext12, useEffect as useEffect17, useRef as useRef23 } from "react";
5113
+ import { useContext as useContext12, useEffect as useEffect18, useRef as useRef23 } from "react";
4976
5114
 
4977
5115
  // packages/native/src/compound-control.tsx
4978
5116
  import { createContext as createContext16 } from "react";
@@ -4986,7 +5124,7 @@ function CompoundTarget({ children, square = false }) {
4986
5124
  }
4987
5125
 
4988
5126
  // shared/package-runtime/select.ts
4989
- import { useEffect as useEffect16, useRef as useRef22, useState as useState23 } from "react";
5127
+ import { useEffect as useEffect17, useRef as useRef22, useState as useState23 } from "react";
4990
5128
  function useSelectState(controlled, disabled, pageSize, onOpenChange) {
4991
5129
  const [internal, setInternal] = useState23(false);
4992
5130
  const [query, setQuery] = useState23("");
@@ -4995,7 +5133,7 @@ function useSelectState(controlled, disabled, pageSize, onOpenChange) {
4995
5133
  const isControlled = typeof controlled === "boolean";
4996
5134
  const open = !disabled && (isControlled ? controlled : internal);
4997
5135
  const previousOpen = useRef22(open);
4998
- useEffect16(() => {
5136
+ useEffect17(() => {
4999
5137
  if (disabled && !isControlled) setInternal(false);
5000
5138
  if (previousOpen.current && !open) {
5001
5139
  setQuery("");
@@ -5004,7 +5142,7 @@ function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5004
5142
  }
5005
5143
  previousOpen.current = open;
5006
5144
  }, [open, disabled, isControlled, pageSize]);
5007
- useEffect16(() => {
5145
+ useEffect17(() => {
5008
5146
  setLimit(pageSize);
5009
5147
  }, [pageSize]);
5010
5148
  const changeOpen = (next) => {
@@ -5054,7 +5192,7 @@ function KjunSelect({
5054
5192
  const { colors } = useKjunStyles(), field = useKjunField(), ref = useRef23(null), spec = tokens28.input[size];
5055
5193
  const trigger = useRef23(null);
5056
5194
  const compound = useContext12(CompoundControlContext);
5057
- useEffect17(() => {
5195
+ useEffect18(() => {
5058
5196
  if (compound == null ? void 0 : compound.focusRef) compound.focusRef.current = trigger.current;
5059
5197
  }, [compound == null ? void 0 : compound.focusRef]);
5060
5198
  const expanded = usePopupExpanded(open, ref);
@@ -5305,10 +5443,10 @@ function Collapse({ open, children }) {
5305
5443
  }
5306
5444
 
5307
5445
  // shared/package-runtime/navigation.ts
5308
- import { useEffect as useEffect18, useRef as useRef25, useState as useState25 } from "react";
5446
+ import { useEffect as useEffect19, useRef as useRef25, useState as useState25 } from "react";
5309
5447
  function useInitialTab(value, candidate, change) {
5310
5448
  const requested = useRef25(/* @__PURE__ */ new Set());
5311
- useEffect18(() => {
5449
+ useEffect19(() => {
5312
5450
  if (value !== "") {
5313
5451
  requested.current.clear();
5314
5452
  return;
@@ -5320,7 +5458,7 @@ function useInitialTab(value, candidate, change) {
5320
5458
  }
5321
5459
  function useAccordionState(multiple) {
5322
5460
  const [opened, setOpened] = useState25(/* @__PURE__ */ new Set());
5323
- useEffect18(() => {
5461
+ useEffect19(() => {
5324
5462
  if (!multiple) setOpened((previous) => previous.size > 1 ? new Set([...previous].slice(-1)) : previous);
5325
5463
  }, [multiple]);
5326
5464
  return {
@@ -5348,7 +5486,7 @@ import {
5348
5486
  Fragment as Fragment10,
5349
5487
  createContext as createContext18,
5350
5488
  useContext as useContext14,
5351
- useEffect as useEffect19,
5489
+ useEffect as useEffect20,
5352
5490
  useId as useId7,
5353
5491
  useState as useState27
5354
5492
  } from "react";
@@ -5522,7 +5660,7 @@ function KjunTabs({
5522
5660
  opacity: tab.disabled ? tokens30.states.opacity.disabled : 1
5523
5661
  },
5524
5662
  children: [
5525
- tab.icon && /* @__PURE__ */ jsx34(
5663
+ !!tab.icon && /* @__PURE__ */ jsx34(
5526
5664
  KjunIcon,
5527
5665
  {
5528
5666
  name: tab.icon,
@@ -5567,11 +5705,11 @@ function KjunTabs({
5567
5705
  tab.name
5568
5706
  );
5569
5707
  }),
5570
- actions
5708
+ content(actions)
5571
5709
  ]
5572
5710
  }
5573
5711
  ),
5574
- children && /* @__PURE__ */ jsx34(View27, { style: { marginTop: geometry2.contentGap }, children })
5712
+ hasContent(children) && /* @__PURE__ */ jsx34(View27, { style: { marginTop: geometry2.contentGap }, children: content(children) })
5575
5713
  ] }) });
5576
5714
  }
5577
5715
  function KjunTabPane({ name, children }) {
@@ -5636,7 +5774,7 @@ function KjunAccordionItem({
5636
5774
  const geometry2 = tokens31.extensions.accordion;
5637
5775
  const [hovered, setHovered] = useState27(false);
5638
5776
  const wrapping = Platform21.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
5639
- useEffect19(() => {
5777
+ useEffect20(() => {
5640
5778
  return group == null ? void 0 : group.register(id, defaultOpen);
5641
5779
  }, []);
5642
5780
  return /* @__PURE__ */ jsxs22(View28, { children: [
@@ -5699,7 +5837,7 @@ function KjunBreadcrumb({
5699
5837
  },
5700
5838
  children: items.map((item, i) => {
5701
5839
  const label = /* @__PURE__ */ jsxs22(View28, { style: { flexDirection: "row", alignItems: "center", gap: tokens31.dimension.value4 }, children: [
5702
- item.icon && /* @__PURE__ */ jsx35(KjunIcon, { name: item.icon, size: tokens31.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
5840
+ !!item.icon && /* @__PURE__ */ jsx35(KjunIcon, { name: item.icon, size: tokens31.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
5703
5841
  /* @__PURE__ */ jsx35(
5704
5842
  KText,
5705
5843
  {
@@ -5872,7 +6010,7 @@ function KjunPagination({
5872
6010
 
5873
6011
  // packages/native/src/actions.tsx
5874
6012
  import { tokens as tokens32 } from "@kjun-ui/tokens";
5875
- import { useContext as useContext15, useEffect as useEffect20, useRef as useRef27, useState as useState28 } from "react";
6013
+ import { useContext as useContext15, useEffect as useEffect21, useRef as useRef27, useState as useState28 } from "react";
5876
6014
  import { Platform as Platform22, ScrollView as ScrollView7, View as View29 } from "react-native";
5877
6015
  import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
5878
6016
  function KjunIconToggle({
@@ -5956,7 +6094,7 @@ function KjunCopyButton({
5956
6094
  onCopyError
5957
6095
  }) {
5958
6096
  const feedback = useOptionalKjunFeedback(), { colors } = useKjunStyles(), [copied, setCopied] = useState28(false), [failed, setFailed] = useState28(false), busy = useRef27(false), timer = useRef27(void 0), mounted = useRef27(true);
5959
- useEffect20(() => {
6097
+ useEffect21(() => {
5960
6098
  mounted.current = true;
5961
6099
  return () => {
5962
6100
  mounted.current = false;
@@ -6015,7 +6153,7 @@ function KjunCopyButton({
6015
6153
  color: copied ? colors.success : colors.textSecondary
6016
6154
  }
6017
6155
  ),
6018
- text && !inline && /* @__PURE__ */ jsx36(
6156
+ !!text && !inline && /* @__PURE__ */ jsx36(
6019
6157
  KText,
6020
6158
  {
6021
6159
  style: {
@@ -6117,10 +6255,10 @@ import { Component, useState as useState30 } from "react";
6117
6255
  import { Platform as Platform23, View as View30 } from "react-native";
6118
6256
 
6119
6257
  // shared/package-runtime/use-loading-reveal.ts
6120
- import { useEffect as useEffect21, useState as useState29 } from "react";
6258
+ import { useEffect as useEffect22, useState as useState29 } from "react";
6121
6259
  function useLoadingReveal(active, delay = 0) {
6122
6260
  const [revealed, setRevealed] = useState29(false);
6123
- useEffect21(() => {
6261
+ useEffect22(() => {
6124
6262
  setRevealed(false);
6125
6263
  if (!active || delay <= 0) return;
6126
6264
  const timer = setTimeout(() => setRevealed(true), delay);
@@ -6208,7 +6346,7 @@ function KjunDataState({
6208
6346
  /* @__PURE__ */ jsx37(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6209
6347
  ] }))
6210
6348
  }
6211
- ) : blockingError ? /* @__PURE__ */ jsx37(DataStateContentContext.Provider, { value: "message", children: /* @__PURE__ */ jsx37(View30, { style: { paddingVertical: padding }, children: errorContent || /* @__PURE__ */ jsx37(KjunAlert, { type: "danger", title: "\uC624\uB958 \uBC1C\uC0DD", actions: onRetry && /* @__PURE__ */ jsx37(KjunButton, { variant: "secondary", onPress: onRetry, children: retryText }), children: query.error }) }) }) : empty ? /* @__PURE__ */ jsx37(DataStateContentContext.Provider, { value: "empty", children: /* @__PURE__ */ jsx37(View30, { style: { paddingVertical: padding }, children: emptyContent || /* @__PURE__ */ jsx37(KjunEmpty, { text: emptyText, icon: emptyIcon, children: emptyActionText && /* @__PURE__ */ jsx37(KjunButton, { onPress: onEmptyAction, children: emptyActionText }) }) }) }) : null,
6349
+ ) : blockingError ? /* @__PURE__ */ jsx37(DataStateContentContext.Provider, { value: "message", children: /* @__PURE__ */ jsx37(View30, { style: { paddingVertical: padding }, children: errorContent || /* @__PURE__ */ jsx37(KjunAlert, { type: "danger", title: "\uC624\uB958 \uBC1C\uC0DD", actions: onRetry && /* @__PURE__ */ jsx37(KjunButton, { variant: "secondary", onPress: onRetry, children: retryText }), children: query.error }) }) }) : empty ? /* @__PURE__ */ jsx37(DataStateContentContext.Provider, { value: "empty", children: /* @__PURE__ */ jsx37(View30, { style: { paddingVertical: padding }, children: emptyContent || /* @__PURE__ */ jsx37(KjunEmpty, { text: emptyText, icon: emptyIcon, children: !!emptyActionText && /* @__PURE__ */ jsx37(KjunButton, { onPress: onEmptyAction, children: emptyActionText }) }) }) }) : null,
6212
6350
  (!concealed || preserveContent && state.hasQueryResult) && /* @__PURE__ */ jsx37(
6213
6351
  View30,
6214
6352
  {
@@ -6283,7 +6421,7 @@ var KjunErrorBoundary = class extends Component {
6283
6421
 
6284
6422
  // packages/native/src/skeletons.tsx
6285
6423
  import { tokens as tokens37 } from "@kjun-ui/tokens";
6286
- import { useEffect as useEffect22, useRef as useRef28, useState as useState32 } from "react";
6424
+ import { useEffect as useEffect23, useRef as useRef28, useState as useState32 } from "react";
6287
6425
  import { Animated as Animated17, Platform as Platform24, View as View32, useWindowDimensions as useWindowDimensions6 } from "react-native";
6288
6426
  import Svg5, { Path as Path4 } from "react-native-svg";
6289
6427
 
@@ -6473,7 +6611,7 @@ function KjunChartSkeleton({
6473
6611
  columns = 14
6474
6612
  }) {
6475
6613
  const kind = chartSkeletonKind(requestedKind), { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = useRef28(new Animated17.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = useState32(0), thickness = tokens37.extensions.chartSkeleton.donutThickness, boxHeight = typeof height === "number" ? height : measured, hole = boxHeight > 0 ? chartDonutHole(boxHeight, thickness) : void 0, rowCount = chartSkeletonCount(rows), columnCount = chartSkeletonCount(columns), status = revealed && loadingText ? "status" : void 0, hidden = { "aria-hidden": true, importantForAccessibility: "no-hide-descendants" };
6476
- useEffect22(() => {
6614
+ useEffect23(() => {
6477
6615
  if (reduced || !skeleton) return;
6478
6616
  const loop = Animated17.loop(
6479
6617
  Animated17.sequence([
@@ -6693,12 +6831,12 @@ function KjunAnimatedNumber({
6693
6831
  style
6694
6832
  ],
6695
6833
  children: [
6696
- prefix && /* @__PURE__ */ jsxs27(Text5, { style: typeStyle("caption"), children: [
6834
+ !!prefix && /* @__PURE__ */ jsxs27(Text5, { style: typeStyle("caption"), children: [
6697
6835
  prefix,
6698
6836
  " "
6699
6837
  ] }),
6700
6838
  typeof n === "string" ? n : formatter ? formatter(n) : format(n, decimals),
6701
- suffix && /* @__PURE__ */ jsxs27(Text5, { style: typeStyle("caption"), children: [
6839
+ !!suffix && /* @__PURE__ */ jsxs27(Text5, { style: typeStyle("caption"), children: [
6702
6840
  " ",
6703
6841
  suffix
6704
6842
  ] })
@@ -7104,7 +7242,7 @@ function KjunSparkline({
7104
7242
 
7105
7243
  // shared/package-runtime/table.ts
7106
7244
  import { nextTableSort } from "@kjun-ui/tokens";
7107
- import { useEffect as useEffect24, useRef as useRef31, useState as useState35 } from "react";
7245
+ import { useEffect as useEffect25, useRef as useRef31, useState as useState35 } from "react";
7108
7246
 
7109
7247
  // shared/package-runtime/table-model.ts
7110
7248
  function getValue(row, key) {
@@ -7152,7 +7290,7 @@ function useTableModel(props) {
7152
7290
  key: "",
7153
7291
  order: "asc"
7154
7292
  }), [internalExpanded, setExpanded] = useState35([]), [search, setSearch] = useState35(""), timer = useRef31(void 0);
7155
- useEffect24(() => () => clearTimeout(timer.current), []);
7293
+ useEffect25(() => () => clearTimeout(timer.current), []);
7156
7294
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7157
7295
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7158
7296
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -7272,7 +7410,7 @@ function TableToolbar({
7272
7410
  selectable && selected.length > 0 && /* @__PURE__ */ jsxs29(
7273
7411
  View35,
7274
7412
  {
7275
- accessibilityRole: "toolbar",
7413
+ role: "group",
7276
7414
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7277
7415
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7278
7416
  children: [
@@ -7586,7 +7724,7 @@ function TableCards({
7586
7724
  },
7587
7725
  children: [
7588
7726
  selectable && checkbox(row, i),
7589
- title && /* @__PURE__ */ jsx44(View37, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7727
+ !!title && /* @__PURE__ */ jsx44(View37, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7590
7728
  ]
7591
7729
  }
7592
7730
  ),
@@ -7607,7 +7745,7 @@ function TableCards({
7607
7745
  marginTop: index ? tokens43.dimension.value12 : 0
7608
7746
  },
7609
7747
  children: [
7610
- section.label && /* @__PURE__ */ jsx44(
7748
+ !!section.label && /* @__PURE__ */ jsx44(
7611
7749
  KText,
7612
7750
  {
7613
7751
  style: {
@@ -7672,7 +7810,7 @@ function TableCards({
7672
7810
  children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7673
7811
  }
7674
7812
  ),
7675
- actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7813
+ !!actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7676
7814
  View37,
7677
7815
  {
7678
7816
  style: {
@@ -7964,7 +8102,7 @@ function KjunKpiHero(props) {
7964
8102
  children: loading ? /* @__PURE__ */ jsx47(KjunSkeleton, { type: "block", height: typeStyle("numberMd").lineHeight, width: geometry2.deltaSkeletonWidth }) : /* @__PURE__ */ jsxs34(Fragment15, { children: [
7965
8103
  deltaAbsolute !== null && delta(deltaAbsolute, "absolute", { ...numeric, color: direction(deltaAbsolute), ...typeStyle("numberMd") }),
7966
8104
  deltaPercent !== null && delta(deltaPercent, "percent", { ...numeric, color: direction(deltaPercent), ...typeStyle("numberMd"), opacity: 0.9 }),
7967
- deltaDescription && /* @__PURE__ */ jsx47(
8105
+ !!deltaDescription && /* @__PURE__ */ jsx47(
7968
8106
  KText,
7969
8107
  {
7970
8108
  style: {
@@ -7978,7 +8116,7 @@ function KjunKpiHero(props) {
7978
8116
  }
7979
8117
  )
7980
8118
  ] }),
7981
- aside && !mobile && /* @__PURE__ */ jsx47(View40, { children: aside })
8119
+ hasContent(aside) && !mobile && /* @__PURE__ */ jsx47(View40, { children: content(aside) })
7982
8120
  ]
7983
8121
  }
7984
8122
  ),
@@ -8040,7 +8178,7 @@ function KjunKpiHero(props) {
8040
8178
  children: kpiFormat(item)
8041
8179
  }
8042
8180
  ),
8043
- item.desc && (loading ? /* @__PURE__ */ jsx47(
8181
+ !!item.desc && (loading ? /* @__PURE__ */ jsx47(
8044
8182
  KjunSkeleton,
8045
8183
  {
8046
8184
  type: "block",
@@ -8251,7 +8389,7 @@ function KjunKpiRow({
8251
8389
  {
8252
8390
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }],
8253
8391
  children: [
8254
- item.prefix && /* @__PURE__ */ jsx48(
8392
+ !!item.prefix && /* @__PURE__ */ jsx48(
8255
8393
  Text6,
8256
8394
  {
8257
8395
  style: {
@@ -8269,7 +8407,7 @@ function KjunKpiRow({
8269
8407
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }]
8270
8408
  }
8271
8409
  ) : kpiFormat(item),
8272
- item.suffix && /* @__PURE__ */ jsx48(
8410
+ !!item.suffix && /* @__PURE__ */ jsx48(
8273
8411
  Text6,
8274
8412
  {
8275
8413
  style: {
@@ -8285,7 +8423,7 @@ function KjunKpiRow({
8285
8423
  ]
8286
8424
  }
8287
8425
  ),
8288
- item.desc && (busy ? /* @__PURE__ */ jsx48(KjunSkeleton, { type: "block", height: typeStyle("caption").lineHeight, width: geometry2.descriptionSkeletonWidth }) : /* @__PURE__ */ jsx48(
8426
+ !!item.desc && (busy ? /* @__PURE__ */ jsx48(KjunSkeleton, { type: "block", height: typeStyle("caption").lineHeight, width: geometry2.descriptionSkeletonWidth }) : /* @__PURE__ */ jsx48(
8289
8427
  KText,
8290
8428
  {
8291
8429
  style: {
@@ -8310,7 +8448,7 @@ import { tokens as tokens48 } from "@kjun-ui/tokens";
8310
8448
  import { ScrollView as ScrollView10, View as View43 } from "react-native";
8311
8449
 
8312
8450
  // shared/package-runtime/market-cards.ts
8313
- import { useEffect as useEffect25, useRef as useRef32, useState as useState38 } from "react";
8451
+ import { useEffect as useEffect26, useRef as useRef32, useState as useState38 } from "react";
8314
8452
  function useMarketCardsModel(props, persistence) {
8315
8453
  const {
8316
8454
  columns,
@@ -8335,7 +8473,7 @@ function useMarketCardsModel(props, persistence) {
8335
8473
  } catch (e) {
8336
8474
  }
8337
8475
  };
8338
- useEffect25(() => {
8476
+ useEffect26(() => {
8339
8477
  var _a, _b;
8340
8478
  if (mounted.current === restoreSignature) return;
8341
8479
  mounted.current = restoreSignature;
@@ -9131,26 +9269,26 @@ var LastListRow = createContext20(false);
9131
9269
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
9132
9270
  const { colors } = useKjunStyles(), last = useContext17(LastListRow);
9133
9271
  const parts = /* @__PURE__ */ jsxs41(Fragment18, { children: [
9134
- leading && /* @__PURE__ */ jsx54(View47, { children: content(leading) }),
9272
+ hasContent(leading) && /* @__PURE__ */ jsx54(View47, { children: content(leading) }),
9135
9273
  /* @__PURE__ */ jsxs41(View47, { style: { flex: 1, minWidth: 0 }, children: [
9136
9274
  /* @__PURE__ */ jsx54(KText, { style: { ...typeStyle("label") }, children: title }),
9137
- description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens52.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9275
+ !!description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens52.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9138
9276
  ] }),
9139
- trailing && /* @__PURE__ */ jsx54(View47, { style: { maxWidth: "35%" }, children: content(trailing) })
9277
+ hasContent(trailing) && /* @__PURE__ */ jsx54(View47, { style: { maxWidth: "35%" }, children: content(trailing) })
9140
9278
  ] });
9141
9279
  const style = { flex: 1, minWidth: 0, minHeight: tokens52.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: tokens52.extensions.list.gap, padding: tokens52.extensions.list.padding };
9142
9280
  return /* @__PURE__ */ jsxs41(View47, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: tokens52.extensions.list.gap, borderBottomWidth: last ? 0 : tokens52.border.defaultWidth, borderBottomColor: colors.divider }, children: [
9143
9281
  onPress || href ? /* @__PURE__ */ jsx54(AccessiblePressable, { ...Platform25.OS === "web" && href && !disabled ? { href } : {}, accessibilityRole: href ? "link" : "button", accessibilityLabel: title + (description ? ", " + description : ""), disabled, accessibilityState: { disabled }, onPress, style: ({ pressed }) => ({ ...style, opacity: disabled ? tokens52.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ jsx54(View47, { style, children: parts }),
9144
- actions && /* @__PURE__ */ jsx54(View47, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform25.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens52.extensions.list.actionsGap, paddingRight: tokens52.extensions.list.actionsPadding, opacity: disabled ? tokens52.states.opacity.disabled : 1 }, children: content(actions) })
9282
+ hasContent(actions) && /* @__PURE__ */ jsx54(View47, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform25.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens52.extensions.list.actionsGap, paddingRight: tokens52.extensions.list.actionsPadding, opacity: disabled ? tokens52.states.opacity.disabled : 1 }, children: content(actions) })
9145
9283
  ] });
9146
9284
  }
9147
9285
  function KjunListSection({ title, description, actions, children, ariaLabel }) {
9148
9286
  const { colors } = useKjunStyles();
9149
9287
  return /* @__PURE__ */ jsxs41(View47, { style: { minWidth: 0, backgroundColor: colors.surface }, children: [
9150
- (title || description || actions) && /* @__PURE__ */ jsxs41(View47, { style: { flexDirection: "row", alignItems: "center", gap: tokens52.extensions.list.gap, padding: tokens52.extensions.list.padding }, children: [
9288
+ !!(title || description || actions) && /* @__PURE__ */ jsxs41(View47, { style: { flexDirection: "row", alignItems: "center", gap: tokens52.extensions.list.gap, padding: tokens52.extensions.list.padding }, children: [
9151
9289
  /* @__PURE__ */ jsxs41(View47, { style: { flex: 1 }, children: [
9152
- title && /* @__PURE__ */ jsx54(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9153
- description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens52.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9290
+ !!title && /* @__PURE__ */ jsx54(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9291
+ !!description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens52.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9154
9292
  ] }),
9155
9293
  content(actions)
9156
9294
  ] }),
@@ -9178,12 +9316,12 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9178
9316
  const flatten = (node) => Children5.toArray(node).flatMap((child) => isValidElement7(child) && child.type === Fragment19 ? flatten(child.props.children) : [child]);
9179
9317
  const actionList = flatten(actions);
9180
9318
  return /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: geometry2, children: /* @__PURE__ */ jsx55(View48, { style: { flexShrink: 0, paddingTop: Math.max(0, safeAreaTop), backgroundColor: colors.surface }, children: /* @__PURE__ */ jsxs42(View48, { style: { minHeight: geometry2.minimumHeight, flexDirection: "row", flexWrap: "wrap", alignItems: "flex-start", gap: geometry2.gap, paddingVertical: geometry2.paddingY, paddingHorizontal: geometry2.paddingX }, children: [
9181
- leading && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ jsx55(View48, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
9319
+ hasContent(leading) && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ jsx55(View48, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
9182
9320
  /* @__PURE__ */ jsxs42(View48, { style: { flexGrow: 1, flexShrink: 1, flexBasis: geometry2.titleMinimumWidth, minWidth: 0, paddingVertical: titleInset }, children: [
9183
9321
  /* @__PURE__ */ jsx55(KText, { accessibilityRole: "header", style: { ...wrapping, ...typeStyle("sectionTitle"), fontWeight: typeStyle("control").fontWeight }, children: title }),
9184
- description && /* @__PURE__ */ jsx55(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9322
+ !!description && /* @__PURE__ */ jsx55(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9185
9323
  ] }),
9186
- actions && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "actions" }, children: /* @__PURE__ */ jsx55(View48, { style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "100%", marginLeft: "auto", flexShrink: 1, flexDirection: "row", flexWrap: "wrap", alignItems: "center", justifyContent: "flex-end", gap: geometry2.actionsGap }, children: actionList.map((action, index) => index < actionList.length - 1 ? content(action) : /* @__PURE__ */ jsx55(View48, { onLayout: square("trailing"), style: { flexShrink: 1, minWidth: 0, maxWidth: "100%", marginRight: iconEdges.trailing ? -edgeInset : 0 }, children: content(action) }, "trailing")) }) })
9324
+ hasContent(actions) && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "actions" }, children: /* @__PURE__ */ jsx55(View48, { style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "100%", marginLeft: "auto", flexShrink: 1, flexDirection: "row", flexWrap: "wrap", alignItems: "center", justifyContent: "flex-end", gap: geometry2.actionsGap }, children: actionList.map((action, index) => index < actionList.length - 1 ? content(action) : /* @__PURE__ */ jsx55(View48, { onLayout: square("trailing"), style: { flexShrink: 1, minWidth: 0, maxWidth: "100%", marginRight: iconEdges.trailing ? -edgeInset : 0 }, children: content(action) }, "trailing")) }) })
9187
9325
  ] }) }) });
9188
9326
  }
9189
9327
  function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\uC0C9", safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = true, onNavigate }) {
@@ -9233,7 +9371,7 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9233
9371
  },
9234
9372
  children: [
9235
9373
  hasIcons && /* @__PURE__ */ jsxs42(View48, { style: { width: geometry2.iconSize, height: geometry2.iconSize, flexShrink: 0 }, children: [
9236
- item.icon && /* @__PURE__ */ jsx55(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9374
+ !!item.icon && /* @__PURE__ */ jsx55(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9237
9375
  badge
9238
9376
  ] }),
9239
9377
  /* @__PURE__ */ jsxs42(View48, { style: { maxWidth: "100%" }, children: [
@@ -9265,7 +9403,7 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9265
9403
  flexDirection: wide ? "row" : "column",
9266
9404
  alignItems: wide ? "center" : "stretch"
9267
9405
  }, children: [
9268
- description && /* @__PURE__ */ jsx55(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9406
+ !!description && /* @__PURE__ */ jsx55(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9269
9407
  /* @__PURE__ */ jsx55(View48, { style: { flexDirection: "row", flexWrap: "wrap", justifyContent: "flex-end", alignItems: "center", gap: geometry2.actionsGap, minWidth: 0, maxWidth: "100%", flexShrink: 1 }, children: content(children) })
9270
9408
  ] }) });
9271
9409
  }
@@ -9303,14 +9441,14 @@ import { jsx as jsx57, jsxs as jsxs43 } from "react/jsx-runtime";
9303
9441
  function KjunChip({ label, icon, removable = false, disabled = false, size = "md", removeLabel, onRemove }) {
9304
9442
  const { colors } = useKjunStyles();
9305
9443
  return /* @__PURE__ */ jsxs43(View50, { style: { alignSelf: "flex-start", maxWidth: "100%", minWidth: 0, flexShrink: 1, flexDirection: "row", alignItems: "center", gap: tokens55.extensions.chip.gap, minHeight: removable ? tokens55.extensions.chip.nativeRemoveSize : tokens55.extensions.chip[size], paddingVertical: removable ? 0 : tokens55.extensions.chip.paddingY, paddingLeft: tokens55.extensions.chip.paddingX, paddingRight: removable ? 0 : tokens55.extensions.chip.paddingX, borderRadius: tokens55.extensions.chip.radius, backgroundColor: colors.secondary, opacity: disabled ? tokens55.states.opacity.disabled : 1 }, children: [
9306
- icon && /* @__PURE__ */ jsx57(KjunIcon, { name: icon, size: tokens55.extensions.chip.iconSizes[size] }),
9444
+ !!icon && /* @__PURE__ */ jsx57(KjunIcon, { name: icon, size: tokens55.extensions.chip.iconSizes[size] }),
9307
9445
  /* @__PURE__ */ jsx57(KText, { style: { ...typeStyle(tokens55.extensions.chip.typography[size]), flexShrink: 1, minWidth: 0 }, children: label }),
9308
9446
  removable && /* @__PURE__ */ jsx57(AccessiblePressable, { accessibilityRole: "button", accessibilityLabel: removeLabel || label + " \uC0AD\uC81C", disabled, accessibilityState: { disabled }, onPress: onRemove, style: { width: tokens55.extensions.chip.nativeRemoveSize, height: tokens55.extensions.chip.nativeRemoveSize, borderRadius: tokens55.extensions.chip.radius, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx57(KjunIcon, { name: "x", size: tokens55.extensions.chip.removeIconSizes[size] }) })
9309
9447
  ] });
9310
9448
  }
9311
9449
 
9312
9450
  // packages/native/src/range-input.tsx
9313
- import { useEffect as useEffect26, useMemo as useMemo2, useRef as useRef34, useState as useState42 } from "react";
9451
+ import { useEffect as useEffect27, useMemo as useMemo2, useRef as useRef34, useState as useState42 } from "react";
9314
9452
  import { PanResponder, Platform as Platform27, Pressable as Pressable3, View as View51 } from "react-native";
9315
9453
  import { tokens as tokens56, createSliderDomain, sliderKey } from "@kjun-ui/tokens";
9316
9454
  import { jsx as jsx58, jsxs as jsxs44 } from "react/jsx-runtime";
@@ -9342,10 +9480,10 @@ function RangeInput({
9342
9480
  latest.current = values;
9343
9481
  const callbacks = useRef34({ onValueChange, onChangeCommit });
9344
9482
  callbacks.current = { onValueChange, onChangeCommit };
9345
- useEffect26(() => {
9483
+ useEffect27(() => {
9346
9484
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9347
9485
  }, [valid]);
9348
- useEffect26(() => {
9486
+ useEffect27(() => {
9349
9487
  if (blocked) {
9350
9488
  setDragging(null);
9351
9489
  setHovered(null);
@@ -9431,7 +9569,7 @@ function RangeInput({
9431
9569
  minWidth: 0
9432
9570
  },
9433
9571
  children: [
9434
- label && /* @__PURE__ */ jsx58(
9572
+ !!label && /* @__PURE__ */ jsx58(
9435
9573
  KText,
9436
9574
  {
9437
9575
  style: {
@@ -9610,7 +9748,7 @@ function KjunRangeSlider(props) {
9610
9748
  }
9611
9749
 
9612
9750
  // packages/native/src/quantity-stepper.tsx
9613
- import { useEffect as useEffect27, useRef as useRef35, useState as useState43 } from "react";
9751
+ import { useEffect as useEffect28, useRef as useRef35, useState as useState43 } from "react";
9614
9752
  import { Platform as Platform28, View as View52 } from "react-native";
9615
9753
  import { createNumberDomain, quantityKeyAction, tokens as tokens57 } from "@kjun-ui/tokens";
9616
9754
 
@@ -9624,7 +9762,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9624
9762
  const { colors, fontFamily } = useKjunStyles();
9625
9763
  const field = useKjunField();
9626
9764
  const invalid = error || !!(field == null ? void 0 : field.error);
9627
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9765
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9628
9766
  const geometry2 = tokens57.extensions.compound;
9629
9767
  const buttonStyle = { width: tokens57.input[size].height, paddingHorizontal: 0 };
9630
9768
  const outer = tokens57.input[size].radius;
@@ -9636,12 +9774,12 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9636
9774
  const inputRef = useRef35(null);
9637
9775
  const composing = useRef35(false), dirty = useRef35(false), accepted = useRef35(value);
9638
9776
  const cancelledEscape = useRef35(false);
9639
- useEffect27(() => {
9777
+ useEffect28(() => {
9640
9778
  setDraft(String(value));
9641
9779
  accepted.current = value;
9642
9780
  dirty.current = false;
9643
9781
  }, [value, precision, min, max, step]);
9644
- useEffect27(() => {
9782
+ useEffect28(() => {
9645
9783
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9646
9784
  }, [valid]);
9647
9785
  const emit = (next) => {
@@ -9686,7 +9824,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9686
9824
  dirty.current = true;
9687
9825
  setDraft(text);
9688
9826
  } };
9689
- useEffect27(() => {
9827
+ useEffect28(() => {
9690
9828
  if (Platform28.OS !== "web") return;
9691
9829
  const input = inputRef.current;
9692
9830
  const keydown = (event) => handlers.current.key(event);
@@ -9732,7 +9870,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9732
9870
  nativeID: id || (field == null ? void 0 : field.id),
9733
9871
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9734
9872
  accessibilityLabelledBy: labelledBy,
9735
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
9873
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9736
9874
  role: "spinbutton",
9737
9875
  accessibilityValue: { min, max, now: value },
9738
9876
  accessibilityState: { disabled: blocked },
@@ -9758,7 +9896,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9758
9896
  }
9759
9897
 
9760
9898
  // packages/native/src/time-picker.tsx
9761
- import { Fragment as Fragment20, useEffect as useEffect28, useId as useId11, useMemo as useMemo3, useRef as useRef36, useState as useState44 } from "react";
9899
+ import { Fragment as Fragment20, useEffect as useEffect29, useId as useId11, useMemo as useMemo3, useRef as useRef36, useState as useState44 } from "react";
9762
9900
  import { View as View53 } from "react-native";
9763
9901
  import { createTimeDomain, tokens as tokens58 } from "@kjun-ui/tokens";
9764
9902
  import { jsx as jsx60, jsxs as jsxs46 } from "react/jsx-runtime";
@@ -9770,7 +9908,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9770
9908
  const field = useKjunField(), uid = useId11();
9771
9909
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
9772
9910
  const domain = useMemo3(() => createTimeDomain({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
9773
- useEffect28(() => {
9911
+ useEffect29(() => {
9774
9912
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9775
9913
  }, [valid]);
9776
9914
  const change = (next) => {
@@ -9792,7 +9930,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9792
9930
  change(null);
9793
9931
  (_a = first.current) == null ? void 0 : _a.focus();
9794
9932
  } }) }),
9795
- (error || !!(field == null ? void 0 : field.error) || focused || opened !== null) && /* @__PURE__ */ jsx60(View53, { pointerEvents: "none", "aria-hidden": true, style: { position: "absolute", inset: 0, borderRadius: spec.radius, borderWidth: error || !!(field == null ? void 0 : field.error) ? tokens58.border.controlWidth : tokens58.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
9933
+ !!(error || !!(field == null ? void 0 : field.error) || focused || opened !== null) && /* @__PURE__ */ jsx60(View53, { pointerEvents: "none", "aria-hidden": true, style: { position: "absolute", inset: 0, borderRadius: spec.radius, borderWidth: error || !!(field == null ? void 0 : field.error) ? tokens58.border.controlWidth : tokens58.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
9796
9934
  ] });
9797
9935
  }
9798
9936
  export {