@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.
@@ -141,9 +141,23 @@ function mergeIcons(parent, own) {
141
141
  function withFallbackIcons(registry, fallbacks) {
142
142
  return mergeIcons(fallbacks, registry);
143
143
  }
144
+ var warnedIcons = /* @__PURE__ */ new Set();
145
+ function developmentMode() {
146
+ try {
147
+ return true;
148
+ } catch (e) {
149
+ return false;
150
+ }
151
+ }
152
+ function warnMissingIcon(name) {
153
+ if (!developmentMode() || warnedIcons.has(name)) return;
154
+ warnedIcons.add(name);
155
+ 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.`);
156
+ }
144
157
  function resolveIcon(registry, name, filled = false) {
145
158
  var _a;
146
159
  const definition = Object.prototype.hasOwnProperty.call(registry, name) ? registry[name] : void 0;
160
+ if (!definition && name) warnMissingIcon(name);
147
161
  const filledNodes = filled && (definition == null ? void 0 : definition.filled);
148
162
  return { nodes: filledNodes || (definition == null ? void 0 : definition.outline) || ((_a = registry["help-circle"]) == null ? void 0 : _a.outline) || import_defaults.defaultIcons["help-circle"].outline, filled: !!filledNodes };
149
163
  }
@@ -863,6 +877,7 @@ function KjunButton(input) {
863
877
  size: declaredSize = "md",
864
878
  loading = false,
865
879
  block = false,
880
+ wrap = false,
866
881
  disabled = false,
867
882
  prefixIcon,
868
883
  suffixIcon,
@@ -883,7 +898,7 @@ function KjunButton(input) {
883
898
  const formActions = (0, import_react13.useContext)(FormActionsContext);
884
899
  const cardActions = (0, import_react13.useContext)(CardActionsContext);
885
900
  const stateContent = (0, import_react13.useContext)(DataStateContentContext);
886
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
901
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
887
902
  const height = Math.max(import_tokens5.tokens.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
888
903
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : import_tokens5.tokens.button.iconSizes[size];
889
904
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -981,7 +996,7 @@ function KjunButton(input) {
981
996
  opacity: overlayLoading ? 0 : 1,
982
997
  ...multiline || navigation ? { flexShrink: 1, minWidth: 0, textAlign: "center" } : {}
983
998
  }, children }),
984
- suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.View, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
999
+ !!suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.View, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
985
1000
  KjunIcon,
986
1001
  {
987
1002
  name: busy && !prefixIcon ? "loader-2" : suffixIcon,
@@ -1044,6 +1059,16 @@ function FieldClear({ size, disabled, label, onPress }) {
1044
1059
  );
1045
1060
  }
1046
1061
 
1062
+ // shared/package-runtime/aria-ids.ts
1063
+ function mergeIds(...lists) {
1064
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
1065
+ return ids.length ? ids.join(" ") : void 0;
1066
+ }
1067
+ function joinHints(...parts) {
1068
+ const text = parts.filter(Boolean);
1069
+ return text.length ? text.join(". ") : void 0;
1070
+ }
1071
+
1047
1072
  // packages/native/src/field-surface.ts
1048
1073
  var import_react16 = require("react");
1049
1074
  var import_react_native7 = require("react-native");
@@ -1212,6 +1237,7 @@ function KjunFormGroup({
1212
1237
  required = false,
1213
1238
  error = "",
1214
1239
  hint = "",
1240
+ labelHidden = false,
1215
1241
  children,
1216
1242
  style
1217
1243
  }) {
@@ -1221,9 +1247,17 @@ function KjunFormGroup({
1221
1247
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1222
1248
  FieldContext.Provider,
1223
1249
  {
1224
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1250
+ value: {
1251
+ id: fieldId,
1252
+ label,
1253
+ error,
1254
+ hint,
1255
+ required,
1256
+ labelHidden,
1257
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1258
+ },
1225
1259
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native8.View, { style: [{ minWidth: 0, marginBottom: import_tokens9.tokens.extensions.form.itemGap }, style], children: [
1226
- label && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1260
+ !!label && !labelHidden && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1227
1261
  import_react_native8.Text,
1228
1262
  {
1229
1263
  nativeID: fieldId + "-label",
@@ -1240,7 +1274,15 @@ function KjunFormGroup({
1240
1274
  }
1241
1275
  ),
1242
1276
  children,
1243
- (error || hint) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1277
+ !!hint && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1278
+ import_react_native8.Text,
1279
+ {
1280
+ nativeID: fieldId + "-hint",
1281
+ style: { fontFamily, ...typeStyle("caption"), marginTop: import_tokens9.tokens.extensions.form.messageGap, color: c.textSecondary },
1282
+ children: hint
1283
+ }
1284
+ ),
1285
+ !!error && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1244
1286
  import_react_native8.View,
1245
1287
  {
1246
1288
  style: {
@@ -1250,19 +1292,14 @@ function KjunFormGroup({
1250
1292
  marginTop: import_tokens9.tokens.extensions.form.messageGap
1251
1293
  },
1252
1294
  children: [
1253
- !!error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(KjunIcon, { name: "alert-circle", size: import_tokens9.tokens.extensions.form.messageIconSize, color: c.danger }),
1295
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(KjunIcon, { name: "alert-circle", size: import_tokens9.tokens.extensions.form.messageIconSize, color: c.danger }),
1254
1296
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1255
1297
  import_react_native8.Text,
1256
1298
  {
1257
- nativeID: fieldId + (error ? "-error" : "-hint"),
1258
- accessibilityRole: error ? "alert" : void 0,
1259
- style: {
1260
- fontFamily,
1261
- ...typeStyle("caption"),
1262
- flexShrink: 1,
1263
- color: error ? c.danger : c.textSecondary
1264
- },
1265
- children: error || hint
1299
+ nativeID: fieldId + "-error",
1300
+ accessibilityRole: "alert",
1301
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1302
+ children: error
1266
1303
  }
1267
1304
  )
1268
1305
  ]
@@ -1312,6 +1349,7 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1312
1349
  const invalid = !!(error || errorMessage || (field == null ? void 0 : field.error));
1313
1350
  const canEdit = !disabled && !readOnly && editable;
1314
1351
  const surface = useFieldSurface(size, { disabled, readOnly: !!readOnly || !editable, invalid, open: props["aria-expanded"] === true });
1352
+ const affixText = { ...inputTypeStyle(size), color: surface.icon };
1315
1353
  const showClear = clearable && !!value;
1316
1354
  const [prefixWidth, setPrefixWidth] = (0, import_react17.useState)(0);
1317
1355
  const [affixWidth, setAffixWidth] = (0, import_react17.useState)(0);
@@ -1336,8 +1374,8 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1336
1374
  editable: canEdit,
1337
1375
  readOnly,
1338
1376
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1339
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1340
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1377
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1378
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1341
1379
  accessibilityState: { disabled },
1342
1380
  ...import_react_native8.Platform.OS === "web" ? {
1343
1381
  // Native Web TextInput does not translate accessibilityState.
@@ -1373,7 +1411,7 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1373
1411
  ]
1374
1412
  }
1375
1413
  ),
1376
- (prefix || prefixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1414
+ !!(prefix || prefixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1377
1415
  import_react_native8.View,
1378
1416
  {
1379
1417
  onLayout: (event) => setPrefixWidth(event.nativeEvent.layout.width),
@@ -1385,7 +1423,7 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1385
1423
  bottom: 0,
1386
1424
  justifyContent: "center"
1387
1425
  },
1388
- children: prefix || /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1426
+ children: prefix ? content(prefix, affixText) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1389
1427
  KjunIcon,
1390
1428
  {
1391
1429
  name: prefixIcon,
@@ -1395,7 +1433,7 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1395
1433
  )
1396
1434
  }
1397
1435
  ),
1398
- (showClear || suffix || suffixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1436
+ !!(showClear || suffix || suffixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1399
1437
  import_react_native8.View,
1400
1438
  {
1401
1439
  onLayout: (event) => setAffixWidth(event.nativeEvent.layout.width),
@@ -1416,7 +1454,7 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1416
1454
  onClear == null ? void 0 : onClear();
1417
1455
  (_a = input.current) == null ? void 0 : _a.focus();
1418
1456
  } }),
1419
- suffix || suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1457
+ suffix ? content(suffix, affixText) : !!suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1420
1458
  KjunIcon,
1421
1459
  {
1422
1460
  name: suffixIcon,
@@ -1456,6 +1494,17 @@ function KjunFormLayout({
1456
1494
  ) });
1457
1495
  }
1458
1496
 
1497
+ // shared/package-runtime/content-presence.ts
1498
+ var import_react18 = require("react");
1499
+ function hasContent(node) {
1500
+ if (node == null || typeof node === "boolean") return false;
1501
+ if (typeof node === "string") return node.trim().length > 0;
1502
+ if (Array.isArray(node)) return node.some(hasContent);
1503
+ if ((0, import_react18.isValidElement)(node) && node.type === import_react18.Fragment)
1504
+ return hasContent(node.props.children);
1505
+ return true;
1506
+ }
1507
+
1459
1508
  // packages/native/src/layer-backdrop.tsx
1460
1509
  var import_react_native9 = require("react-native");
1461
1510
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -1464,7 +1513,7 @@ function LayerBackdrop({ onPress, style }) {
1464
1513
  }
1465
1514
 
1466
1515
  // packages/native/src/modal.tsx
1467
- var import_react20 = require("react");
1516
+ var import_react21 = require("react");
1468
1517
 
1469
1518
  // shared/package-runtime/form-actions.ts
1470
1519
  function formActionsNeedStack(width, widths, gap) {
@@ -1477,17 +1526,17 @@ var import_tokens10 = require("@kjun-ui/tokens");
1477
1526
  var import_react_native13 = require("react-native");
1478
1527
 
1479
1528
  // packages/native/src/overlay-host.tsx
1480
- var import_react19 = require("react");
1529
+ var import_react20 = require("react");
1481
1530
  var import_react_native11 = require("react-native");
1482
1531
 
1483
1532
  // packages/native/src/modal-window.web.tsx
1484
- var import_react18 = require("react");
1533
+ var import_react19 = require("react");
1485
1534
  var import_react_dom = require("react-dom");
1486
1535
  var import_react_native10 = require("react-native");
1487
1536
  var import_jsx_runtime10 = require("react/jsx-runtime");
1488
1537
  function ModalWindow({ generation, children, visible, ...props }) {
1489
- const [container, setContainer] = (0, import_react18.useState)(null);
1490
- (0, import_react18.useLayoutEffect)(() => {
1538
+ const [container, setContainer] = (0, import_react19.useState)(null);
1539
+ (0, import_react19.useLayoutEffect)(() => {
1491
1540
  const element = document.createElement("div");
1492
1541
  element.style.display = "contents";
1493
1542
  setContainer(element);
@@ -1508,17 +1557,17 @@ var import_jsx_runtime11 = (
1508
1557
  // a new window even when an interrupted exit retained the old surface.
1509
1558
  require("react/jsx-runtime")
1510
1559
  );
1511
- var FeedbackOverlayContext = (0, import_react19.createContext)(
1560
+ var FeedbackOverlayContext = (0, import_react20.createContext)(
1512
1561
  null
1513
1562
  );
1514
1563
  function ScopedNativeModal({ children, visible, ...props }) {
1515
1564
  var _a;
1516
- const layers = useLayerState(), scope = (0, import_react19.useContext)(LayerScope);
1565
+ const layers = useLayerState(), scope = (0, import_react20.useContext)(LayerScope);
1517
1566
  const entry = scope ? layers.entries.get(scope) : void 0;
1518
1567
  const generation = (_a = entry == null ? void 0 : entry.order) != null ? _a : 0;
1519
1568
  const inactive = !!scope && !layers.isActive(scope);
1520
- const host = (0, import_react19.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react19.useId)(), register = host == null ? void 0 : host.register;
1521
- (0, import_react19.useEffect)(
1569
+ const host = (0, import_react20.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react20.useId)(), register = host == null ? void 0 : host.register;
1570
+ (0, import_react20.useEffect)(
1522
1571
  () => visible && register ? register(id) : void 0,
1523
1572
  [visible, register, id, generation]
1524
1573
  );
@@ -1564,7 +1613,7 @@ function KjunFormActions({
1564
1613
  onConfirm
1565
1614
  }) {
1566
1615
  const { fontFamily } = useKjunStyles();
1567
- const [width, setWidth] = (0, import_react20.useState)(0), [measurements, setMeasurements] = (0, import_react20.useState)({});
1616
+ const [width, setWidth] = (0, import_react21.useState)(0), [measurements, setMeasurements] = (0, import_react21.useState)({});
1568
1617
  const labels = [showCancel && { text: cancelText }, showConfirm && { text: confirmText }].filter(Boolean);
1569
1618
  const keyFor = (text) => `${fontFamily}:${size}:${text}`;
1570
1619
  const widths = labels.map((label) => measurements[keyFor(label.text)] === void 0 ? 0 : Math.max(import_tokens10.tokens.button.minWidths[size], measurements[keyFor(label.text)] + import_tokens10.tokens.button.paddingX[size] * 2));
@@ -1665,7 +1714,7 @@ function KjunModal({
1665
1714
  const motion = useNativePresence(open, import_tokens10.tokens.motion.layerEnter, import_tokens10.tokens.motion.layerExit, true, 0, { fade: import_tokens10.tokens.motion.backdropExit });
1666
1715
  const offset = useMotionValue(open ? 0 : import_tokens10.tokens.motionDistance.modalExit, open ? import_tokens10.tokens.motion.layerEnter : import_tokens10.tokens.motion.layerExit, import_tokens10.tokens.motionDistance.modalEnter, open ? motionOut : motionIn, true);
1667
1716
  const backdrop = useNativePresence(open, import_tokens10.tokens.motion.backdrop, import_tokens10.tokens.motion.backdropExit, true, 0, { fade: import_tokens10.tokens.motion.backdropExit });
1668
- (0, import_react20.useLayoutEffect)(() => {
1717
+ (0, import_react21.useLayoutEffect)(() => {
1669
1718
  if (!motion.present) {
1670
1719
  offset.stopAnimation();
1671
1720
  offset.setValue(import_tokens10.tokens.motionDistance.modalEnter);
@@ -1755,7 +1804,7 @@ function KjunModal({
1755
1804
  borderBottomColor: c.border
1756
1805
  },
1757
1806
  children: [
1758
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: { flex: 1, paddingRight: import_tokens10.tokens.modal.titleActionGap }, children: header || /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1807
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native13.View, { style: { flex: 1, paddingRight: import_tokens10.tokens.modal.titleActionGap }, children: hasContent(header) ? content(header) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1759
1808
  import_react_native13.Text,
1760
1809
  {
1761
1810
  accessibilityRole: "header",
@@ -1795,7 +1844,7 @@ function KjunModal({
1795
1844
  children
1796
1845
  }
1797
1846
  ),
1798
- (showFooter || footer) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1847
+ (showFooter || hasContent(footer)) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1799
1848
  import_react_native13.View,
1800
1849
  {
1801
1850
  style: {
@@ -1808,7 +1857,7 @@ function KjunModal({
1808
1857
  borderTopWidth: import_tokens10.tokens.border.defaultWidth,
1809
1858
  borderTopColor: c.border
1810
1859
  },
1811
- children: footer || /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1860
+ children: hasContent(footer) ? content(footer) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1812
1861
  KjunFormActions,
1813
1862
  {
1814
1863
  size: footerSize,
@@ -1844,19 +1893,6 @@ var import_tokens11 = require("@kjun-ui/tokens");
1844
1893
  var import_react22 = require("react");
1845
1894
  var import_react_native14 = require("react-native");
1846
1895
  var import_react_native_svg2 = __toESM(require("react-native-svg"), 1);
1847
-
1848
- // shared/package-runtime/content-presence.ts
1849
- var import_react21 = require("react");
1850
- function hasContent(node) {
1851
- if (node == null || typeof node === "boolean") return false;
1852
- if (typeof node === "string") return node.trim().length > 0;
1853
- if (Array.isArray(node)) return node.some(hasContent);
1854
- if ((0, import_react21.isValidElement)(node) && node.type === import_react21.Fragment)
1855
- return hasContent(node.props.children);
1856
- return true;
1857
- }
1858
-
1859
- // packages/native/src/card.tsx
1860
1896
  var import_jsx_runtime13 = require("react/jsx-runtime");
1861
1897
  function KjunCard({
1862
1898
  title,
@@ -1917,8 +1953,8 @@ function KjunCard({
1917
1953
  borderBottomColor: borderColor
1918
1954
  }, children: [
1919
1955
  hasHeading && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, { style: { flexGrow: 1, flexShrink: 1, minWidth: 0, maxWidth: "100%" }, children: hasContent(header) ? content(header, { color: foreground }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1920
- title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1921
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { style: { ...typeStyle("caption"), marginTop: import_tokens11.tokens.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1956
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1957
+ !!subtitle && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { style: { ...typeStyle("caption"), marginTop: import_tokens11.tokens.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1922
1958
  ] }) }),
1923
1959
  hasContent(headerActions) && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CardActionsContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native14.View, { style: { flexDirection: "row", flexWrap: "wrap", minWidth: 0, maxWidth: "100%", marginStart: "auto", alignItems: "center", justifyContent: "flex-end", gap: import_tokens11.tokens.card.titleGap }, children: content(headerActions, { color: foreground }) }) })
1924
1960
  ] }),
@@ -1938,6 +1974,13 @@ function KjunCard({
1938
1974
  ] });
1939
1975
  }
1940
1976
 
1977
+ // shared/package-runtime/alert-live.ts
1978
+ function alertRole(tone, live) {
1979
+ if (live === "off") return void 0;
1980
+ if (live) return live === "assertive" ? "alert" : "status";
1981
+ return tone === "danger" ? "alert" : "status";
1982
+ }
1983
+
1941
1984
  // packages/native/src/display-primitives.tsx
1942
1985
  var import_tokens12 = require("@kjun-ui/tokens");
1943
1986
  var import_react23 = require("react");
@@ -2024,9 +2067,9 @@ function KjunEmpty({
2024
2067
  paddingVertical: stateContent === "empty" ? 0 : import_tokens12.tokens.dimension.value32
2025
2068
  },
2026
2069
  children: [
2027
- icon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { marginBottom: spec.iconGap }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KjunIcon, { name: icon, size: spec.iconSize, color: colors.textSecondary }) }),
2070
+ !!icon && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { marginBottom: spec.iconGap }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KjunIcon, { name: icon, size: spec.iconSize, color: colors.textSecondary }) }),
2028
2071
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KText, { style: { ...typeStyle("cardTitle"), color: colors.text, fontSize: spec.titleSize, lineHeight: spec.titleLineHeight, textAlign: "center", maxWidth: spec.textMaxWidth }, children: text }),
2029
- description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2072
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2030
2073
  KText,
2031
2074
  {
2032
2075
  style: {
@@ -2041,7 +2084,7 @@ function KjunEmpty({
2041
2084
  children: description
2042
2085
  }
2043
2086
  ),
2044
- children && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
2087
+ hasContent(children) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
2045
2088
  ]
2046
2089
  }
2047
2090
  );
@@ -2119,7 +2162,7 @@ function KjunSpinner({ size = "md", text }) {
2119
2162
  )
2120
2163
  }
2121
2164
  ),
2122
- text && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KText, { style: { marginLeft: import_tokens12.tokens.dimension.value8, color: colors.textSecondary }, children: text })
2165
+ !!text && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KText, { style: { marginLeft: import_tokens12.tokens.dimension.value8, color: colors.textSecondary }, children: text })
2123
2166
  ]
2124
2167
  }
2125
2168
  );
@@ -2130,6 +2173,7 @@ function KjunAlert({
2130
2173
  size = "md",
2131
2174
  title,
2132
2175
  closable = false,
2176
+ live,
2133
2177
  children,
2134
2178
  actions,
2135
2179
  onClose
@@ -2146,7 +2190,7 @@ function KjunAlert({
2146
2190
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2147
2191
  import_react_native15.View,
2148
2192
  {
2149
- role: tone === "danger" ? "alert" : "status",
2193
+ role: alertRole(tone, live),
2150
2194
  style: {
2151
2195
  flexDirection: "row",
2152
2196
  alignItems: "flex-start",
@@ -2179,7 +2223,7 @@ function KjunAlert({
2179
2223
  ...typeStyle(small ? "caption" : "body"),
2180
2224
  color: colors.textSecondary
2181
2225
  }) }),
2182
- hasContent(actions) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActionSizeContext.Provider, { value: alertActionSizes[small ? "sm" : "md"], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AlertActionsContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: geometry2.actionsGap, marginTop: geometry2[small ? "sm" : "md"].actionGap }, children: actions }) }) })
2226
+ hasContent(actions) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActionSizeContext.Provider, { value: alertActionSizes[small ? "sm" : "md"], children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AlertActionsContext.Provider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: geometry2.actionsGap, marginTop: geometry2[small ? "sm" : "md"].actionGap }, children: content(actions) }) }) })
2183
2227
  ] }),
2184
2228
  closable && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2185
2229
  AccessiblePressable,
@@ -2229,7 +2273,7 @@ function KjunProgress({
2229
2273
  const { colors } = useKjunStyles();
2230
2274
  const percentage = Number.isFinite(value) && max > 0 ? Math.min(100, Math.max(0, Math.round(value / max * 100))) : 0;
2231
2275
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { children: [
2232
- (label || showLabel) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2276
+ !!(label || showLabel) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2233
2277
  import_react_native15.View,
2234
2278
  {
2235
2279
  style: {
@@ -2644,7 +2688,7 @@ function ToastView({
2644
2688
  children: [
2645
2689
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KjunIcon, { name: icon, color: colors[`${tone}Accent`] }),
2646
2690
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native17.View, { style: { flex: 1, minWidth: 0 }, children: [
2647
- toast.title && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: import_tokens15.tokens.extensions.toast.titleGap }, children: toast.title }),
2691
+ !!toast.title && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: import_tokens15.tokens.extensions.toast.titleGap }, children: toast.title }),
2648
2692
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KText, { style: { color: colors.textSecondary }, children: toast.message }),
2649
2693
  toast.action && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2650
2694
  AccessiblePressable,
@@ -2889,7 +2933,7 @@ function FeedbackDialog({
2889
2933
  onConfirm: submit,
2890
2934
  confirmVariant: options.type === "danger" ? "danger" : "primary",
2891
2935
  children: [
2892
- options.message && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(KText, { style: { marginBottom: import_tokens18.tokens.dimension.value16 }, children: options.message }),
2936
+ !!options.message && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(KText, { style: { marginBottom: import_tokens18.tokens.dimension.value16 }, children: options.message }),
2893
2937
  request.kind === "prompt" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2894
2938
  KjunInput,
2895
2939
  {
@@ -2906,7 +2950,7 @@ function FeedbackDialog({
2906
2950
  }
2907
2951
  );
2908
2952
  }
2909
- function KjunFeedbackProvider({ children }) {
2953
+ function KjunFeedbackProvider({ children, style }) {
2910
2954
  const [controller] = (0, import_react31.useState)(import_tokens18.createFeedbackController);
2911
2955
  const life = (0, import_react31.useRef)(0);
2912
2956
  const snapshot = (0, import_react31.useSyncExternalStore)(
@@ -2936,7 +2980,7 @@ function KjunFeedbackProvider({ children }) {
2936
2980
  {
2937
2981
  value: { top: layers[layers.length - 1], register, render: renderToasts },
2938
2982
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(KjunFeedbackContext.Provider, { value: controller.api, children: [
2939
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: { position: "relative" }, children: [
2983
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: [{ position: "relative", flexGrow: 1, flexShrink: 1, alignSelf: "stretch" }, style], children: [
2940
2984
  children,
2941
2985
  layers.length === 0 && renderToasts()
2942
2986
  ] }),
@@ -3018,7 +3062,7 @@ function KjunCheckbox({
3018
3062
  )
3019
3063
  }
3020
3064
  ),
3021
- (children || label) && content(children || label, {
3065
+ !!(children || label) && content(children || label, {
3022
3066
  marginLeft: import_tokens19.tokens.extensions.checkbox.labelGap,
3023
3067
  ...typeStyle("label")
3024
3068
  })
@@ -3129,7 +3173,7 @@ function KjunSwitch({
3129
3173
  ]
3130
3174
  }
3131
3175
  ),
3132
- (children || label) && content(children || label, {
3176
+ !!(children || label) && content(children || label, {
3133
3177
  marginLeft: import_tokens19.tokens.extensions.switch.labelGap,
3134
3178
  ...typeStyle("label")
3135
3179
  })
@@ -3206,7 +3250,7 @@ function KjunRadio({
3206
3250
  )
3207
3251
  }
3208
3252
  ),
3209
- (children || label) && content(children || label, {
3253
+ !!(children || label) && content(children || label, {
3210
3254
  marginLeft: geometry2.labelGap,
3211
3255
  ...typeStyle("label")
3212
3256
  })
@@ -3449,7 +3493,7 @@ function KjunButtonGroup({
3449
3493
  opacity: blocked ? import_tokens21.tokens.states.opacity.disabled : 1
3450
3494
  }),
3451
3495
  children: [
3452
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3496
+ !!option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3453
3497
  KjunIcon,
3454
3498
  {
3455
3499
  name: option.icon,
@@ -3546,7 +3590,7 @@ function KjunFilterGroup({
3546
3590
  }
3547
3591
  }
3548
3592
  ),
3549
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3593
+ !!option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3550
3594
  KjunIcon,
3551
3595
  {
3552
3596
  name: option.icon,
@@ -3634,13 +3678,13 @@ var KjunTextarea = (0, import_react35.forwardRef)(
3634
3678
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3635
3679
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3636
3680
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3637
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3681
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3638
3682
  accessibilityState: { disabled },
3639
3683
  ...import_react_native24.Platform.OS === "web" ? {
3640
3684
  "aria-disabled": disabled,
3641
3685
  "aria-invalid": invalid || void 0,
3642
3686
  "aria-describedby": field == null ? void 0 : field.describedBy,
3643
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3687
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3644
3688
  "aria-required": (field == null ? void 0 : field.required) || void 0
3645
3689
  } : {},
3646
3690
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3677,8 +3721,50 @@ var KjunTextarea = (0, import_react35.forwardRef)(
3677
3721
  var import_tokens23 = require("@kjun-ui/tokens");
3678
3722
  var import_react36 = require("react");
3679
3723
  var import_react_native25 = require("react-native");
3724
+
3725
+ // shared/package-runtime/drawer-geometry.ts
3726
+ function keyboardEvents(os) {
3727
+ return os === "ios" ? { show: "keyboardWillShow", hide: "keyboardWillHide" } : null;
3728
+ }
3729
+ function keyboardOverlap(windowHeight, keyboardScreenY) {
3730
+ return Math.max(0, windowHeight - keyboardScreenY);
3731
+ }
3732
+ function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap, keyboard, maxHeight }) {
3733
+ const visible = windowHeight - keyboard;
3734
+ const bottomInset = keyboard > 0 ? 0 : safeAreaBottom;
3735
+ if (position === "left" || position === "right")
3736
+ return { maxHeight: visible, height: visible, paddingTop: safeAreaTop, paddingBottom: bottomInset, lift: 0 };
3737
+ const sheet = maxHeight != null ? maxHeight : visible - (position === "bottom" ? safeAreaTop : bottomInset) - gap;
3738
+ return {
3739
+ maxHeight: Math.max(0, Math.min(sheet, visible)),
3740
+ height: void 0,
3741
+ paddingTop: position === "top" ? safeAreaTop : 0,
3742
+ paddingBottom: position === "bottom" ? bottomInset : 0,
3743
+ lift: position === "bottom" ? keyboard : 0
3744
+ };
3745
+ }
3746
+
3747
+ // packages/native/src/drawer.tsx
3680
3748
  var import_react_native26 = require("react-native");
3681
3749
  var import_jsx_runtime23 = require("react/jsx-runtime");
3750
+ function useKeyboardOverlap(enabled2, windowHeight) {
3751
+ const [overlap, setOverlap] = (0, import_react36.useState)(0);
3752
+ (0, import_react36.useEffect)(() => {
3753
+ var _a, _b;
3754
+ setOverlap(0);
3755
+ const events = keyboardEvents(import_react_native26.Platform.OS);
3756
+ if (!enabled2 || !events) return;
3757
+ const metrics = (_b = (_a = import_react_native25.Keyboard).metrics) == null ? void 0 : _b.call(_a);
3758
+ if (metrics) setOverlap(keyboardOverlap(windowHeight, metrics.screenY));
3759
+ const show = import_react_native25.Keyboard.addListener(events.show, (event) => setOverlap(keyboardOverlap(windowHeight, event.endCoordinates.screenY)));
3760
+ const hide = import_react_native25.Keyboard.addListener(events.hide, () => setOverlap(0));
3761
+ return () => {
3762
+ show.remove();
3763
+ hide.remove();
3764
+ };
3765
+ }, [enabled2, windowHeight]);
3766
+ return overlap;
3767
+ }
3682
3768
  function KjunDrawer({
3683
3769
  open,
3684
3770
  onOpenChange,
@@ -3693,9 +3779,24 @@ function KjunDrawer({
3693
3779
  footer,
3694
3780
  children,
3695
3781
  onClose,
3696
- ariaLabel
3782
+ ariaLabel,
3783
+ safeAreaTop = 0,
3784
+ safeAreaBottom = 0,
3785
+ maxHeight,
3786
+ avoidKeyboard = true
3697
3787
  }) {
3698
3788
  const { colors } = useKjunStyles(), window2 = (0, import_react_native26.useWindowDimensions)(), vertical = position === "left" || position === "right";
3789
+ const [rootHeight, setRootHeight] = (0, import_react36.useState)(0);
3790
+ const keyboard = useKeyboardOverlap(open && avoidKeyboard, window2.height);
3791
+ const frame = drawerFrame({
3792
+ position,
3793
+ windowHeight: rootHeight || window2.height,
3794
+ safeAreaTop: Math.max(0, safeAreaTop),
3795
+ safeAreaBottom: Math.max(0, safeAreaBottom),
3796
+ gap: import_tokens23.tokens.extensions.drawer.sheetGap,
3797
+ keyboard,
3798
+ maxHeight
3799
+ });
3699
3800
  const [panelHeight, setPanelHeight] = (0, import_react36.useState)(0);
3700
3801
  const motion = useNativePresence(open, import_tokens23.tokens.motion.layerEnter, import_tokens23.tokens.motion.layerExit, vertical || panelHeight > 0, 0, { enterEasing: motionEmphasized });
3701
3802
  const backdrop = useNativePresence(open, import_tokens23.tokens.motion.backdrop, import_tokens23.tokens.motion.backdropExit, true, 0, { fade: import_tokens23.tokens.motion.backdropExit });
@@ -3719,6 +3820,7 @@ function KjunDrawer({
3719
3820
  children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3720
3821
  import_react_native26.View,
3721
3822
  {
3823
+ onLayout: (event) => setRootHeight(event.nativeEvent.layout.height),
3722
3824
  style: {
3723
3825
  flex: 1,
3724
3826
  justifyContent: position === "bottom" ? "flex-end" : "flex-start",
@@ -3757,8 +3859,11 @@ function KjunDrawer({
3757
3859
  opacity: vertical || panelHeight > 0 ? 1 : 0,
3758
3860
  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] }) }],
3759
3861
  width: vertical ? Math.min(width, window2.width) : window2.width,
3760
- height: vertical ? window2.height : void 0,
3761
- maxHeight: window2.height,
3862
+ height: frame.height,
3863
+ maxHeight: frame.maxHeight,
3864
+ paddingTop: frame.paddingTop,
3865
+ paddingBottom: frame.paddingBottom,
3866
+ marginBottom: frame.lift,
3762
3867
  borderRadius: import_tokens23.tokens.extensions.drawer.radius,
3763
3868
  borderTopLeftRadius: position === "bottom" ? import_tokens23.tokens.extensions.drawer.bottomRadius : import_tokens23.tokens.extensions.drawer.radius,
3764
3869
  borderTopRightRadius: position === "bottom" ? import_tokens23.tokens.extensions.drawer.bottomRadius : import_tokens23.tokens.extensions.drawer.radius,
@@ -3766,7 +3871,7 @@ function KjunDrawer({
3766
3871
  backgroundColor: colors.surface
3767
3872
  },
3768
3873
  children: [
3769
- (title || header || closable) && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3874
+ !!(title || header || closable) && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3770
3875
  import_react_native26.View,
3771
3876
  {
3772
3877
  style: {
@@ -3779,7 +3884,7 @@ function KjunDrawer({
3779
3884
  borderBottomColor: colors.border
3780
3885
  },
3781
3886
  children: [
3782
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.View, { style: { flex: 1 }, children: header || /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3887
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.View, { style: { flex: 1 }, children: hasContent(header) ? content(header) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3783
3888
  KText,
3784
3889
  {
3785
3890
  style: { ...typeStyle("sectionTitle") },
@@ -3804,12 +3909,12 @@ function KjunDrawer({
3804
3909
  import_react_native26.ScrollView,
3805
3910
  {
3806
3911
  keyboardShouldPersistTaps: "handled",
3807
- style: vertical ? { flex: 1 } : { flexGrow: 0 },
3912
+ style: vertical ? { flex: 1 } : { flexGrow: 0, flexShrink: 1 },
3808
3913
  contentContainerStyle: { padding: noPadding ? 0 : import_tokens23.tokens.extensions.drawer.bodyPadding },
3809
3914
  children: content(children)
3810
3915
  }
3811
3916
  ),
3812
- footer && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3917
+ hasContent(footer) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3813
3918
  import_react_native26.View,
3814
3919
  {
3815
3920
  style: {
@@ -3879,8 +3984,11 @@ function FloatingPanel({
3879
3984
  flip = true,
3880
3985
  minimumWidth = 0,
3881
3986
  focusOnOpen = false,
3882
- reveal
3987
+ reveal,
3988
+ header,
3989
+ nonModal = false
3883
3990
  }) {
3991
+ const [headerHeight, setHeaderHeight] = (0, import_react37.useState)(0);
3884
3992
  const panelRef = (0, import_react37.useRef)(null);
3885
3993
  const scroll = (0, import_react37.useRef)(null), viewport = (0, import_react37.useRef)(0);
3886
3994
  const { colors } = useKjunStyles(), window2 = (0, import_react_native28.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react37.useState)(false), [sized, setSized] = (0, import_react37.useState)(false), [anchor, setAnchor] = (0, import_react37.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react37.useState)({ width: 0, height: 0 });
@@ -3935,7 +4043,7 @@ function FloatingPanel({
3935
4043
  maximumWidth,
3936
4044
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : import_tokens24.tokens.extensions.popover.width)
3937
4045
  );
3938
- const available = Math.min(maxHeight != null ? maxHeight : field ? import_tokens24.tokens.extensions.menu.listMaxHeight : import_tokens24.tokens.extensions.floating.maxHeight, window2.height - 2 * import_tokens24.tokens.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - import_tokens24.tokens.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - import_tokens24.tokens.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
4046
+ const available = Math.min(maxHeight != null ? maxHeight : field ? import_tokens24.tokens.extensions.menu.listMaxHeight : import_tokens24.tokens.extensions.floating.maxHeight, window2.height - 2 * import_tokens24.tokens.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - import_tokens24.tokens.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - import_tokens24.tokens.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3939
4047
  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;
3940
4048
  if (placement === "left" || placement === "right") {
3941
4049
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -4019,7 +4127,7 @@ function FloatingPanel({
4019
4127
  }
4020
4128
  }
4021
4129
  ),
4022
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4130
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4023
4131
  import_react_native27.Animated.View,
4024
4132
  {
4025
4133
  ref: panelRef,
@@ -4030,7 +4138,7 @@ function FloatingPanel({
4030
4138
  pointerEvents: open && anchored && sized ? "auto" : "none",
4031
4139
  accessibilityElementsHidden: !open || !anchored || !sized,
4032
4140
  ...!open || !anchored || !sized ? { inert: true } : {},
4033
- accessibilityViewIsModal: true,
4141
+ accessibilityViewIsModal: !nonModal,
4034
4142
  accessibilityLabel: ariaLabel,
4035
4143
  style: {
4036
4144
  opacity: anchored && sized ? motion.opacity : 0,
@@ -4052,26 +4160,42 @@ function FloatingPanel({
4052
4160
  boxShadow: (tooltip ? import_tokens24.tokens.extensions.tooltip.elevation : popover ? import_tokens24.tokens.extensions.popover.elevation : import_tokens24.tokens.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
4053
4161
  overflow: "hidden"
4054
4162
  },
4055
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4056
- import_react_native28.ScrollView,
4057
- {
4058
- ref: scroll,
4059
- onLayout: (event) => {
4060
- viewport.current = event.nativeEvent.layout.height;
4061
- },
4062
- keyboardShouldPersistTaps: "handled",
4063
- style: { maxHeight: available },
4064
- contentContainerStyle: { padding: menu || field ? import_tokens24.tokens.extensions.menu.padding : 0 },
4065
- onContentSizeChange: (width2, height2) => {
4066
- if (contentSizedMenu) return;
4067
- setSized(true);
4068
- setPanel(
4069
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4070
- );
4071
- },
4072
- children
4073
- }
4074
- )
4163
+ children: [
4164
+ header != null && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4165
+ import_react_native28.View,
4166
+ {
4167
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
4168
+ style: {
4169
+ paddingTop: import_tokens24.tokens.extensions.popover.padding,
4170
+ paddingHorizontal: import_tokens24.tokens.extensions.popover.padding,
4171
+ paddingBottom: import_tokens24.tokens.extensions.popover.headerPaddingBottom,
4172
+ borderBottomWidth: import_tokens24.tokens.border.defaultWidth,
4173
+ borderBottomColor: colors.divider
4174
+ },
4175
+ children: content(header, typeStyle("cardTitle"))
4176
+ }
4177
+ ),
4178
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4179
+ import_react_native28.ScrollView,
4180
+ {
4181
+ ref: scroll,
4182
+ onLayout: (event) => {
4183
+ viewport.current = event.nativeEvent.layout.height;
4184
+ },
4185
+ keyboardShouldPersistTaps: "handled",
4186
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
4187
+ contentContainerStyle: { padding: menu || field ? import_tokens24.tokens.extensions.menu.padding : 0 },
4188
+ onContentSizeChange: (width2, height2) => {
4189
+ if (contentSizedMenu) return;
4190
+ setSized(true);
4191
+ setPanel(
4192
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4193
+ );
4194
+ },
4195
+ children
4196
+ }
4197
+ )
4198
+ ]
4075
4199
  }
4076
4200
  )
4077
4201
  ]
@@ -4097,9 +4221,12 @@ function KjunPopover({
4097
4221
  focusOnOpen = false,
4098
4222
  ariaLabel,
4099
4223
  placement = "bottom",
4224
+ header,
4225
+ modal = true,
4100
4226
  trigger,
4101
4227
  children
4102
4228
  }) {
4229
+ const { colors } = useKjunStyles();
4103
4230
  const [internal, setInternal] = (0, import_react38.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react38.useRef)(null);
4104
4231
  const change = (v) => {
4105
4232
  setInternal(v);
@@ -4128,6 +4255,8 @@ function KjunPopover({
4128
4255
  popover: true,
4129
4256
  focusOnOpen,
4130
4257
  ariaLabel,
4258
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4259
+ nonModal: !modal,
4131
4260
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native29.View, { style: { padding: noPadding ? 0 : import_tokens25.tokens.extensions.popover.padding, gap: noPadding ? 0 : import_tokens25.tokens.extensions.popover.gap }, children: content(
4132
4261
  typeof children === "function" ? children(() => change(false)) : children,
4133
4262
  import_react_native29.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -4142,9 +4271,18 @@ function KjunTooltip({
4142
4271
  delay = 0,
4143
4272
  children,
4144
4273
  onPress,
4145
- disabled
4274
+ disabled,
4275
+ open: controlled,
4276
+ onOpenChange
4146
4277
  }) {
4147
- const { colors } = useKjunStyles(), [open, setOpen] = (0, import_react38.useState)(false), ref = (0, import_react38.useRef)(null), timer = (0, import_react38.useRef)(void 0);
4278
+ const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react38.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react38.useRef)({ open, onOpenChange }), setOpen = (value) => {
4279
+ var _a, _b;
4280
+ if (latest.current.open === value) return;
4281
+ latest.current.open = value;
4282
+ setInternal(value);
4283
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4284
+ }, ref = (0, import_react38.useRef)(null), timer = (0, import_react38.useRef)(void 0);
4285
+ latest.current = { open, onOpenChange };
4148
4286
  const scopeActive = useActiveLayerScope();
4149
4287
  const hide = () => {
4150
4288
  clearTimeout(timer.current);
@@ -5755,7 +5893,7 @@ function KjunTabs({
5755
5893
  opacity: tab.disabled ? import_tokens33.tokens.states.opacity.disabled : 1
5756
5894
  },
5757
5895
  children: [
5758
- tab.icon && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
5896
+ !!tab.icon && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
5759
5897
  KjunIcon,
5760
5898
  {
5761
5899
  name: tab.icon,
@@ -5800,11 +5938,11 @@ function KjunTabs({
5800
5938
  tab.name
5801
5939
  );
5802
5940
  }),
5803
- actions
5941
+ content(actions)
5804
5942
  ]
5805
5943
  }
5806
5944
  ),
5807
- children && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native37.View, { style: { marginTop: geometry2.contentGap }, children })
5945
+ hasContent(children) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native37.View, { style: { marginTop: geometry2.contentGap }, children: content(children) })
5808
5946
  ] }) });
5809
5947
  }
5810
5948
  function KjunTabPane({ name, children }) {
@@ -5932,7 +6070,7 @@ function KjunBreadcrumb({
5932
6070
  },
5933
6071
  children: items.map((item, i) => {
5934
6072
  const label = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens34.tokens.dimension.value4 }, children: [
5935
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(KjunIcon, { name: item.icon, size: import_tokens34.tokens.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
6073
+ !!item.icon && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(KjunIcon, { name: item.icon, size: import_tokens34.tokens.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
5936
6074
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
5937
6075
  KText,
5938
6076
  {
@@ -6248,7 +6386,7 @@ function KjunCopyButton({
6248
6386
  color: copied ? colors.success : colors.textSecondary
6249
6387
  }
6250
6388
  ),
6251
- text && !inline && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
6389
+ !!text && !inline && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
6252
6390
  KText,
6253
6391
  {
6254
6392
  style: {
@@ -6436,7 +6574,7 @@ function KjunDataState({
6436
6574
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6437
6575
  ] }))
6438
6576
  }
6439
- ) : blockingError ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DataStateContentContext.Provider, { value: "message", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native40.View, { style: { paddingVertical: padding }, children: errorContent || /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunAlert, { type: "danger", title: "\uC624\uB958 \uBC1C\uC0DD", actions: onRetry && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunButton, { variant: "secondary", onPress: onRetry, children: retryText }), children: query.error }) }) }) : empty ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DataStateContentContext.Provider, { value: "empty", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native40.View, { style: { paddingVertical: padding }, children: emptyContent || /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunEmpty, { text: emptyText, icon: emptyIcon, children: emptyActionText && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunButton, { onPress: onEmptyAction, children: emptyActionText }) }) }) }) : null,
6577
+ ) : blockingError ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DataStateContentContext.Provider, { value: "message", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native40.View, { style: { paddingVertical: padding }, children: errorContent || /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunAlert, { type: "danger", title: "\uC624\uB958 \uBC1C\uC0DD", actions: onRetry && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunButton, { variant: "secondary", onPress: onRetry, children: retryText }), children: query.error }) }) }) : empty ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DataStateContentContext.Provider, { value: "empty", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native40.View, { style: { paddingVertical: padding }, children: emptyContent || /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunEmpty, { text: emptyText, icon: emptyIcon, children: !!emptyActionText && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KjunButton, { onPress: onEmptyAction, children: emptyActionText }) }) }) }) : null,
6440
6578
  (!concealed || preserveContent && state.hasQueryResult) && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6441
6579
  import_react_native40.View,
6442
6580
  {
@@ -6909,12 +7047,12 @@ function KjunAnimatedNumber({
6909
7047
  style
6910
7048
  ],
6911
7049
  children: [
6912
- prefix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
7050
+ !!prefix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
6913
7051
  prefix,
6914
7052
  " "
6915
7053
  ] }),
6916
7054
  typeof n === "string" ? n : formatter ? formatter(n) : format(n, decimals),
6917
- suffix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
7055
+ !!suffix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
6918
7056
  " ",
6919
7057
  suffix
6920
7058
  ] })
@@ -7488,7 +7626,7 @@ function TableToolbar({
7488
7626
  selectable && selected.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
7489
7627
  import_react_native45.View,
7490
7628
  {
7491
- accessibilityRole: "toolbar",
7629
+ role: "group",
7492
7630
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7493
7631
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7494
7632
  children: [
@@ -7797,7 +7935,7 @@ function TableCards({
7797
7935
  },
7798
7936
  children: [
7799
7937
  selectable && checkbox(row, i),
7800
- title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7938
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7801
7939
  ]
7802
7940
  }
7803
7941
  ),
@@ -7818,7 +7956,7 @@ function TableCards({
7818
7956
  marginTop: index ? import_tokens47.tokens.dimension.value12 : 0
7819
7957
  },
7820
7958
  children: [
7821
- section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7959
+ !!section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7822
7960
  KText,
7823
7961
  {
7824
7962
  style: {
@@ -7883,7 +8021,7 @@ function TableCards({
7883
8021
  children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7884
8022
  }
7885
8023
  ),
7886
- actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
8024
+ !!actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7887
8025
  import_react_native47.View,
7888
8026
  {
7889
8027
  style: {
@@ -8172,7 +8310,7 @@ function KjunKpiHero(props) {
8172
8310
  children: loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(KjunSkeleton, { type: "block", height: typeStyle("numberMd").lineHeight, width: geometry2.deltaSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
8173
8311
  deltaAbsolute !== null && delta(deltaAbsolute, "absolute", { ...numeric, color: direction(deltaAbsolute), ...typeStyle("numberMd") }),
8174
8312
  deltaPercent !== null && delta(deltaPercent, "percent", { ...numeric, color: direction(deltaPercent), ...typeStyle("numberMd"), opacity: 0.9 }),
8175
- deltaDescription && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8313
+ !!deltaDescription && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8176
8314
  KText,
8177
8315
  {
8178
8316
  style: {
@@ -8186,7 +8324,7 @@ function KjunKpiHero(props) {
8186
8324
  }
8187
8325
  )
8188
8326
  ] }),
8189
- aside && !mobile && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native50.View, { children: aside })
8327
+ hasContent(aside) && !mobile && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native50.View, { children: content(aside) })
8190
8328
  ]
8191
8329
  }
8192
8330
  ),
@@ -8248,7 +8386,7 @@ function KjunKpiHero(props) {
8248
8386
  children: kpiFormat(item)
8249
8387
  }
8250
8388
  ),
8251
- item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8389
+ !!item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8252
8390
  KjunSkeleton,
8253
8391
  {
8254
8392
  type: "block",
@@ -8455,7 +8593,7 @@ function KjunKpiRow({
8455
8593
  {
8456
8594
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }],
8457
8595
  children: [
8458
- item.prefix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8596
+ !!item.prefix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8459
8597
  import_react_native51.Text,
8460
8598
  {
8461
8599
  style: {
@@ -8473,7 +8611,7 @@ function KjunKpiRow({
8473
8611
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }]
8474
8612
  }
8475
8613
  ) : kpiFormat(item),
8476
- item.suffix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8614
+ !!item.suffix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8477
8615
  import_react_native51.Text,
8478
8616
  {
8479
8617
  style: {
@@ -8489,7 +8627,7 @@ function KjunKpiRow({
8489
8627
  ]
8490
8628
  }
8491
8629
  ),
8492
- item.desc && (busy ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(KjunSkeleton, { type: "block", height: typeStyle("caption").lineHeight, width: geometry2.descriptionSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8630
+ !!item.desc && (busy ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(KjunSkeleton, { type: "block", height: typeStyle("caption").lineHeight, width: geometry2.descriptionSkeletonWidth }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8493
8631
  KText,
8494
8632
  {
8495
8633
  style: {
@@ -9335,26 +9473,26 @@ var LastListRow = (0, import_react66.createContext)(false);
9335
9473
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
9336
9474
  const { colors } = useKjunStyles(), last = (0, import_react66.useContext)(LastListRow);
9337
9475
  const parts = /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_jsx_runtime54.Fragment, { children: [
9338
- leading && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { children: content(leading) }),
9476
+ hasContent(leading) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { children: content(leading) }),
9339
9477
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flex: 1, minWidth: 0 }, children: [
9340
9478
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { ...typeStyle("label") }, children: title }),
9341
- description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens56.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9479
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens56.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9342
9480
  ] }),
9343
- trailing && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { style: { maxWidth: "35%" }, children: content(trailing) })
9481
+ hasContent(trailing) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { style: { maxWidth: "35%" }, children: content(trailing) })
9344
9482
  ] });
9345
9483
  const style = { flex: 1, minWidth: 0, minHeight: import_tokens56.tokens.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: import_tokens56.tokens.extensions.list.gap, padding: import_tokens56.tokens.extensions.list.padding };
9346
9484
  return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: import_tokens56.tokens.extensions.list.gap, borderBottomWidth: last ? 0 : import_tokens56.tokens.border.defaultWidth, borderBottomColor: colors.divider }, children: [
9347
9485
  onPress || href ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(AccessiblePressable, { ...import_react_native57.Platform.OS === "web" && href && !disabled ? { href } : {}, accessibilityRole: href ? "link" : "button", accessibilityLabel: title + (description ? ", " + description : ""), disabled, accessibilityState: { disabled }, onPress, style: ({ pressed }) => ({ ...style, opacity: disabled ? import_tokens56.tokens.states.opacity.disabled : 1, backgroundColor: pressed ? colors.hover : "transparent" }), children: parts }) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { style, children: parts }),
9348
- actions && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { pointerEvents: disabled ? "none" : "auto", ...disabled && import_react_native57.Platform.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: import_tokens56.tokens.extensions.list.actionsGap, paddingRight: import_tokens56.tokens.extensions.list.actionsPadding, opacity: disabled ? import_tokens56.tokens.states.opacity.disabled : 1 }, children: content(actions) })
9486
+ hasContent(actions) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { pointerEvents: disabled ? "none" : "auto", ...disabled && import_react_native57.Platform.OS === "web" ? { inert: true } : {}, style: { maxWidth: "45%", flexDirection: "row", flexWrap: "wrap", gap: import_tokens56.tokens.extensions.list.actionsGap, paddingRight: import_tokens56.tokens.extensions.list.actionsPadding, opacity: disabled ? import_tokens56.tokens.states.opacity.disabled : 1 }, children: content(actions) })
9349
9487
  ] });
9350
9488
  }
9351
9489
  function KjunListSection({ title, description, actions, children, ariaLabel }) {
9352
9490
  const { colors } = useKjunStyles();
9353
9491
  return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { minWidth: 0, backgroundColor: colors.surface }, children: [
9354
- (title || description || actions) && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens56.tokens.extensions.list.gap, padding: import_tokens56.tokens.extensions.list.padding }, children: [
9492
+ !!(title || description || actions) && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens56.tokens.extensions.list.gap, padding: import_tokens56.tokens.extensions.list.padding }, children: [
9355
9493
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flex: 1 }, children: [
9356
- title && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9357
- description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens56.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9494
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9495
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens56.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9358
9496
  ] }),
9359
9497
  content(actions)
9360
9498
  ] }),
@@ -9382,12 +9520,12 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9382
9520
  const flatten = (node) => import_react67.Children.toArray(node).flatMap((child) => (0, import_react67.isValidElement)(child) && child.type === import_react67.Fragment ? flatten(child.props.children) : [child]);
9383
9521
  const actionList = flatten(actions);
9384
9522
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: geometry2, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { style: { flexShrink: 0, paddingTop: Math.max(0, safeAreaTop), backgroundColor: colors.surface }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { minHeight: geometry2.minimumHeight, flexDirection: "row", flexWrap: "wrap", alignItems: "flex-start", gap: geometry2.gap, paddingVertical: geometry2.paddingY, paddingHorizontal: geometry2.paddingX }, children: [
9385
- leading && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
9523
+ hasContent(leading) && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: { ...geometry2, slot: "leading" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { onLayout: square("leading"), style: { minHeight: geometry2.controlSize, minWidth: 0, maxWidth: "40%", flexShrink: 1, justifyContent: "center", marginLeft: iconEdges.leading ? -edgeInset : 0 }, children: content(leading) }) }),
9386
9524
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { flexGrow: 1, flexShrink: 1, flexBasis: geometry2.titleMinimumWidth, minWidth: 0, paddingVertical: titleInset }, children: [
9387
9525
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { accessibilityRole: "header", style: { ...wrapping, ...typeStyle("sectionTitle"), fontWeight: typeStyle("control").fontWeight }, children: title }),
9388
- description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9526
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9389
9527
  ] }),
9390
- actions && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: { ...geometry2, slot: "actions" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { 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__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { onLayout: square("trailing"), style: { flexShrink: 1, minWidth: 0, maxWidth: "100%", marginRight: iconEdges.trailing ? -edgeInset : 0 }, children: content(action) }, "trailing")) }) })
9528
+ hasContent(actions) && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TopNavigationContext.Provider, { value: { ...geometry2, slot: "actions" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { 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__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { onLayout: square("trailing"), style: { flexShrink: 1, minWidth: 0, maxWidth: "100%", marginRight: iconEdges.trailing ? -edgeInset : 0 }, children: content(action) }, "trailing")) }) })
9391
9529
  ] }) }) });
9392
9530
  }
9393
9531
  function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\uC0C9", safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = true, onNavigate }) {
@@ -9437,7 +9575,7 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9437
9575
  },
9438
9576
  children: [
9439
9577
  hasIcons && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { width: geometry2.iconSize, height: geometry2.iconSize, flexShrink: 0 }, children: [
9440
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9578
+ !!item.icon && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9441
9579
  badge
9442
9580
  ] }),
9443
9581
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { maxWidth: "100%" }, children: [
@@ -9469,7 +9607,7 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9469
9607
  flexDirection: wide ? "row" : "column",
9470
9608
  alignItems: wide ? "center" : "stretch"
9471
9609
  }, children: [
9472
- description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9610
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9473
9611
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native58.View, { style: { flexDirection: "row", flexWrap: "wrap", justifyContent: "flex-end", alignItems: "center", gap: geometry2.actionsGap, minWidth: 0, maxWidth: "100%", flexShrink: 1 }, children: content(children) })
9474
9612
  ] }) });
9475
9613
  }
@@ -9507,7 +9645,7 @@ var import_jsx_runtime57 = require("react/jsx-runtime");
9507
9645
  function KjunChip({ label, icon, removable = false, disabled = false, size = "md", removeLabel, onRemove }) {
9508
9646
  const { colors } = useKjunStyles();
9509
9647
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_react_native60.View, { style: { alignSelf: "flex-start", maxWidth: "100%", minWidth: 0, flexShrink: 1, flexDirection: "row", alignItems: "center", gap: import_tokens59.tokens.extensions.chip.gap, minHeight: removable ? import_tokens59.tokens.extensions.chip.nativeRemoveSize : import_tokens59.tokens.extensions.chip[size], paddingVertical: removable ? 0 : import_tokens59.tokens.extensions.chip.paddingY, paddingLeft: import_tokens59.tokens.extensions.chip.paddingX, paddingRight: removable ? 0 : import_tokens59.tokens.extensions.chip.paddingX, borderRadius: import_tokens59.tokens.extensions.chip.radius, backgroundColor: colors.secondary, opacity: disabled ? import_tokens59.tokens.states.opacity.disabled : 1 }, children: [
9510
- icon && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KjunIcon, { name: icon, size: import_tokens59.tokens.extensions.chip.iconSizes[size] }),
9648
+ !!icon && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KjunIcon, { name: icon, size: import_tokens59.tokens.extensions.chip.iconSizes[size] }),
9511
9649
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KText, { style: { ...typeStyle(import_tokens59.tokens.extensions.chip.typography[size]), flexShrink: 1, minWidth: 0 }, children: label }),
9512
9650
  removable && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(AccessiblePressable, { accessibilityRole: "button", accessibilityLabel: removeLabel || label + " \uC0AD\uC81C", disabled, accessibilityState: { disabled }, onPress: onRemove, style: { width: import_tokens59.tokens.extensions.chip.nativeRemoveSize, height: import_tokens59.tokens.extensions.chip.nativeRemoveSize, borderRadius: import_tokens59.tokens.extensions.chip.radius, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KjunIcon, { name: "x", size: import_tokens59.tokens.extensions.chip.removeIconSizes[size] }) })
9513
9651
  ] });
@@ -9635,7 +9773,7 @@ function RangeInput({
9635
9773
  minWidth: 0
9636
9774
  },
9637
9775
  children: [
9638
- label && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
9776
+ !!label && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
9639
9777
  KText,
9640
9778
  {
9641
9779
  style: {
@@ -9828,7 +9966,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9828
9966
  const { colors, fontFamily } = useKjunStyles();
9829
9967
  const field = useKjunField();
9830
9968
  const invalid = error || !!(field == null ? void 0 : field.error);
9831
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9969
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9832
9970
  const geometry2 = import_tokens61.tokens.extensions.compound;
9833
9971
  const buttonStyle = { width: import_tokens61.tokens.input[size].height, paddingHorizontal: 0 };
9834
9972
  const outer = import_tokens61.tokens.input[size].radius;
@@ -9936,7 +10074,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9936
10074
  nativeID: id || (field == null ? void 0 : field.id),
9937
10075
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9938
10076
  accessibilityLabelledBy: labelledBy,
9939
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
10077
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9940
10078
  role: "spinbutton",
9941
10079
  accessibilityValue: { min, max, now: value },
9942
10080
  accessibilityState: { disabled: blocked },
@@ -9996,6 +10134,6 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9996
10134
  change(null);
9997
10135
  (_a = first.current) == null ? void 0 : _a.focus();
9998
10136
  } }) }),
9999
- (error || !!(field == null ? void 0 : field.error) || focused || opened !== null) && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native63.View, { pointerEvents: "none", "aria-hidden": true, style: { position: "absolute", inset: 0, borderRadius: spec.radius, borderWidth: error || !!(field == null ? void 0 : field.error) ? import_tokens62.tokens.border.controlWidth : import_tokens62.tokens.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
10137
+ !!(error || !!(field == null ? void 0 : field.error) || focused || opened !== null) && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native63.View, { pointerEvents: "none", "aria-hidden": true, style: { position: "absolute", inset: 0, borderRadius: spec.radius, borderWidth: error || !!(field == null ? void 0 : field.error) ? import_tokens62.tokens.border.controlWidth : import_tokens62.tokens.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
10000
10138
  ] });
10001
10139
  }