@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.web.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
  }
@@ -773,6 +787,7 @@ function KjunButton(input) {
773
787
  size: declaredSize = "md",
774
788
  loading = false,
775
789
  block = false,
790
+ wrap = false,
776
791
  disabled = false,
777
792
  prefixIcon,
778
793
  suffixIcon,
@@ -793,7 +808,7 @@ function KjunButton(input) {
793
808
  const formActions = useContext5(FormActionsContext);
794
809
  const cardActions = useContext5(CardActionsContext);
795
810
  const stateContent = useContext5(DataStateContentContext);
796
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
811
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
797
812
  const height = Math.max(tokens3.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
798
813
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : tokens3.button.iconSizes[size];
799
814
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -891,7 +906,7 @@ function KjunButton(input) {
891
906
  opacity: overlayLoading ? 0 : 1,
892
907
  ...multiline || navigation ? { flexShrink: 1, minWidth: 0, textAlign: "center" } : {}
893
908
  }, children }),
894
- suffixIcon && /* @__PURE__ */ jsx5(View2, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ jsx5(
909
+ !!suffixIcon && /* @__PURE__ */ jsx5(View2, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ jsx5(
895
910
  KjunIcon,
896
911
  {
897
912
  name: busy && !prefixIcon ? "loader-2" : suffixIcon,
@@ -954,6 +969,16 @@ function FieldClear({ size, disabled, label, onPress }) {
954
969
  );
955
970
  }
956
971
 
972
+ // shared/package-runtime/aria-ids.ts
973
+ function mergeIds(...lists) {
974
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
975
+ return ids.length ? ids.join(" ") : void 0;
976
+ }
977
+ function joinHints(...parts) {
978
+ const text = parts.filter(Boolean);
979
+ return text.length ? text.join(". ") : void 0;
980
+ }
981
+
957
982
  // packages/native/src/field-surface.ts
958
983
  import { useState as useState6 } from "react";
959
984
  import { Animated as Animated3, Platform as Platform5, TextInput } from "react-native";
@@ -1141,6 +1166,7 @@ function KjunFormGroup({
1141
1166
  required = false,
1142
1167
  error = "",
1143
1168
  hint = "",
1169
+ labelHidden = false,
1144
1170
  children,
1145
1171
  style
1146
1172
  }) {
@@ -1150,9 +1176,17 @@ function KjunFormGroup({
1150
1176
  return /* @__PURE__ */ jsx8(
1151
1177
  FieldContext.Provider,
1152
1178
  {
1153
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1179
+ value: {
1180
+ id: fieldId,
1181
+ label,
1182
+ error,
1183
+ hint,
1184
+ required,
1185
+ labelHidden,
1186
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1187
+ },
1154
1188
  children: /* @__PURE__ */ jsxs2(View4, { style: [{ minWidth: 0, marginBottom: tokens7.extensions.form.itemGap }, style], children: [
1155
- label && /* @__PURE__ */ jsxs2(
1189
+ !!label && !labelHidden && /* @__PURE__ */ jsxs2(
1156
1190
  Text3,
1157
1191
  {
1158
1192
  nativeID: fieldId + "-label",
@@ -1169,7 +1203,15 @@ function KjunFormGroup({
1169
1203
  }
1170
1204
  ),
1171
1205
  children,
1172
- (error || hint) && /* @__PURE__ */ jsxs2(
1206
+ !!hint && /* @__PURE__ */ jsx8(
1207
+ Text3,
1208
+ {
1209
+ nativeID: fieldId + "-hint",
1210
+ style: { fontFamily, ...typeStyle("caption"), marginTop: tokens7.extensions.form.messageGap, color: c.textSecondary },
1211
+ children: hint
1212
+ }
1213
+ ),
1214
+ !!error && /* @__PURE__ */ jsxs2(
1173
1215
  View4,
1174
1216
  {
1175
1217
  style: {
@@ -1179,19 +1221,14 @@ function KjunFormGroup({
1179
1221
  marginTop: tokens7.extensions.form.messageGap
1180
1222
  },
1181
1223
  children: [
1182
- !!error && /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1224
+ /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1183
1225
  /* @__PURE__ */ jsx8(
1184
1226
  Text3,
1185
1227
  {
1186
- nativeID: fieldId + (error ? "-error" : "-hint"),
1187
- accessibilityRole: error ? "alert" : void 0,
1188
- style: {
1189
- fontFamily,
1190
- ...typeStyle("caption"),
1191
- flexShrink: 1,
1192
- color: error ? c.danger : c.textSecondary
1193
- },
1194
- children: error || hint
1228
+ nativeID: fieldId + "-error",
1229
+ accessibilityRole: "alert",
1230
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1231
+ children: error
1195
1232
  }
1196
1233
  )
1197
1234
  ]
@@ -1241,6 +1278,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1241
1278
  const invalid = !!(error || errorMessage || (field == null ? void 0 : field.error));
1242
1279
  const canEdit = !disabled && !readOnly && editable;
1243
1280
  const surface = useFieldSurface(size, { disabled, readOnly: !!readOnly || !editable, invalid, open: props["aria-expanded"] === true });
1281
+ const affixText = { ...inputTypeStyle(size), color: surface.icon };
1244
1282
  const showClear = clearable && !!value;
1245
1283
  const [prefixWidth, setPrefixWidth] = useState7(0);
1246
1284
  const [affixWidth, setAffixWidth] = useState7(0);
@@ -1265,8 +1303,8 @@ var KjunInput = forwardRef2(function KjunInput2({
1265
1303
  editable: canEdit,
1266
1304
  readOnly,
1267
1305
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1268
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1269
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1306
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1307
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1270
1308
  accessibilityState: { disabled },
1271
1309
  ...Platform6.OS === "web" ? {
1272
1310
  // Native Web TextInput does not translate accessibilityState.
@@ -1302,7 +1340,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1302
1340
  ]
1303
1341
  }
1304
1342
  ),
1305
- (prefix || prefixIcon) && /* @__PURE__ */ jsx8(
1343
+ !!(prefix || prefixIcon) && /* @__PURE__ */ jsx8(
1306
1344
  View4,
1307
1345
  {
1308
1346
  onLayout: (event) => setPrefixWidth(event.nativeEvent.layout.width),
@@ -1314,7 +1352,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1314
1352
  bottom: 0,
1315
1353
  justifyContent: "center"
1316
1354
  },
1317
- children: prefix || /* @__PURE__ */ jsx8(
1355
+ children: prefix ? content(prefix, affixText) : /* @__PURE__ */ jsx8(
1318
1356
  KjunIcon,
1319
1357
  {
1320
1358
  name: prefixIcon,
@@ -1324,7 +1362,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1324
1362
  )
1325
1363
  }
1326
1364
  ),
1327
- (showClear || suffix || suffixIcon) && /* @__PURE__ */ jsxs2(
1365
+ !!(showClear || suffix || suffixIcon) && /* @__PURE__ */ jsxs2(
1328
1366
  View4,
1329
1367
  {
1330
1368
  onLayout: (event) => setAffixWidth(event.nativeEvent.layout.width),
@@ -1345,7 +1383,7 @@ var KjunInput = forwardRef2(function KjunInput2({
1345
1383
  onClear == null ? void 0 : onClear();
1346
1384
  (_a = input.current) == null ? void 0 : _a.focus();
1347
1385
  } }),
1348
- suffix || suffixIcon && /* @__PURE__ */ jsx8(
1386
+ suffix ? content(suffix, affixText) : !!suffixIcon && /* @__PURE__ */ jsx8(
1349
1387
  KjunIcon,
1350
1388
  {
1351
1389
  name: suffixIcon,
@@ -1385,6 +1423,17 @@ function KjunFormLayout({
1385
1423
  ) });
1386
1424
  }
1387
1425
 
1426
+ // shared/package-runtime/content-presence.ts
1427
+ import { Fragment as Fragment2, isValidElement as isValidElement2 } from "react";
1428
+ function hasContent(node) {
1429
+ if (node == null || typeof node === "boolean") return false;
1430
+ if (typeof node === "string") return node.trim().length > 0;
1431
+ if (Array.isArray(node)) return node.some(hasContent);
1432
+ if (isValidElement2(node) && node.type === Fragment2)
1433
+ return hasContent(node.props.children);
1434
+ return true;
1435
+ }
1436
+
1388
1437
  // packages/native/src/layer-backdrop.tsx
1389
1438
  import { Platform as Platform7, Pressable as Pressable2, View as View5 } from "react-native";
1390
1439
  import { jsx as jsx9 } from "react/jsx-runtime";
@@ -1425,7 +1474,7 @@ import { Platform as Platform8, View as View7 } from "react-native";
1425
1474
  import { useLayoutEffect as useLayoutEffect4, useState as useState8 } from "react";
1426
1475
  import { createPortal } from "react-dom";
1427
1476
  import { Modal, View as View6 } from "react-native";
1428
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
1477
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
1429
1478
  function ModalWindow({ generation, children, visible, ...props }) {
1430
1479
  const [container, setContainer] = useState8(null);
1431
1480
  useLayoutEffect4(() => {
@@ -1434,7 +1483,7 @@ function ModalWindow({ generation, children, visible, ...props }) {
1434
1483
  setContainer(element);
1435
1484
  return () => element.remove();
1436
1485
  }, []);
1437
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1486
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
1438
1487
  /* @__PURE__ */ jsx10(Modal, { ...props, visible, children: /* @__PURE__ */ jsx10(View6, { style: { flex: 1 }, ref: (node) => {
1439
1488
  if (node && container && container.parentNode !== node)
1440
1489
  node.appendChild(container);
@@ -1696,7 +1745,7 @@ function KjunModal({
1696
1745
  borderBottomColor: c.border
1697
1746
  },
1698
1747
  children: [
1699
- /* @__PURE__ */ jsx12(View8, { style: { flex: 1, paddingRight: tokens8.modal.titleActionGap }, children: header || /* @__PURE__ */ jsx12(
1748
+ /* @__PURE__ */ jsx12(View8, { style: { flex: 1, paddingRight: tokens8.modal.titleActionGap }, children: hasContent(header) ? content(header) : /* @__PURE__ */ jsx12(
1700
1749
  Text4,
1701
1750
  {
1702
1751
  accessibilityRole: "header",
@@ -1736,7 +1785,7 @@ function KjunModal({
1736
1785
  children
1737
1786
  }
1738
1787
  ),
1739
- (showFooter || footer) && /* @__PURE__ */ jsx12(
1788
+ (showFooter || hasContent(footer)) && /* @__PURE__ */ jsx12(
1740
1789
  View8,
1741
1790
  {
1742
1791
  style: {
@@ -1749,7 +1798,7 @@ function KjunModal({
1749
1798
  borderTopWidth: tokens8.border.defaultWidth,
1750
1799
  borderTopColor: c.border
1751
1800
  },
1752
- children: footer || /* @__PURE__ */ jsx12(
1801
+ children: hasContent(footer) ? content(footer) : /* @__PURE__ */ jsx12(
1753
1802
  KjunFormActions,
1754
1803
  {
1755
1804
  size: footerSize,
@@ -1785,19 +1834,6 @@ import { tokens as tokens9 } from "@kjun-ui/tokens";
1785
1834
  import { useId as useId4 } from "react";
1786
1835
  import { Platform as Platform10, View as View9 } from "react-native";
1787
1836
  import Svg2, { Defs, LinearGradient, Rect as Rect2, Stop } from "react-native-svg";
1788
-
1789
- // shared/package-runtime/content-presence.ts
1790
- import { Fragment as Fragment3, isValidElement as isValidElement2 } from "react";
1791
- function hasContent(node) {
1792
- if (node == null || typeof node === "boolean") return false;
1793
- if (typeof node === "string") return node.trim().length > 0;
1794
- if (Array.isArray(node)) return node.some(hasContent);
1795
- if (isValidElement2(node) && node.type === Fragment3)
1796
- return hasContent(node.props.children);
1797
- return true;
1798
- }
1799
-
1800
- // packages/native/src/card.tsx
1801
1837
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
1802
1838
  function KjunCard({
1803
1839
  title,
@@ -1858,8 +1894,8 @@ function KjunCard({
1858
1894
  borderBottomColor: borderColor
1859
1895
  }, children: [
1860
1896
  hasHeading && /* @__PURE__ */ jsx13(View9, { style: { flexGrow: 1, flexShrink: 1, minWidth: 0, maxWidth: "100%" }, children: hasContent(header) ? content(header, { color: foreground }) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
1861
- title && /* @__PURE__ */ jsx13(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1862
- subtitle && /* @__PURE__ */ jsx13(KText, { style: { ...typeStyle("caption"), marginTop: tokens9.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1897
+ !!title && /* @__PURE__ */ jsx13(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1898
+ !!subtitle && /* @__PURE__ */ jsx13(KText, { style: { ...typeStyle("caption"), marginTop: tokens9.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1863
1899
  ] }) }),
1864
1900
  hasContent(headerActions) && /* @__PURE__ */ jsx13(CardActionsContext.Provider, { value: true, children: /* @__PURE__ */ jsx13(View9, { 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 }) }) })
1865
1901
  ] }),
@@ -1879,6 +1915,13 @@ function KjunCard({
1879
1915
  ] });
1880
1916
  }
1881
1917
 
1918
+ // shared/package-runtime/alert-live.ts
1919
+ function alertRole(tone, live) {
1920
+ if (live === "off") return void 0;
1921
+ if (live) return live === "assertive" ? "alert" : "status";
1922
+ return tone === "danger" ? "alert" : "status";
1923
+ }
1924
+
1882
1925
  // packages/native/src/display-primitives.tsx
1883
1926
  import { tokens as tokens10 } from "@kjun-ui/tokens";
1884
1927
  import { useContext as useContext8, useEffect as useEffect5, useRef as useRef6, useState as useState10 } from "react";
@@ -1970,9 +2013,9 @@ function KjunEmpty({
1970
2013
  paddingVertical: stateContent === "empty" ? 0 : tokens10.dimension.value32
1971
2014
  },
1972
2015
  children: [
1973
- icon && /* @__PURE__ */ jsx14(View10, { style: { marginBottom: spec.iconGap }, children: /* @__PURE__ */ jsx14(KjunIcon, { name: icon, size: spec.iconSize, color: colors.textSecondary }) }),
2016
+ !!icon && /* @__PURE__ */ jsx14(View10, { style: { marginBottom: spec.iconGap }, children: /* @__PURE__ */ jsx14(KjunIcon, { name: icon, size: spec.iconSize, color: colors.textSecondary }) }),
1974
2017
  /* @__PURE__ */ jsx14(KText, { style: { ...typeStyle("cardTitle"), color: colors.text, fontSize: spec.titleSize, lineHeight: spec.titleLineHeight, textAlign: "center", maxWidth: spec.textMaxWidth }, children: text }),
1975
- description && /* @__PURE__ */ jsx14(
2018
+ !!description && /* @__PURE__ */ jsx14(
1976
2019
  KText,
1977
2020
  {
1978
2021
  style: {
@@ -1987,7 +2030,7 @@ function KjunEmpty({
1987
2030
  children: description
1988
2031
  }
1989
2032
  ),
1990
- children && /* @__PURE__ */ jsx14(View10, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
2033
+ hasContent(children) && /* @__PURE__ */ jsx14(View10, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
1991
2034
  ]
1992
2035
  }
1993
2036
  );
@@ -2065,7 +2108,7 @@ function KjunSpinner({ size = "md", text }) {
2065
2108
  )
2066
2109
  }
2067
2110
  ),
2068
- text && /* @__PURE__ */ jsx14(KText, { style: { marginLeft: tokens10.dimension.value8, color: colors.textSecondary }, children: text })
2111
+ !!text && /* @__PURE__ */ jsx14(KText, { style: { marginLeft: tokens10.dimension.value8, color: colors.textSecondary }, children: text })
2069
2112
  ]
2070
2113
  }
2071
2114
  );
@@ -2076,6 +2119,7 @@ function KjunAlert({
2076
2119
  size = "md",
2077
2120
  title,
2078
2121
  closable = false,
2122
+ live,
2079
2123
  children,
2080
2124
  actions,
2081
2125
  onClose
@@ -2092,7 +2136,7 @@ function KjunAlert({
2092
2136
  return /* @__PURE__ */ jsxs7(
2093
2137
  View10,
2094
2138
  {
2095
- role: tone === "danger" ? "alert" : "status",
2139
+ role: alertRole(tone, live),
2096
2140
  style: {
2097
2141
  flexDirection: "row",
2098
2142
  alignItems: "flex-start",
@@ -2125,7 +2169,7 @@ function KjunAlert({
2125
2169
  ...typeStyle(small ? "caption" : "body"),
2126
2170
  color: colors.textSecondary
2127
2171
  }) }),
2128
- hasContent(actions) && /* @__PURE__ */ jsx14(ActionSizeContext.Provider, { value: alertActionSizes[small ? "sm" : "md"], children: /* @__PURE__ */ jsx14(AlertActionsContext.Provider, { value: true, children: /* @__PURE__ */ jsx14(View10, { style: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: geometry2.actionsGap, marginTop: geometry2[small ? "sm" : "md"].actionGap }, children: actions }) }) })
2172
+ hasContent(actions) && /* @__PURE__ */ jsx14(ActionSizeContext.Provider, { value: alertActionSizes[small ? "sm" : "md"], children: /* @__PURE__ */ jsx14(AlertActionsContext.Provider, { value: true, children: /* @__PURE__ */ jsx14(View10, { style: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: geometry2.actionsGap, marginTop: geometry2[small ? "sm" : "md"].actionGap }, children: content(actions) }) }) })
2129
2173
  ] }),
2130
2174
  closable && /* @__PURE__ */ jsx14(
2131
2175
  AccessiblePressable,
@@ -2175,7 +2219,7 @@ function KjunProgress({
2175
2219
  const { colors } = useKjunStyles();
2176
2220
  const percentage = Number.isFinite(value) && max > 0 ? Math.min(100, Math.max(0, Math.round(value / max * 100))) : 0;
2177
2221
  return /* @__PURE__ */ jsxs7(View10, { children: [
2178
- (label || showLabel) && /* @__PURE__ */ jsxs7(
2222
+ !!(label || showLabel) && /* @__PURE__ */ jsxs7(
2179
2223
  View10,
2180
2224
  {
2181
2225
  style: {
@@ -2593,7 +2637,7 @@ function ToastView({
2593
2637
  children: [
2594
2638
  /* @__PURE__ */ jsx16(KjunIcon, { name: icon, color: colors[`${tone}Accent`] }),
2595
2639
  /* @__PURE__ */ jsxs9(View12, { style: { flex: 1, minWidth: 0 }, children: [
2596
- toast.title && /* @__PURE__ */ jsx16(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: tokens13.extensions.toast.titleGap }, children: toast.title }),
2640
+ !!toast.title && /* @__PURE__ */ jsx16(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: tokens13.extensions.toast.titleGap }, children: toast.title }),
2597
2641
  /* @__PURE__ */ jsx16(KText, { style: { color: colors.textSecondary }, children: toast.message }),
2598
2642
  toast.action && /* @__PURE__ */ jsx16(
2599
2643
  AccessiblePressable,
@@ -2847,7 +2891,7 @@ function FeedbackDialog({
2847
2891
  onConfirm: submit,
2848
2892
  confirmVariant: options.type === "danger" ? "danger" : "primary",
2849
2893
  children: [
2850
- options.message && /* @__PURE__ */ jsx18(KText, { style: { marginBottom: tokens16.dimension.value16 }, children: options.message }),
2894
+ !!options.message && /* @__PURE__ */ jsx18(KText, { style: { marginBottom: tokens16.dimension.value16 }, children: options.message }),
2851
2895
  request.kind === "prompt" && /* @__PURE__ */ jsx18(
2852
2896
  KjunInput,
2853
2897
  {
@@ -2864,7 +2908,7 @@ function FeedbackDialog({
2864
2908
  }
2865
2909
  );
2866
2910
  }
2867
- function KjunFeedbackProvider({ children }) {
2911
+ function KjunFeedbackProvider({ children, style }) {
2868
2912
  const [controller] = useState13(createFeedbackController);
2869
2913
  const life = useRef13(0);
2870
2914
  const snapshot = useSyncExternalStore2(
@@ -2894,7 +2938,7 @@ function KjunFeedbackProvider({ children }) {
2894
2938
  {
2895
2939
  value: { top: layers[layers.length - 1], register, render: renderToasts },
2896
2940
  children: /* @__PURE__ */ jsxs10(KjunFeedbackContext.Provider, { value: controller.api, children: [
2897
- /* @__PURE__ */ jsxs10(View14, { style: { position: "relative" }, children: [
2941
+ /* @__PURE__ */ jsxs10(View14, { style: [{ position: "relative", flexGrow: 1, flexShrink: 1, alignSelf: "stretch" }, style], children: [
2898
2942
  children,
2899
2943
  layers.length === 0 && renderToasts()
2900
2944
  ] }),
@@ -2984,7 +3028,7 @@ function KjunCheckbox({
2984
3028
  )
2985
3029
  }
2986
3030
  ),
2987
- (children || label) && content(children || label, {
3031
+ !!(children || label) && content(children || label, {
2988
3032
  marginLeft: tokens17.extensions.checkbox.labelGap,
2989
3033
  ...typeStyle("label")
2990
3034
  })
@@ -3095,7 +3139,7 @@ function KjunSwitch({
3095
3139
  ]
3096
3140
  }
3097
3141
  ),
3098
- (children || label) && content(children || label, {
3142
+ !!(children || label) && content(children || label, {
3099
3143
  marginLeft: tokens17.extensions.switch.labelGap,
3100
3144
  ...typeStyle("label")
3101
3145
  })
@@ -3172,7 +3216,7 @@ function KjunRadio({
3172
3216
  )
3173
3217
  }
3174
3218
  ),
3175
- (children || label) && content(children || label, {
3219
+ !!(children || label) && content(children || label, {
3176
3220
  marginLeft: geometry2.labelGap,
3177
3221
  ...typeStyle("label")
3178
3222
  })
@@ -3420,7 +3464,7 @@ function KjunButtonGroup({
3420
3464
  opacity: blocked ? tokens19.states.opacity.disabled : 1
3421
3465
  }),
3422
3466
  children: [
3423
- option.icon && /* @__PURE__ */ jsx21(
3467
+ !!option.icon && /* @__PURE__ */ jsx21(
3424
3468
  KjunIcon,
3425
3469
  {
3426
3470
  name: option.icon,
@@ -3517,7 +3561,7 @@ function KjunFilterGroup({
3517
3561
  }
3518
3562
  }
3519
3563
  ),
3520
- option.icon && /* @__PURE__ */ jsx21(
3564
+ !!option.icon && /* @__PURE__ */ jsx21(
3521
3565
  KjunIcon,
3522
3566
  {
3523
3567
  name: option.icon,
@@ -3609,13 +3653,13 @@ var KjunTextarea = forwardRef3(
3609
3653
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3610
3654
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3611
3655
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3612
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3656
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3613
3657
  accessibilityState: { disabled },
3614
3658
  ...Platform13.OS === "web" ? {
3615
3659
  "aria-disabled": disabled,
3616
3660
  "aria-invalid": invalid || void 0,
3617
3661
  "aria-describedby": field == null ? void 0 : field.describedBy,
3618
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3662
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3619
3663
  "aria-required": (field == null ? void 0 : field.required) || void 0
3620
3664
  } : {},
3621
3665
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3650,8 +3694,32 @@ var KjunTextarea = forwardRef3(
3650
3694
 
3651
3695
  // packages/native/src/drawer.tsx
3652
3696
  import { tokens as tokens21 } from "@kjun-ui/tokens";
3653
- import { useState as useState16 } from "react";
3654
- import { Animated as Animated12 } from "react-native";
3697
+ import { useEffect as useEffect11, useState as useState16 } from "react";
3698
+ import { Animated as Animated12, Keyboard } from "react-native";
3699
+
3700
+ // shared/package-runtime/drawer-geometry.ts
3701
+ function keyboardEvents(os) {
3702
+ return os === "ios" ? { show: "keyboardWillShow", hide: "keyboardWillHide" } : null;
3703
+ }
3704
+ function keyboardOverlap(windowHeight, keyboardScreenY) {
3705
+ return Math.max(0, windowHeight - keyboardScreenY);
3706
+ }
3707
+ function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap, keyboard, maxHeight }) {
3708
+ const visible = windowHeight - keyboard;
3709
+ const bottomInset = keyboard > 0 ? 0 : safeAreaBottom;
3710
+ if (position === "left" || position === "right")
3711
+ return { maxHeight: visible, height: visible, paddingTop: safeAreaTop, paddingBottom: bottomInset, lift: 0 };
3712
+ const sheet = maxHeight != null ? maxHeight : visible - (position === "bottom" ? safeAreaTop : bottomInset) - gap;
3713
+ return {
3714
+ maxHeight: Math.max(0, Math.min(sheet, visible)),
3715
+ height: void 0,
3716
+ paddingTop: position === "top" ? safeAreaTop : 0,
3717
+ paddingBottom: position === "bottom" ? bottomInset : 0,
3718
+ lift: position === "bottom" ? keyboard : 0
3719
+ };
3720
+ }
3721
+
3722
+ // packages/native/src/drawer.tsx
3655
3723
  import {
3656
3724
  Platform as Platform14,
3657
3725
  ScrollView as ScrollView3,
@@ -3659,6 +3727,24 @@ import {
3659
3727
  useWindowDimensions as useWindowDimensions3
3660
3728
  } from "react-native";
3661
3729
  import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
3730
+ function useKeyboardOverlap(enabled2, windowHeight) {
3731
+ const [overlap, setOverlap] = useState16(0);
3732
+ useEffect11(() => {
3733
+ var _a, _b;
3734
+ setOverlap(0);
3735
+ const events = keyboardEvents(Platform14.OS);
3736
+ if (!enabled2 || !events) return;
3737
+ const metrics = (_b = (_a = Keyboard).metrics) == null ? void 0 : _b.call(_a);
3738
+ if (metrics) setOverlap(keyboardOverlap(windowHeight, metrics.screenY));
3739
+ const show = Keyboard.addListener(events.show, (event) => setOverlap(keyboardOverlap(windowHeight, event.endCoordinates.screenY)));
3740
+ const hide = Keyboard.addListener(events.hide, () => setOverlap(0));
3741
+ return () => {
3742
+ show.remove();
3743
+ hide.remove();
3744
+ };
3745
+ }, [enabled2, windowHeight]);
3746
+ return overlap;
3747
+ }
3662
3748
  function KjunDrawer({
3663
3749
  open,
3664
3750
  onOpenChange,
@@ -3673,9 +3759,24 @@ function KjunDrawer({
3673
3759
  footer,
3674
3760
  children,
3675
3761
  onClose,
3676
- ariaLabel
3762
+ ariaLabel,
3763
+ safeAreaTop = 0,
3764
+ safeAreaBottom = 0,
3765
+ maxHeight,
3766
+ avoidKeyboard = true
3677
3767
  }) {
3678
3768
  const { colors } = useKjunStyles(), window2 = useWindowDimensions3(), vertical = position === "left" || position === "right";
3769
+ const [rootHeight, setRootHeight] = useState16(0);
3770
+ const keyboard = useKeyboardOverlap(open && avoidKeyboard, window2.height);
3771
+ const frame = drawerFrame({
3772
+ position,
3773
+ windowHeight: rootHeight || window2.height,
3774
+ safeAreaTop: Math.max(0, safeAreaTop),
3775
+ safeAreaBottom: Math.max(0, safeAreaBottom),
3776
+ gap: tokens21.extensions.drawer.sheetGap,
3777
+ keyboard,
3778
+ maxHeight
3779
+ });
3679
3780
  const [panelHeight, setPanelHeight] = useState16(0);
3680
3781
  const motion = useNativePresence(open, tokens21.motion.layerEnter, tokens21.motion.layerExit, vertical || panelHeight > 0, 0, { enterEasing: motionEmphasized });
3681
3782
  const backdrop = useNativePresence(open, tokens21.motion.backdrop, tokens21.motion.backdropExit, true, 0, { fade: tokens21.motion.backdropExit });
@@ -3699,6 +3800,7 @@ function KjunDrawer({
3699
3800
  children: /* @__PURE__ */ jsxs14(
3700
3801
  View18,
3701
3802
  {
3803
+ onLayout: (event) => setRootHeight(event.nativeEvent.layout.height),
3702
3804
  style: {
3703
3805
  flex: 1,
3704
3806
  justifyContent: position === "bottom" ? "flex-end" : "flex-start",
@@ -3737,8 +3839,11 @@ function KjunDrawer({
3737
3839
  opacity: vertical || panelHeight > 0 ? 1 : 0,
3738
3840
  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] }) }],
3739
3841
  width: vertical ? Math.min(width, window2.width) : window2.width,
3740
- height: vertical ? window2.height : void 0,
3741
- maxHeight: window2.height,
3842
+ height: frame.height,
3843
+ maxHeight: frame.maxHeight,
3844
+ paddingTop: frame.paddingTop,
3845
+ paddingBottom: frame.paddingBottom,
3846
+ marginBottom: frame.lift,
3742
3847
  borderRadius: tokens21.extensions.drawer.radius,
3743
3848
  borderTopLeftRadius: position === "bottom" ? tokens21.extensions.drawer.bottomRadius : tokens21.extensions.drawer.radius,
3744
3849
  borderTopRightRadius: position === "bottom" ? tokens21.extensions.drawer.bottomRadius : tokens21.extensions.drawer.radius,
@@ -3746,7 +3851,7 @@ function KjunDrawer({
3746
3851
  backgroundColor: colors.surface
3747
3852
  },
3748
3853
  children: [
3749
- (title || header || closable) && /* @__PURE__ */ jsxs14(
3854
+ !!(title || header || closable) && /* @__PURE__ */ jsxs14(
3750
3855
  View18,
3751
3856
  {
3752
3857
  style: {
@@ -3759,7 +3864,7 @@ function KjunDrawer({
3759
3864
  borderBottomColor: colors.border
3760
3865
  },
3761
3866
  children: [
3762
- /* @__PURE__ */ jsx23(View18, { style: { flex: 1 }, children: header || /* @__PURE__ */ jsx23(
3867
+ /* @__PURE__ */ jsx23(View18, { style: { flex: 1 }, children: hasContent(header) ? content(header) : /* @__PURE__ */ jsx23(
3763
3868
  KText,
3764
3869
  {
3765
3870
  style: { ...typeStyle("sectionTitle") },
@@ -3784,12 +3889,12 @@ function KjunDrawer({
3784
3889
  ScrollView3,
3785
3890
  {
3786
3891
  keyboardShouldPersistTaps: "handled",
3787
- style: vertical ? { flex: 1 } : { flexGrow: 0 },
3892
+ style: vertical ? { flex: 1 } : { flexGrow: 0, flexShrink: 1 },
3788
3893
  contentContainerStyle: { padding: noPadding ? 0 : tokens21.extensions.drawer.bodyPadding },
3789
3894
  children: content(children)
3790
3895
  }
3791
3896
  ),
3792
- footer && /* @__PURE__ */ jsx23(
3897
+ hasContent(footer) && /* @__PURE__ */ jsx23(
3793
3898
  View18,
3794
3899
  {
3795
3900
  style: {
@@ -3819,7 +3924,7 @@ import {
3819
3924
  createContext as createContext15,
3820
3925
  isValidElement as isValidElement5,
3821
3926
  useContext as useContext11,
3822
- useEffect as useEffect13,
3927
+ useEffect as useEffect14,
3823
3928
  useRef as useRef19,
3824
3929
  useState as useState19
3825
3930
  } from "react";
@@ -3833,7 +3938,7 @@ import { tokens as tokens22 } from "@kjun-ui/tokens";
3833
3938
  import {
3834
3939
  cloneElement as cloneElement2,
3835
3940
  isValidElement as isValidElement3,
3836
- useEffect as useEffect11,
3941
+ useEffect as useEffect12,
3837
3942
  useLayoutEffect as useLayoutEffect11,
3838
3943
  useRef as useRef17,
3839
3944
  useState as useState17
@@ -3885,8 +3990,11 @@ function FloatingPanel({
3885
3990
  flip = true,
3886
3991
  minimumWidth = 0,
3887
3992
  focusOnOpen = false,
3888
- reveal
3993
+ reveal,
3994
+ header,
3995
+ nonModal = false
3889
3996
  }) {
3997
+ const [headerHeight, setHeaderHeight] = useState17(0);
3890
3998
  const panelRef = useRef17(null);
3891
3999
  const scroll = useRef17(null), viewport = useRef17(0);
3892
4000
  const { colors } = useKjunStyles(), window2 = useWindowDimensions4(), [anchored, setAnchored] = useState17(false), [sized, setSized] = useState17(false), [anchor, setAnchor] = useState17({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = useState17({ width: 0, height: 0 });
@@ -3912,14 +4020,14 @@ function FloatingPanel({
3912
4020
  setAnchored(false);
3913
4021
  }
3914
4022
  }, [motion.present]);
3915
- useEffect11(() => {
4023
+ useEffect12(() => {
3916
4024
  var _a;
3917
4025
  const row = reveal == null ? void 0 : reveal.current;
3918
4026
  if (!open || !anchored || !sized || !row || !viewport.current) return;
3919
4027
  const offset = row.y + row.height + (menu || field ? tokens22.extensions.menu.padding : 0) - viewport.current;
3920
4028
  if (offset > 0) (_a = scroll.current) == null ? void 0 : _a.scrollTo({ y: offset, animated: false });
3921
4029
  }, [open, anchored, sized, reveal, menu, field]);
3922
- useEffect11(() => {
4030
+ useEffect12(() => {
3923
4031
  if (!open || !anchored || !sized || !focusOnOpen || !panelRef.current) return;
3924
4032
  if (Platform15.OS === "web") {
3925
4033
  const node = panelRef.current;
@@ -3941,7 +4049,7 @@ function FloatingPanel({
3941
4049
  maximumWidth,
3942
4050
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : tokens22.extensions.popover.width)
3943
4051
  );
3944
- const available = Math.min(maxHeight != null ? maxHeight : field ? tokens22.extensions.menu.listMaxHeight : tokens22.extensions.floating.maxHeight, window2.height - 2 * tokens22.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - tokens22.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens22.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
4052
+ const available = Math.min(maxHeight != null ? maxHeight : field ? tokens22.extensions.menu.listMaxHeight : tokens22.extensions.floating.maxHeight, window2.height - 2 * tokens22.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - tokens22.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens22.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3945
4053
  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;
3946
4054
  if (placement === "left" || placement === "right") {
3947
4055
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -4025,7 +4133,7 @@ function FloatingPanel({
4025
4133
  }
4026
4134
  }
4027
4135
  ),
4028
- /* @__PURE__ */ jsx24(
4136
+ /* @__PURE__ */ jsxs15(
4029
4137
  Animated13.View,
4030
4138
  {
4031
4139
  ref: panelRef,
@@ -4036,7 +4144,7 @@ function FloatingPanel({
4036
4144
  pointerEvents: open && anchored && sized ? "auto" : "none",
4037
4145
  accessibilityElementsHidden: !open || !anchored || !sized,
4038
4146
  ...!open || !anchored || !sized ? { inert: true } : {},
4039
- accessibilityViewIsModal: true,
4147
+ accessibilityViewIsModal: !nonModal,
4040
4148
  accessibilityLabel: ariaLabel,
4041
4149
  style: {
4042
4150
  opacity: anchored && sized ? motion.opacity : 0,
@@ -4058,26 +4166,42 @@ function FloatingPanel({
4058
4166
  boxShadow: (tooltip ? tokens22.extensions.tooltip.elevation : popover ? tokens22.extensions.popover.elevation : tokens22.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
4059
4167
  overflow: "hidden"
4060
4168
  },
4061
- children: /* @__PURE__ */ jsx24(
4062
- ScrollView4,
4063
- {
4064
- ref: scroll,
4065
- onLayout: (event) => {
4066
- viewport.current = event.nativeEvent.layout.height;
4067
- },
4068
- keyboardShouldPersistTaps: "handled",
4069
- style: { maxHeight: available },
4070
- contentContainerStyle: { padding: menu || field ? tokens22.extensions.menu.padding : 0 },
4071
- onContentSizeChange: (width2, height2) => {
4072
- if (contentSizedMenu) return;
4073
- setSized(true);
4074
- setPanel(
4075
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4076
- );
4077
- },
4078
- children
4079
- }
4080
- )
4169
+ children: [
4170
+ header != null && /* @__PURE__ */ jsx24(
4171
+ View19,
4172
+ {
4173
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
4174
+ style: {
4175
+ paddingTop: tokens22.extensions.popover.padding,
4176
+ paddingHorizontal: tokens22.extensions.popover.padding,
4177
+ paddingBottom: tokens22.extensions.popover.headerPaddingBottom,
4178
+ borderBottomWidth: tokens22.border.defaultWidth,
4179
+ borderBottomColor: colors.divider
4180
+ },
4181
+ children: content(header, typeStyle("cardTitle"))
4182
+ }
4183
+ ),
4184
+ /* @__PURE__ */ jsx24(
4185
+ ScrollView4,
4186
+ {
4187
+ ref: scroll,
4188
+ onLayout: (event) => {
4189
+ viewport.current = event.nativeEvent.layout.height;
4190
+ },
4191
+ keyboardShouldPersistTaps: "handled",
4192
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
4193
+ contentContainerStyle: { padding: menu || field ? tokens22.extensions.menu.padding : 0 },
4194
+ onContentSizeChange: (width2, height2) => {
4195
+ if (contentSizedMenu) return;
4196
+ setSized(true);
4197
+ setPanel(
4198
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4199
+ );
4200
+ },
4201
+ children
4202
+ }
4203
+ )
4204
+ ]
4081
4205
  }
4082
4206
  )
4083
4207
  ]
@@ -4091,7 +4215,7 @@ function FloatingPanel({
4091
4215
  import {
4092
4216
  cloneElement as cloneElement3,
4093
4217
  isValidElement as isValidElement4,
4094
- useEffect as useEffect12,
4218
+ useEffect as useEffect13,
4095
4219
  useRef as useRef18,
4096
4220
  useState as useState18
4097
4221
  } from "react";
@@ -4112,9 +4236,12 @@ function KjunPopover({
4112
4236
  focusOnOpen = false,
4113
4237
  ariaLabel,
4114
4238
  placement = "bottom",
4239
+ header,
4240
+ modal = true,
4115
4241
  trigger,
4116
4242
  children
4117
4243
  }) {
4244
+ const { colors } = useKjunStyles();
4118
4245
  const [internal, setInternal] = useState18(false), open = controlled != null ? controlled : internal, ref = useRef18(null);
4119
4246
  const change = (v) => {
4120
4247
  setInternal(v);
@@ -4143,6 +4270,8 @@ function KjunPopover({
4143
4270
  popover: true,
4144
4271
  focusOnOpen,
4145
4272
  ariaLabel,
4273
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4274
+ nonModal: !modal,
4146
4275
  children: /* @__PURE__ */ jsx25(View20, { style: { padding: noPadding ? 0 : tokens23.extensions.popover.padding, gap: noPadding ? 0 : tokens23.extensions.popover.gap }, children: content(
4147
4276
  typeof children === "function" ? children(() => change(false)) : children,
4148
4277
  Platform16.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -4157,9 +4286,18 @@ function KjunTooltip({
4157
4286
  delay = 0,
4158
4287
  children,
4159
4288
  onPress,
4160
- disabled
4289
+ disabled,
4290
+ open: controlled,
4291
+ onOpenChange
4161
4292
  }) {
4162
- const { colors } = useKjunStyles(), [open, setOpen] = useState18(false), ref = useRef18(null), timer = useRef18(void 0);
4293
+ const { colors } = useKjunStyles(), [internal, setInternal] = useState18(false), open = controlled != null ? controlled : internal, latest = useRef18({ open, onOpenChange }), setOpen = (value) => {
4294
+ var _a, _b;
4295
+ if (latest.current.open === value) return;
4296
+ latest.current.open = value;
4297
+ setInternal(value);
4298
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4299
+ }, ref = useRef18(null), timer = useRef18(void 0);
4300
+ latest.current = { open, onOpenChange };
4163
4301
  const scopeActive = useActiveLayerScope();
4164
4302
  const hide = () => {
4165
4303
  clearTimeout(timer.current);
@@ -4170,11 +4308,11 @@ function KjunTooltip({
4170
4308
  clearTimeout(timer.current);
4171
4309
  timer.current = setTimeout(() => setOpen(true), delay);
4172
4310
  };
4173
- useEffect12(() => {
4311
+ useEffect13(() => {
4174
4312
  if (!scopeActive) hide();
4175
4313
  }, [scopeActive]);
4176
- useEffect12(() => () => clearTimeout(timer.current), []);
4177
- useEffect12(() => {
4314
+ useEffect13(() => () => clearTimeout(timer.current), []);
4315
+ useEffect13(() => {
4178
4316
  if (Platform16.OS !== "web" || !open) return;
4179
4317
  const escape = (event) => {
4180
4318
  if (event.key === "Escape") hide();
@@ -4290,7 +4428,7 @@ function KjunDropdown({
4290
4428
  if (v) onOpen == null ? void 0 : onOpen();
4291
4429
  else onClose == null ? void 0 : onClose();
4292
4430
  };
4293
- useEffect13(() => {
4431
+ useEffect14(() => {
4294
4432
  if (unavailable) change(false);
4295
4433
  }, [unavailable]);
4296
4434
  return /* @__PURE__ */ jsxs17(DropdownContext.Provider, { value: () => change(false), children: [
@@ -4425,7 +4563,7 @@ function KjunMenuButton({
4425
4563
  }
4426
4564
 
4427
4565
  // shared/package-runtime/combobox.ts
4428
- import { useEffect as useEffect14, useState as useState20 } from "react";
4566
+ import { useEffect as useEffect15, useState as useState20 } from "react";
4429
4567
 
4430
4568
  // shared/package-runtime/options.ts
4431
4569
  var optionValue = (option, key = "value") => typeof option === "object" ? option[key] : option;
@@ -4504,8 +4642,8 @@ function useComboboxModel({
4504
4642
  const selectedLabel = selected !== void 0 ? optionLabel(selected, labelKey) : "";
4505
4643
  const [query, setQuery] = useState20(selectedLabel);
4506
4644
  const [open, setOpen] = useState20(false);
4507
- useEffect14(() => setQuery(selectedLabel), [value, selectedLabel]);
4508
- useEffect14(() => {
4645
+ useEffect15(() => setQuery(selectedLabel), [value, selectedLabel]);
4646
+ useEffect15(() => {
4509
4647
  if (disabled) setOpen(false);
4510
4648
  }, [disabled]);
4511
4649
  const change = (next) => {
@@ -4962,7 +5100,7 @@ function KjunDatePicker({
4962
5100
  import { useState as useState23 } from "react";
4963
5101
 
4964
5102
  // shared/package-runtime/search-input.ts
4965
- import { useEffect as useEffect15, useLayoutEffect as useLayoutEffect12, useRef as useRef22, useState as useState22 } from "react";
5103
+ import { useEffect as useEffect16, useLayoutEffect as useLayoutEffect12, useRef as useRef22, useState as useState22 } from "react";
4966
5104
  function useSearchInputModel({
4967
5105
  value,
4968
5106
  disabled = false,
@@ -4985,7 +5123,7 @@ function useSearchInputModel({
4985
5123
  clearTimeout(timer.current);
4986
5124
  return () => clearTimeout(timer.current);
4987
5125
  }, [disabled, debounce, !!loadOptions]);
4988
- useEffect15(() => {
5126
+ useEffect16(() => {
4989
5127
  const current = ++version.current, abort = new AbortController();
4990
5128
  setFailed(false);
4991
5129
  if (!loadOptions || !open || disabled || query.length < minChars) {
@@ -5134,7 +5272,7 @@ function KjunSearchInput({
5134
5272
 
5135
5273
  // packages/native/src/select-control.tsx
5136
5274
  import { tokens as tokens29 } from "@kjun-ui/tokens";
5137
- import { useContext as useContext12, useEffect as useEffect17, useRef as useRef24 } from "react";
5275
+ import { useContext as useContext12, useEffect as useEffect18, useRef as useRef24 } from "react";
5138
5276
 
5139
5277
  // packages/native/src/compound-control.tsx
5140
5278
  import { createContext as createContext16 } from "react";
@@ -5148,7 +5286,7 @@ function CompoundTarget({ children, square = false }) {
5148
5286
  }
5149
5287
 
5150
5288
  // shared/package-runtime/select.ts
5151
- import { useEffect as useEffect16, useRef as useRef23, useState as useState24 } from "react";
5289
+ import { useEffect as useEffect17, useRef as useRef23, useState as useState24 } from "react";
5152
5290
  function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5153
5291
  const [internal, setInternal] = useState24(false);
5154
5292
  const [query, setQuery] = useState24("");
@@ -5157,7 +5295,7 @@ function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5157
5295
  const isControlled = typeof controlled === "boolean";
5158
5296
  const open = !disabled && (isControlled ? controlled : internal);
5159
5297
  const previousOpen = useRef23(open);
5160
- useEffect16(() => {
5298
+ useEffect17(() => {
5161
5299
  if (disabled && !isControlled) setInternal(false);
5162
5300
  if (previousOpen.current && !open) {
5163
5301
  setQuery("");
@@ -5166,7 +5304,7 @@ function useSelectState(controlled, disabled, pageSize, onOpenChange) {
5166
5304
  }
5167
5305
  previousOpen.current = open;
5168
5306
  }, [open, disabled, isControlled, pageSize]);
5169
- useEffect16(() => {
5307
+ useEffect17(() => {
5170
5308
  setLimit(pageSize);
5171
5309
  }, [pageSize]);
5172
5310
  const changeOpen = (next) => {
@@ -5216,7 +5354,7 @@ function KjunSelect({
5216
5354
  const { colors } = useKjunStyles(), field = useKjunField(), ref = useRef24(null), spec = tokens29.input[size];
5217
5355
  const trigger = useRef24(null);
5218
5356
  const compound = useContext12(CompoundControlContext);
5219
- useEffect17(() => {
5357
+ useEffect18(() => {
5220
5358
  if (compound == null ? void 0 : compound.focusRef) compound.focusRef.current = trigger.current;
5221
5359
  }, [compound == null ? void 0 : compound.focusRef]);
5222
5360
  const expanded = usePopupExpanded(open, ref);
@@ -5467,10 +5605,10 @@ function Collapse({ open, children }) {
5467
5605
  }
5468
5606
 
5469
5607
  // shared/package-runtime/navigation.ts
5470
- import { useEffect as useEffect18, useRef as useRef26, useState as useState26 } from "react";
5608
+ import { useEffect as useEffect19, useRef as useRef26, useState as useState26 } from "react";
5471
5609
  function useInitialTab(value, candidate, change) {
5472
5610
  const requested = useRef26(/* @__PURE__ */ new Set());
5473
- useEffect18(() => {
5611
+ useEffect19(() => {
5474
5612
  if (value !== "") {
5475
5613
  requested.current.clear();
5476
5614
  return;
@@ -5482,7 +5620,7 @@ function useInitialTab(value, candidate, change) {
5482
5620
  }
5483
5621
  function useAccordionState(multiple) {
5484
5622
  const [opened, setOpened] = useState26(/* @__PURE__ */ new Set());
5485
- useEffect18(() => {
5623
+ useEffect19(() => {
5486
5624
  if (!multiple) setOpened((previous) => previous.size > 1 ? new Set([...previous].slice(-1)) : previous);
5487
5625
  }, [multiple]);
5488
5626
  return {
@@ -5510,7 +5648,7 @@ import {
5510
5648
  Fragment as Fragment11,
5511
5649
  createContext as createContext18,
5512
5650
  useContext as useContext14,
5513
- useEffect as useEffect19,
5651
+ useEffect as useEffect20,
5514
5652
  useId as useId7,
5515
5653
  useState as useState28
5516
5654
  } from "react";
@@ -5777,7 +5915,7 @@ function KjunTabs({
5777
5915
  opacity: tab.disabled ? tokens31.states.opacity.disabled : 1
5778
5916
  },
5779
5917
  children: [
5780
- tab.icon && /* @__PURE__ */ jsx34(
5918
+ !!tab.icon && /* @__PURE__ */ jsx34(
5781
5919
  KjunIcon,
5782
5920
  {
5783
5921
  name: tab.icon,
@@ -5822,11 +5960,11 @@ function KjunTabs({
5822
5960
  tab.name
5823
5961
  );
5824
5962
  }),
5825
- actions
5963
+ content(actions)
5826
5964
  ]
5827
5965
  }
5828
5966
  ),
5829
- children && /* @__PURE__ */ jsx34(View28, { style: { marginTop: geometry2.contentGap }, children })
5967
+ hasContent(children) && /* @__PURE__ */ jsx34(View28, { style: { marginTop: geometry2.contentGap }, children: content(children) })
5830
5968
  ] }) });
5831
5969
  }
5832
5970
  function KjunTabPane({ name, children }) {
@@ -5891,7 +6029,7 @@ function KjunAccordionItem({
5891
6029
  const geometry2 = tokens32.extensions.accordion;
5892
6030
  const [hovered, setHovered] = useState28(false);
5893
6031
  const wrapping = Platform21.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : {};
5894
- useEffect19(() => {
6032
+ useEffect20(() => {
5895
6033
  return group == null ? void 0 : group.register(id, defaultOpen);
5896
6034
  }, []);
5897
6035
  return /* @__PURE__ */ jsxs23(View29, { children: [
@@ -5954,7 +6092,7 @@ function KjunBreadcrumb({
5954
6092
  },
5955
6093
  children: items.map((item, i) => {
5956
6094
  const label = /* @__PURE__ */ jsxs23(View29, { style: { flexDirection: "row", alignItems: "center", gap: tokens32.dimension.value4 }, children: [
5957
- item.icon && /* @__PURE__ */ jsx35(KjunIcon, { name: item.icon, size: tokens32.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
6095
+ !!item.icon && /* @__PURE__ */ jsx35(KjunIcon, { name: item.icon, size: tokens32.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
5958
6096
  /* @__PURE__ */ jsx35(
5959
6097
  KText,
5960
6098
  {
@@ -6127,7 +6265,7 @@ function KjunPagination({
6127
6265
 
6128
6266
  // packages/native/src/actions.tsx
6129
6267
  import { tokens as tokens33 } from "@kjun-ui/tokens";
6130
- import { useContext as useContext15, useEffect as useEffect20, useRef as useRef29, useState as useState29 } from "react";
6268
+ import { useContext as useContext15, useEffect as useEffect21, useRef as useRef29, useState as useState29 } from "react";
6131
6269
  import { Platform as Platform22, ScrollView as ScrollView7, View as View30 } from "react-native";
6132
6270
  import { jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
6133
6271
  function KjunIconToggle({
@@ -6211,7 +6349,7 @@ function KjunCopyButton({
6211
6349
  onCopyError
6212
6350
  }) {
6213
6351
  const feedback = useOptionalKjunFeedback(), { colors } = useKjunStyles(), [copied, setCopied] = useState29(false), [failed, setFailed] = useState29(false), busy = useRef29(false), timer = useRef29(void 0), mounted = useRef29(true);
6214
- useEffect20(() => {
6352
+ useEffect21(() => {
6215
6353
  mounted.current = true;
6216
6354
  return () => {
6217
6355
  mounted.current = false;
@@ -6270,7 +6408,7 @@ function KjunCopyButton({
6270
6408
  color: copied ? colors.success : colors.textSecondary
6271
6409
  }
6272
6410
  ),
6273
- text && !inline && /* @__PURE__ */ jsx36(
6411
+ !!text && !inline && /* @__PURE__ */ jsx36(
6274
6412
  KText,
6275
6413
  {
6276
6414
  style: {
@@ -6372,10 +6510,10 @@ import { Component, useState as useState31 } from "react";
6372
6510
  import { Platform as Platform23, View as View31 } from "react-native";
6373
6511
 
6374
6512
  // shared/package-runtime/use-loading-reveal.ts
6375
- import { useEffect as useEffect21, useState as useState30 } from "react";
6513
+ import { useEffect as useEffect22, useState as useState30 } from "react";
6376
6514
  function useLoadingReveal(active, delay = 0) {
6377
6515
  const [revealed, setRevealed] = useState30(false);
6378
- useEffect21(() => {
6516
+ useEffect22(() => {
6379
6517
  setRevealed(false);
6380
6518
  if (!active || delay <= 0) return;
6381
6519
  const timer = setTimeout(() => setRevealed(true), delay);
@@ -6463,7 +6601,7 @@ function KjunDataState({
6463
6601
  /* @__PURE__ */ jsx37(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6464
6602
  ] }))
6465
6603
  }
6466
- ) : blockingError ? /* @__PURE__ */ jsx37(DataStateContentContext.Provider, { value: "message", children: /* @__PURE__ */ jsx37(View31, { 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(View31, { style: { paddingVertical: padding }, children: emptyContent || /* @__PURE__ */ jsx37(KjunEmpty, { text: emptyText, icon: emptyIcon, children: emptyActionText && /* @__PURE__ */ jsx37(KjunButton, { onPress: onEmptyAction, children: emptyActionText }) }) }) }) : null,
6604
+ ) : blockingError ? /* @__PURE__ */ jsx37(DataStateContentContext.Provider, { value: "message", children: /* @__PURE__ */ jsx37(View31, { 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(View31, { style: { paddingVertical: padding }, children: emptyContent || /* @__PURE__ */ jsx37(KjunEmpty, { text: emptyText, icon: emptyIcon, children: !!emptyActionText && /* @__PURE__ */ jsx37(KjunButton, { onPress: onEmptyAction, children: emptyActionText }) }) }) }) : null,
6467
6605
  (!concealed || preserveContent && state.hasQueryResult) && /* @__PURE__ */ jsx37(
6468
6606
  View31,
6469
6607
  {
@@ -6538,7 +6676,7 @@ var KjunErrorBoundary = class extends Component {
6538
6676
 
6539
6677
  // packages/native/src/skeletons.tsx
6540
6678
  import { tokens as tokens38 } from "@kjun-ui/tokens";
6541
- import { useEffect as useEffect22, useRef as useRef30, useState as useState33 } from "react";
6679
+ import { useEffect as useEffect23, useRef as useRef30, useState as useState33 } from "react";
6542
6680
  import { Animated as Animated17, Platform as Platform24, View as View33, useWindowDimensions as useWindowDimensions6 } from "react-native";
6543
6681
  import Svg5, { Path as Path4 } from "react-native-svg";
6544
6682
 
@@ -6728,7 +6866,7 @@ function KjunChartSkeleton({
6728
6866
  columns = 14
6729
6867
  }) {
6730
6868
  const kind = chartSkeletonKind(requestedKind), { colors } = useKjunStyles(), reduced = useReducedMotion(), opacity = useRef30(new Animated17.Value(1)).current, revealed = useLoadingReveal(true, loadingDelay), [measured, setMeasured] = useState33(0), thickness = tokens38.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" };
6731
- useEffect22(() => {
6869
+ useEffect23(() => {
6732
6870
  if (reduced || !skeleton) return;
6733
6871
  const loop = Animated17.loop(
6734
6872
  Animated17.sequence([
@@ -6948,12 +7086,12 @@ function KjunAnimatedNumber({
6948
7086
  style
6949
7087
  ],
6950
7088
  children: [
6951
- prefix && /* @__PURE__ */ jsxs28(Text5, { style: typeStyle("caption"), children: [
7089
+ !!prefix && /* @__PURE__ */ jsxs28(Text5, { style: typeStyle("caption"), children: [
6952
7090
  prefix,
6953
7091
  " "
6954
7092
  ] }),
6955
7093
  typeof n === "string" ? n : formatter ? formatter(n) : format(n, decimals),
6956
- suffix && /* @__PURE__ */ jsxs28(Text5, { style: typeStyle("caption"), children: [
7094
+ !!suffix && /* @__PURE__ */ jsxs28(Text5, { style: typeStyle("caption"), children: [
6957
7095
  " ",
6958
7096
  suffix
6959
7097
  ] })
@@ -7359,7 +7497,7 @@ function KjunSparkline({
7359
7497
 
7360
7498
  // shared/package-runtime/table.ts
7361
7499
  import { nextTableSort } from "@kjun-ui/tokens";
7362
- import { useEffect as useEffect24, useRef as useRef33, useState as useState36 } from "react";
7500
+ import { useEffect as useEffect25, useRef as useRef33, useState as useState36 } from "react";
7363
7501
 
7364
7502
  // shared/package-runtime/table-model.ts
7365
7503
  function getValue(row, key) {
@@ -7407,7 +7545,7 @@ function useTableModel(props) {
7407
7545
  key: "",
7408
7546
  order: "asc"
7409
7547
  }), [internalExpanded, setExpanded] = useState36([]), [search, setSearch] = useState36(""), timer = useRef33(void 0);
7410
- useEffect24(() => () => clearTimeout(timer.current), []);
7548
+ useEffect25(() => () => clearTimeout(timer.current), []);
7411
7549
  const sort = props.sort === void 0 ? internalSort : (_a = props.sort) != null ? _a : { key: "", order: "asc" };
7412
7550
  const rows = sortTableRows(data, sort, { sortable, sortMode });
7413
7551
  const key = (row, index = 0) => tableRowKey(row, rowKey, index);
@@ -7527,7 +7665,7 @@ function TableToolbar({
7527
7665
  selectable && selected.length > 0 && /* @__PURE__ */ jsxs30(
7528
7666
  View36,
7529
7667
  {
7530
- accessibilityRole: "toolbar",
7668
+ role: "group",
7531
7669
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7532
7670
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7533
7671
  children: [
@@ -7841,7 +7979,7 @@ function TableCards({
7841
7979
  },
7842
7980
  children: [
7843
7981
  selectable && checkbox(row, i),
7844
- title && /* @__PURE__ */ jsx44(View38, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7982
+ !!title && /* @__PURE__ */ jsx44(View38, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7845
7983
  ]
7846
7984
  }
7847
7985
  ),
@@ -7862,7 +8000,7 @@ function TableCards({
7862
8000
  marginTop: index ? tokens44.dimension.value12 : 0
7863
8001
  },
7864
8002
  children: [
7865
- section.label && /* @__PURE__ */ jsx44(
8003
+ !!section.label && /* @__PURE__ */ jsx44(
7866
8004
  KText,
7867
8005
  {
7868
8006
  style: {
@@ -7927,7 +8065,7 @@ function TableCards({
7927
8065
  children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7928
8066
  }
7929
8067
  ),
7930
- actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
8068
+ !!actions && !actions.inlineInCard && /* @__PURE__ */ jsx44(
7931
8069
  View38,
7932
8070
  {
7933
8071
  style: {
@@ -8219,7 +8357,7 @@ function KjunKpiHero(props) {
8219
8357
  children: loading ? /* @__PURE__ */ jsx47(KjunSkeleton, { type: "block", height: typeStyle("numberMd").lineHeight, width: geometry2.deltaSkeletonWidth }) : /* @__PURE__ */ jsxs35(Fragment16, { children: [
8220
8358
  deltaAbsolute !== null && delta(deltaAbsolute, "absolute", { ...numeric, color: direction(deltaAbsolute), ...typeStyle("numberMd") }),
8221
8359
  deltaPercent !== null && delta(deltaPercent, "percent", { ...numeric, color: direction(deltaPercent), ...typeStyle("numberMd"), opacity: 0.9 }),
8222
- deltaDescription && /* @__PURE__ */ jsx47(
8360
+ !!deltaDescription && /* @__PURE__ */ jsx47(
8223
8361
  KText,
8224
8362
  {
8225
8363
  style: {
@@ -8233,7 +8371,7 @@ function KjunKpiHero(props) {
8233
8371
  }
8234
8372
  )
8235
8373
  ] }),
8236
- aside && !mobile && /* @__PURE__ */ jsx47(View41, { children: aside })
8374
+ hasContent(aside) && !mobile && /* @__PURE__ */ jsx47(View41, { children: content(aside) })
8237
8375
  ]
8238
8376
  }
8239
8377
  ),
@@ -8295,7 +8433,7 @@ function KjunKpiHero(props) {
8295
8433
  children: kpiFormat(item)
8296
8434
  }
8297
8435
  ),
8298
- item.desc && (loading ? /* @__PURE__ */ jsx47(
8436
+ !!item.desc && (loading ? /* @__PURE__ */ jsx47(
8299
8437
  KjunSkeleton,
8300
8438
  {
8301
8439
  type: "block",
@@ -8506,7 +8644,7 @@ function KjunKpiRow({
8506
8644
  {
8507
8645
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }],
8508
8646
  children: [
8509
- item.prefix && /* @__PURE__ */ jsx48(
8647
+ !!item.prefix && /* @__PURE__ */ jsx48(
8510
8648
  Text6,
8511
8649
  {
8512
8650
  style: {
@@ -8524,7 +8662,7 @@ function KjunKpiRow({
8524
8662
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }]
8525
8663
  }
8526
8664
  ) : kpiFormat(item),
8527
- item.suffix && /* @__PURE__ */ jsx48(
8665
+ !!item.suffix && /* @__PURE__ */ jsx48(
8528
8666
  Text6,
8529
8667
  {
8530
8668
  style: {
@@ -8540,7 +8678,7 @@ function KjunKpiRow({
8540
8678
  ]
8541
8679
  }
8542
8680
  ),
8543
- item.desc && (busy ? /* @__PURE__ */ jsx48(KjunSkeleton, { type: "block", height: typeStyle("caption").lineHeight, width: geometry2.descriptionSkeletonWidth }) : /* @__PURE__ */ jsx48(
8681
+ !!item.desc && (busy ? /* @__PURE__ */ jsx48(KjunSkeleton, { type: "block", height: typeStyle("caption").lineHeight, width: geometry2.descriptionSkeletonWidth }) : /* @__PURE__ */ jsx48(
8544
8682
  KText,
8545
8683
  {
8546
8684
  style: {
@@ -8565,7 +8703,7 @@ import { tokens as tokens49 } from "@kjun-ui/tokens";
8565
8703
  import { ScrollView as ScrollView10, View as View44 } from "react-native";
8566
8704
 
8567
8705
  // shared/package-runtime/market-cards.ts
8568
- import { useEffect as useEffect25, useRef as useRef34, useState as useState39 } from "react";
8706
+ import { useEffect as useEffect26, useRef as useRef34, useState as useState39 } from "react";
8569
8707
  function useMarketCardsModel(props, persistence) {
8570
8708
  const {
8571
8709
  columns,
@@ -8590,7 +8728,7 @@ function useMarketCardsModel(props, persistence) {
8590
8728
  } catch (e) {
8591
8729
  }
8592
8730
  };
8593
- useEffect25(() => {
8731
+ useEffect26(() => {
8594
8732
  var _a, _b;
8595
8733
  if (mounted.current === restoreSignature) return;
8596
8734
  mounted.current = restoreSignature;
@@ -9386,26 +9524,26 @@ var LastListRow = createContext20(false);
9386
9524
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
9387
9525
  const { colors } = useKjunStyles(), last = useContext17(LastListRow);
9388
9526
  const parts = /* @__PURE__ */ jsxs42(Fragment19, { children: [
9389
- leading && /* @__PURE__ */ jsx54(View48, { children: content(leading) }),
9527
+ hasContent(leading) && /* @__PURE__ */ jsx54(View48, { children: content(leading) }),
9390
9528
  /* @__PURE__ */ jsxs42(View48, { style: { flex: 1, minWidth: 0 }, children: [
9391
9529
  /* @__PURE__ */ jsx54(KText, { style: { ...typeStyle("label") }, children: title }),
9392
- description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens53.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9530
+ !!description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens53.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9393
9531
  ] }),
9394
- trailing && /* @__PURE__ */ jsx54(View48, { style: { maxWidth: "35%" }, children: content(trailing) })
9532
+ hasContent(trailing) && /* @__PURE__ */ jsx54(View48, { style: { maxWidth: "35%" }, children: content(trailing) })
9395
9533
  ] });
9396
9534
  const style = { flex: 1, minWidth: 0, minHeight: tokens53.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: tokens53.extensions.list.gap, padding: tokens53.extensions.list.padding };
9397
9535
  return /* @__PURE__ */ jsxs42(View48, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: tokens53.extensions.list.gap, borderBottomWidth: last ? 0 : tokens53.border.defaultWidth, borderBottomColor: colors.divider }, children: [
9398
9536
  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 ? tokens53.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ jsx54(View48, { style, children: parts }),
9399
- actions && /* @__PURE__ */ jsx54(View48, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform25.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens53.extensions.list.actionsGap, paddingRight: tokens53.extensions.list.actionsPadding, opacity: disabled ? tokens53.states.opacity.disabled : 1 }, children: content(actions) })
9537
+ hasContent(actions) && /* @__PURE__ */ jsx54(View48, { pointerEvents: disabled ? "none" : "auto", ...disabled && Platform25.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: tokens53.extensions.list.actionsGap, paddingRight: tokens53.extensions.list.actionsPadding, opacity: disabled ? tokens53.states.opacity.disabled : 1 }, children: content(actions) })
9400
9538
  ] });
9401
9539
  }
9402
9540
  function KjunListSection({ title, description, actions, children, ariaLabel }) {
9403
9541
  const { colors } = useKjunStyles();
9404
9542
  return /* @__PURE__ */ jsxs42(View48, { style: { minWidth: 0, backgroundColor: colors.surface }, children: [
9405
- (title || description || actions) && /* @__PURE__ */ jsxs42(View48, { style: { flexDirection: "row", alignItems: "center", gap: tokens53.extensions.list.gap, padding: tokens53.extensions.list.padding }, children: [
9543
+ !!(title || description || actions) && /* @__PURE__ */ jsxs42(View48, { style: { flexDirection: "row", alignItems: "center", gap: tokens53.extensions.list.gap, padding: tokens53.extensions.list.padding }, children: [
9406
9544
  /* @__PURE__ */ jsxs42(View48, { style: { flex: 1 }, children: [
9407
- title && /* @__PURE__ */ jsx54(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9408
- description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens53.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9545
+ !!title && /* @__PURE__ */ jsx54(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9546
+ !!description && /* @__PURE__ */ jsx54(KText, { style: { marginTop: tokens53.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9409
9547
  ] }),
9410
9548
  content(actions)
9411
9549
  ] }),
@@ -9433,12 +9571,12 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9433
9571
  const flatten = (node) => Children5.toArray(node).flatMap((child) => isValidElement7(child) && child.type === Fragment20 ? flatten(child.props.children) : [child]);
9434
9572
  const actionList = flatten(actions);
9435
9573
  return /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: geometry2, children: /* @__PURE__ */ jsx55(View49, { style: { flexShrink: 0, paddingTop: Math.max(0, safeAreaTop), backgroundColor: colors.surface }, children: /* @__PURE__ */ jsxs43(View49, { style: { minHeight: geometry2.minimumHeight, flexDirection: "row", flexWrap: "wrap", alignItems: "flex-start", gap: geometry2.gap, paddingVertical: geometry2.paddingY, paddingHorizontal: geometry2.paddingX }, children: [
9436
- leading && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ jsx55(View49, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
9574
+ hasContent(leading) && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ jsx55(View49, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
9437
9575
  /* @__PURE__ */ jsxs43(View49, { style: { flexGrow: 1, flexShrink: 1, flexBasis: geometry2.titleMinimumWidth, minWidth: 0, paddingVertical: titleInset }, children: [
9438
9576
  /* @__PURE__ */ jsx55(KText, { accessibilityRole: "header", style: { ...wrapping, ...typeStyle("sectionTitle"), fontWeight: typeStyle("control").fontWeight }, children: title }),
9439
- description && /* @__PURE__ */ jsx55(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9577
+ !!description && /* @__PURE__ */ jsx55(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9440
9578
  ] }),
9441
- actions && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "actions" }, children: /* @__PURE__ */ jsx55(View49, { 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(View49, { onLayout: square("trailing"), style: { flexShrink: 1, minWidth: 0, maxWidth: "100%", marginRight: iconEdges.trailing ? -edgeInset : 0 }, children: content(action) }, "trailing")) }) })
9579
+ hasContent(actions) && /* @__PURE__ */ jsx55(TopNavigationContext.Provider, { value: { ...geometry2, slot: "actions" }, children: /* @__PURE__ */ jsx55(View49, { 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(View49, { onLayout: square("trailing"), style: { flexShrink: 1, minWidth: 0, maxWidth: "100%", marginRight: iconEdges.trailing ? -edgeInset : 0 }, children: content(action) }, "trailing")) }) })
9442
9580
  ] }) }) });
9443
9581
  }
9444
9582
  function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\uC0C9", safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = true, onNavigate }) {
@@ -9488,7 +9626,7 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9488
9626
  },
9489
9627
  children: [
9490
9628
  hasIcons && /* @__PURE__ */ jsxs43(View49, { style: { width: geometry2.iconSize, height: geometry2.iconSize, flexShrink: 0 }, children: [
9491
- item.icon && /* @__PURE__ */ jsx55(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9629
+ !!item.icon && /* @__PURE__ */ jsx55(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9492
9630
  badge
9493
9631
  ] }),
9494
9632
  /* @__PURE__ */ jsxs43(View49, { style: { maxWidth: "100%" }, children: [
@@ -9520,7 +9658,7 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9520
9658
  flexDirection: wide ? "row" : "column",
9521
9659
  alignItems: wide ? "center" : "stretch"
9522
9660
  }, children: [
9523
- description && /* @__PURE__ */ jsx55(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9661
+ !!description && /* @__PURE__ */ jsx55(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9524
9662
  /* @__PURE__ */ jsx55(View49, { style: { flexDirection: "row", flexWrap: "wrap", justifyContent: "flex-end", alignItems: "center", gap: geometry2.actionsGap, minWidth: 0, maxWidth: "100%", flexShrink: 1 }, children: content(children) })
9525
9663
  ] }) });
9526
9664
  }
@@ -9558,14 +9696,14 @@ import { jsx as jsx57, jsxs as jsxs44 } from "react/jsx-runtime";
9558
9696
  function KjunChip({ label, icon, removable = false, disabled = false, size = "md", removeLabel, onRemove }) {
9559
9697
  const { colors } = useKjunStyles();
9560
9698
  return /* @__PURE__ */ jsxs44(View51, { style: { alignSelf: "flex-start", maxWidth: "100%", minWidth: 0, flexShrink: 1, flexDirection: "row", alignItems: "center", gap: tokens56.extensions.chip.gap, minHeight: removable ? tokens56.extensions.chip.nativeRemoveSize : tokens56.extensions.chip[size], paddingVertical: removable ? 0 : tokens56.extensions.chip.paddingY, paddingLeft: tokens56.extensions.chip.paddingX, paddingRight: removable ? 0 : tokens56.extensions.chip.paddingX, borderRadius: tokens56.extensions.chip.radius, backgroundColor: colors.secondary, opacity: disabled ? tokens56.states.opacity.disabled : 1 }, children: [
9561
- icon && /* @__PURE__ */ jsx57(KjunIcon, { name: icon, size: tokens56.extensions.chip.iconSizes[size] }),
9699
+ !!icon && /* @__PURE__ */ jsx57(KjunIcon, { name: icon, size: tokens56.extensions.chip.iconSizes[size] }),
9562
9700
  /* @__PURE__ */ jsx57(KText, { style: { ...typeStyle(tokens56.extensions.chip.typography[size]), flexShrink: 1, minWidth: 0 }, children: label }),
9563
9701
  removable && /* @__PURE__ */ jsx57(AccessiblePressable, { accessibilityRole: "button", accessibilityLabel: removeLabel || label + " \uC0AD\uC81C", disabled, accessibilityState: { disabled }, onPress: onRemove, style: { width: tokens56.extensions.chip.nativeRemoveSize, height: tokens56.extensions.chip.nativeRemoveSize, borderRadius: tokens56.extensions.chip.radius, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx57(KjunIcon, { name: "x", size: tokens56.extensions.chip.removeIconSizes[size] }) })
9564
9702
  ] });
9565
9703
  }
9566
9704
 
9567
9705
  // packages/native/src/range-input.tsx
9568
- import { useEffect as useEffect26, useMemo as useMemo2, useRef as useRef36, useState as useState43 } from "react";
9706
+ import { useEffect as useEffect27, useMemo as useMemo2, useRef as useRef36, useState as useState43 } from "react";
9569
9707
  import { PanResponder, Platform as Platform27, Pressable as Pressable3, View as View52 } from "react-native";
9570
9708
  import { tokens as tokens57, createSliderDomain, sliderKey } from "@kjun-ui/tokens";
9571
9709
  import { jsx as jsx58, jsxs as jsxs45 } from "react/jsx-runtime";
@@ -9597,10 +9735,10 @@ function RangeInput({
9597
9735
  latest.current = values;
9598
9736
  const callbacks = useRef36({ onValueChange, onChangeCommit });
9599
9737
  callbacks.current = { onValueChange, onChangeCommit };
9600
- useEffect26(() => {
9738
+ useEffect27(() => {
9601
9739
  if (!valid) console.warn("KJUN Slider: \uBC94\uC704\xB7step\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9602
9740
  }, [valid]);
9603
- useEffect26(() => {
9741
+ useEffect27(() => {
9604
9742
  if (blocked) {
9605
9743
  setDragging(null);
9606
9744
  setHovered(null);
@@ -9686,7 +9824,7 @@ function RangeInput({
9686
9824
  minWidth: 0
9687
9825
  },
9688
9826
  children: [
9689
- label && /* @__PURE__ */ jsx58(
9827
+ !!label && /* @__PURE__ */ jsx58(
9690
9828
  KText,
9691
9829
  {
9692
9830
  style: {
@@ -9865,7 +10003,7 @@ function KjunRangeSlider(props) {
9865
10003
  }
9866
10004
 
9867
10005
  // packages/native/src/quantity-stepper.tsx
9868
- import { useEffect as useEffect27, useRef as useRef37, useState as useState44 } from "react";
10006
+ import { useEffect as useEffect28, useRef as useRef37, useState as useState44 } from "react";
9869
10007
  import { Platform as Platform28, View as View53 } from "react-native";
9870
10008
  import { createNumberDomain, quantityKeyAction, tokens as tokens58 } from "@kjun-ui/tokens";
9871
10009
 
@@ -9879,7 +10017,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9879
10017
  const { colors, fontFamily } = useKjunStyles();
9880
10018
  const field = useKjunField();
9881
10019
  const invalid = error || !!(field == null ? void 0 : field.error);
9882
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
10020
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9883
10021
  const geometry2 = tokens58.extensions.compound;
9884
10022
  const buttonStyle = { width: tokens58.input[size].height, paddingHorizontal: 0 };
9885
10023
  const outer = tokens58.input[size].radius;
@@ -9891,12 +10029,12 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9891
10029
  const inputRef = useRef37(null);
9892
10030
  const composing = useRef37(false), dirty = useRef37(false), accepted = useRef37(value);
9893
10031
  const cancelledEscape = useRef37(false);
9894
- useEffect27(() => {
10032
+ useEffect28(() => {
9895
10033
  setDraft(String(value));
9896
10034
  accepted.current = value;
9897
10035
  dirty.current = false;
9898
10036
  }, [value, precision, min, max, step]);
9899
- useEffect27(() => {
10037
+ useEffect28(() => {
9900
10038
  if (!valid) console.warn("KJUN QuantityStepper: \uBC94\uC704\xB7step\xB7\uC815\uBC00\uB3C4\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
9901
10039
  }, [valid]);
9902
10040
  const emit = (next) => {
@@ -9941,7 +10079,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9941
10079
  dirty.current = true;
9942
10080
  setDraft(text);
9943
10081
  } };
9944
- useEffect27(() => {
10082
+ useEffect28(() => {
9945
10083
  if (Platform28.OS !== "web") return;
9946
10084
  const input = inputRef.current;
9947
10085
  const keydown = (event) => handlers.current.key(event);
@@ -9987,7 +10125,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9987
10125
  nativeID: id || (field == null ? void 0 : field.id),
9988
10126
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9989
10127
  accessibilityLabelledBy: labelledBy,
9990
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
10128
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9991
10129
  role: "spinbutton",
9992
10130
  accessibilityValue: { min, max, now: value },
9993
10131
  accessibilityState: { disabled: blocked },
@@ -10013,7 +10151,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
10013
10151
  }
10014
10152
 
10015
10153
  // packages/native/src/time-picker.tsx
10016
- import { Fragment as Fragment21, useEffect as useEffect28, useId as useId11, useMemo as useMemo3, useRef as useRef38, useState as useState45 } from "react";
10154
+ import { Fragment as Fragment21, useEffect as useEffect29, useId as useId11, useMemo as useMemo3, useRef as useRef38, useState as useState45 } from "react";
10017
10155
  import { View as View54 } from "react-native";
10018
10156
  import { createTimeDomain, tokens as tokens59 } from "@kjun-ui/tokens";
10019
10157
  import { jsx as jsx60, jsxs as jsxs47 } from "react/jsx-runtime";
@@ -10025,7 +10163,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
10025
10163
  const field = useKjunField(), uid = useId11();
10026
10164
  const fieldId = (field == null ? void 0 : field.id) || `kjun-time-${uid}`;
10027
10165
  const domain = useMemo3(() => createTimeDomain({ precision, min, max, minuteStep, secondStep }), [precision, min, max, minuteStep, secondStep]), valid = domain.valid && domain.accepts(value);
10028
- useEffect28(() => {
10166
+ useEffect29(() => {
10029
10167
  if (!valid) console.warn("KJUN TimePicker: \uC2DC\uAC01 \uBC94\uC704\xB7\uAC04\uACA9\xB7\uAC12\uC744 \uD655\uC778\uD558\uC138\uC694.");
10030
10168
  }, [valid]);
10031
10169
  const change = (next) => {
@@ -10047,7 +10185,7 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
10047
10185
  change(null);
10048
10186
  (_a = first.current) == null ? void 0 : _a.focus();
10049
10187
  } }) }),
10050
- (error || !!(field == null ? void 0 : field.error) || focused || opened !== null) && /* @__PURE__ */ jsx60(View54, { pointerEvents: "none", "aria-hidden": true, style: { position: "absolute", inset: 0, borderRadius: spec.radius, borderWidth: error || !!(field == null ? void 0 : field.error) ? tokens59.border.controlWidth : tokens59.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
10188
+ !!(error || !!(field == null ? void 0 : field.error) || focused || opened !== null) && /* @__PURE__ */ jsx60(View54, { pointerEvents: "none", "aria-hidden": true, style: { position: "absolute", inset: 0, borderRadius: spec.radius, borderWidth: error || !!(field == null ? void 0 : field.error) ? tokens59.border.controlWidth : tokens59.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
10051
10189
  ] });
10052
10190
  }
10053
10191
  export {