@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.cjs CHANGED
@@ -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
  }
@@ -783,6 +797,7 @@ function KjunButton(input) {
783
797
  size: declaredSize = "md",
784
798
  loading = false,
785
799
  block = false,
800
+ wrap = false,
786
801
  disabled = false,
787
802
  prefixIcon,
788
803
  suffixIcon,
@@ -803,7 +818,7 @@ function KjunButton(input) {
803
818
  const formActions = (0, import_react12.useContext)(FormActionsContext);
804
819
  const cardActions = (0, import_react12.useContext)(CardActionsContext);
805
820
  const stateContent = (0, import_react12.useContext)(DataStateContentContext);
806
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
821
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
807
822
  const height = Math.max(import_tokens5.tokens.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
808
823
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : import_tokens5.tokens.button.iconSizes[size];
809
824
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -901,7 +916,7 @@ function KjunButton(input) {
901
916
  opacity: overlayLoading ? 0 : 1,
902
917
  ...multiline || navigation ? { flexShrink: 1, minWidth: 0, textAlign: "center" } : {}
903
918
  }, children }),
904
- suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.View, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
919
+ !!suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native3.View, { style: { opacity: busy && prefixIcon ? 0 : 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
905
920
  KjunIcon,
906
921
  {
907
922
  name: busy && !prefixIcon ? "loader-2" : suffixIcon,
@@ -964,6 +979,16 @@ function FieldClear({ size, disabled, label, onPress }) {
964
979
  );
965
980
  }
966
981
 
982
+ // shared/package-runtime/aria-ids.ts
983
+ function mergeIds(...lists) {
984
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
985
+ return ids.length ? ids.join(" ") : void 0;
986
+ }
987
+ function joinHints(...parts) {
988
+ const text = parts.filter(Boolean);
989
+ return text.length ? text.join(". ") : void 0;
990
+ }
991
+
967
992
  // packages/native/src/field-surface.ts
968
993
  var import_react15 = require("react");
969
994
  var import_react_native7 = require("react-native");
@@ -1132,6 +1157,7 @@ function KjunFormGroup({
1132
1157
  required = false,
1133
1158
  error = "",
1134
1159
  hint = "",
1160
+ labelHidden = false,
1135
1161
  children,
1136
1162
  style
1137
1163
  }) {
@@ -1141,9 +1167,17 @@ function KjunFormGroup({
1141
1167
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1142
1168
  FieldContext.Provider,
1143
1169
  {
1144
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1170
+ value: {
1171
+ id: fieldId,
1172
+ label,
1173
+ error,
1174
+ hint,
1175
+ required,
1176
+ labelHidden,
1177
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1178
+ },
1145
1179
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native8.View, { style: [{ minWidth: 0, marginBottom: import_tokens9.tokens.extensions.form.itemGap }, style], children: [
1146
- label && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1180
+ !!label && !labelHidden && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1147
1181
  import_react_native8.Text,
1148
1182
  {
1149
1183
  nativeID: fieldId + "-label",
@@ -1160,7 +1194,15 @@ function KjunFormGroup({
1160
1194
  }
1161
1195
  ),
1162
1196
  children,
1163
- (error || hint) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1197
+ !!hint && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1198
+ import_react_native8.Text,
1199
+ {
1200
+ nativeID: fieldId + "-hint",
1201
+ style: { fontFamily, ...typeStyle("caption"), marginTop: import_tokens9.tokens.extensions.form.messageGap, color: c.textSecondary },
1202
+ children: hint
1203
+ }
1204
+ ),
1205
+ !!error && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1164
1206
  import_react_native8.View,
1165
1207
  {
1166
1208
  style: {
@@ -1170,19 +1212,14 @@ function KjunFormGroup({
1170
1212
  marginTop: import_tokens9.tokens.extensions.form.messageGap
1171
1213
  },
1172
1214
  children: [
1173
- !!error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(KjunIcon, { name: "alert-circle", size: import_tokens9.tokens.extensions.form.messageIconSize, color: c.danger }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(KjunIcon, { name: "alert-circle", size: import_tokens9.tokens.extensions.form.messageIconSize, color: c.danger }),
1174
1216
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1175
1217
  import_react_native8.Text,
1176
1218
  {
1177
- nativeID: fieldId + (error ? "-error" : "-hint"),
1178
- accessibilityRole: error ? "alert" : void 0,
1179
- style: {
1180
- fontFamily,
1181
- ...typeStyle("caption"),
1182
- flexShrink: 1,
1183
- color: error ? c.danger : c.textSecondary
1184
- },
1185
- children: error || hint
1219
+ nativeID: fieldId + "-error",
1220
+ accessibilityRole: "alert",
1221
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1222
+ children: error
1186
1223
  }
1187
1224
  )
1188
1225
  ]
@@ -1232,6 +1269,7 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1232
1269
  const invalid = !!(error || errorMessage || (field == null ? void 0 : field.error));
1233
1270
  const canEdit = !disabled && !readOnly && editable;
1234
1271
  const surface = useFieldSurface(size, { disabled, readOnly: !!readOnly || !editable, invalid, open: props["aria-expanded"] === true });
1272
+ const affixText = { ...inputTypeStyle(size), color: surface.icon };
1235
1273
  const showClear = clearable && !!value;
1236
1274
  const [prefixWidth, setPrefixWidth] = (0, import_react16.useState)(0);
1237
1275
  const [affixWidth, setAffixWidth] = (0, import_react16.useState)(0);
@@ -1256,8 +1294,8 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1256
1294
  editable: canEdit,
1257
1295
  readOnly,
1258
1296
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1259
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1260
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1297
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1298
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1261
1299
  accessibilityState: { disabled },
1262
1300
  ...import_react_native8.Platform.OS === "web" ? {
1263
1301
  // Native Web TextInput does not translate accessibilityState.
@@ -1293,7 +1331,7 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1293
1331
  ]
1294
1332
  }
1295
1333
  ),
1296
- (prefix || prefixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1334
+ !!(prefix || prefixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1297
1335
  import_react_native8.View,
1298
1336
  {
1299
1337
  onLayout: (event) => setPrefixWidth(event.nativeEvent.layout.width),
@@ -1305,7 +1343,7 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1305
1343
  bottom: 0,
1306
1344
  justifyContent: "center"
1307
1345
  },
1308
- children: prefix || /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1346
+ children: prefix ? content(prefix, affixText) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1309
1347
  KjunIcon,
1310
1348
  {
1311
1349
  name: prefixIcon,
@@ -1315,7 +1353,7 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1315
1353
  )
1316
1354
  }
1317
1355
  ),
1318
- (showClear || suffix || suffixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1356
+ !!(showClear || suffix || suffixIcon) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1319
1357
  import_react_native8.View,
1320
1358
  {
1321
1359
  onLayout: (event) => setAffixWidth(event.nativeEvent.layout.width),
@@ -1336,7 +1374,7 @@ var KjunInput = (0, import_react16.forwardRef)(function KjunInput2({
1336
1374
  onClear == null ? void 0 : onClear();
1337
1375
  (_a = input.current) == null ? void 0 : _a.focus();
1338
1376
  } }),
1339
- suffix || suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1377
+ suffix ? content(suffix, affixText) : !!suffixIcon && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1340
1378
  KjunIcon,
1341
1379
  {
1342
1380
  name: suffixIcon,
@@ -1376,6 +1414,17 @@ function KjunFormLayout({
1376
1414
  ) });
1377
1415
  }
1378
1416
 
1417
+ // shared/package-runtime/content-presence.ts
1418
+ var import_react17 = require("react");
1419
+ function hasContent(node) {
1420
+ if (node == null || typeof node === "boolean") return false;
1421
+ if (typeof node === "string") return node.trim().length > 0;
1422
+ if (Array.isArray(node)) return node.some(hasContent);
1423
+ if ((0, import_react17.isValidElement)(node) && node.type === import_react17.Fragment)
1424
+ return hasContent(node.props.children);
1425
+ return true;
1426
+ }
1427
+
1379
1428
  // packages/native/src/layer-backdrop.tsx
1380
1429
  var import_react_native9 = require("react-native");
1381
1430
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -1384,7 +1433,7 @@ function LayerBackdrop({ onPress, style }) {
1384
1433
  }
1385
1434
 
1386
1435
  // packages/native/src/modal.tsx
1387
- var import_react18 = require("react");
1436
+ var import_react19 = require("react");
1388
1437
 
1389
1438
  // shared/package-runtime/form-actions.ts
1390
1439
  function formActionsNeedStack(width, widths, gap) {
@@ -1397,7 +1446,7 @@ var import_tokens10 = require("@kjun-ui/tokens");
1397
1446
  var import_react_native13 = require("react-native");
1398
1447
 
1399
1448
  // packages/native/src/overlay-host.tsx
1400
- var import_react17 = require("react");
1449
+ var import_react18 = require("react");
1401
1450
  var import_react_native11 = require("react-native");
1402
1451
 
1403
1452
  // packages/native/src/modal-window.tsx
@@ -1413,17 +1462,17 @@ var import_jsx_runtime11 = (
1413
1462
  // a new window even when an interrupted exit retained the old surface.
1414
1463
  require("react/jsx-runtime")
1415
1464
  );
1416
- var FeedbackOverlayContext = (0, import_react17.createContext)(
1465
+ var FeedbackOverlayContext = (0, import_react18.createContext)(
1417
1466
  null
1418
1467
  );
1419
1468
  function ScopedNativeModal({ children, visible, ...props }) {
1420
1469
  var _a;
1421
- const layers = useLayerState(), scope = (0, import_react17.useContext)(LayerScope);
1470
+ const layers = useLayerState(), scope = (0, import_react18.useContext)(LayerScope);
1422
1471
  const entry = scope ? layers.entries.get(scope) : void 0;
1423
1472
  const generation = (_a = entry == null ? void 0 : entry.order) != null ? _a : 0;
1424
1473
  const inactive = !!scope && !layers.isActive(scope);
1425
- const host = (0, import_react17.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react17.useId)(), register = host == null ? void 0 : host.register;
1426
- (0, import_react17.useEffect)(
1474
+ const host = (0, import_react18.useContext)(FeedbackOverlayContext), id = "kjun-" + (0, import_react18.useId)(), register = host == null ? void 0 : host.register;
1475
+ (0, import_react18.useEffect)(
1427
1476
  () => visible && register ? register(id) : void 0,
1428
1477
  [visible, register, id, generation]
1429
1478
  );
@@ -1469,7 +1518,7 @@ function KjunFormActions({
1469
1518
  onConfirm
1470
1519
  }) {
1471
1520
  const { fontFamily } = useKjunStyles();
1472
- const [width, setWidth] = (0, import_react18.useState)(0), [measurements, setMeasurements] = (0, import_react18.useState)({});
1521
+ const [width, setWidth] = (0, import_react19.useState)(0), [measurements, setMeasurements] = (0, import_react19.useState)({});
1473
1522
  const labels = [showCancel && { text: cancelText }, showConfirm && { text: confirmText }].filter(Boolean);
1474
1523
  const keyFor = (text) => `${fontFamily}:${size}:${text}`;
1475
1524
  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));
@@ -1570,7 +1619,7 @@ function KjunModal({
1570
1619
  const motion = useNativePresence(open, import_tokens10.tokens.motion.layerEnter, import_tokens10.tokens.motion.layerExit, true, 0, { fade: import_tokens10.tokens.motion.backdropExit });
1571
1620
  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);
1572
1621
  const backdrop = useNativePresence(open, import_tokens10.tokens.motion.backdrop, import_tokens10.tokens.motion.backdropExit, true, 0, { fade: import_tokens10.tokens.motion.backdropExit });
1573
- (0, import_react18.useLayoutEffect)(() => {
1622
+ (0, import_react19.useLayoutEffect)(() => {
1574
1623
  if (!motion.present) {
1575
1624
  offset.stopAnimation();
1576
1625
  offset.setValue(import_tokens10.tokens.motionDistance.modalEnter);
@@ -1660,7 +1709,7 @@ function KjunModal({
1660
1709
  borderBottomColor: c.border
1661
1710
  },
1662
1711
  children: [
1663
- /* @__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)(
1712
+ /* @__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)(
1664
1713
  import_react_native13.Text,
1665
1714
  {
1666
1715
  accessibilityRole: "header",
@@ -1700,7 +1749,7 @@ function KjunModal({
1700
1749
  children
1701
1750
  }
1702
1751
  ),
1703
- (showFooter || footer) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1752
+ (showFooter || hasContent(footer)) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1704
1753
  import_react_native13.View,
1705
1754
  {
1706
1755
  style: {
@@ -1713,7 +1762,7 @@ function KjunModal({
1713
1762
  borderTopWidth: import_tokens10.tokens.border.defaultWidth,
1714
1763
  borderTopColor: c.border
1715
1764
  },
1716
- children: footer || /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1765
+ children: hasContent(footer) ? content(footer) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1717
1766
  KjunFormActions,
1718
1767
  {
1719
1768
  size: footerSize,
@@ -1749,19 +1798,6 @@ var import_tokens11 = require("@kjun-ui/tokens");
1749
1798
  var import_react20 = require("react");
1750
1799
  var import_react_native14 = require("react-native");
1751
1800
  var import_react_native_svg2 = __toESM(require("react-native-svg"), 1);
1752
-
1753
- // shared/package-runtime/content-presence.ts
1754
- var import_react19 = require("react");
1755
- function hasContent(node) {
1756
- if (node == null || typeof node === "boolean") return false;
1757
- if (typeof node === "string") return node.trim().length > 0;
1758
- if (Array.isArray(node)) return node.some(hasContent);
1759
- if ((0, import_react19.isValidElement)(node) && node.type === import_react19.Fragment)
1760
- return hasContent(node.props.children);
1761
- return true;
1762
- }
1763
-
1764
- // packages/native/src/card.tsx
1765
1801
  var import_jsx_runtime13 = require("react/jsx-runtime");
1766
1802
  function KjunCard({
1767
1803
  title,
@@ -1822,8 +1858,8 @@ function KjunCard({
1822
1858
  borderBottomColor: borderColor
1823
1859
  }, children: [
1824
1860
  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: [
1825
- title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1826
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { style: { ...typeStyle("caption"), marginTop: import_tokens11.tokens.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1861
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle"), color: foreground }, children: title }),
1862
+ !!subtitle && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(KText, { style: { ...typeStyle("caption"), marginTop: import_tokens11.tokens.card.subtitleGap, color: colors[paint.description] }, children: subtitle })
1827
1863
  ] }) }),
1828
1864
  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 }) }) })
1829
1865
  ] }),
@@ -1843,6 +1879,13 @@ function KjunCard({
1843
1879
  ] });
1844
1880
  }
1845
1881
 
1882
+ // shared/package-runtime/alert-live.ts
1883
+ function alertRole(tone, live) {
1884
+ if (live === "off") return void 0;
1885
+ if (live) return live === "assertive" ? "alert" : "status";
1886
+ return tone === "danger" ? "alert" : "status";
1887
+ }
1888
+
1846
1889
  // packages/native/src/display-primitives.tsx
1847
1890
  var import_tokens12 = require("@kjun-ui/tokens");
1848
1891
  var import_react21 = require("react");
@@ -1929,9 +1972,9 @@ function KjunEmpty({
1929
1972
  paddingVertical: stateContent === "empty" ? 0 : import_tokens12.tokens.dimension.value32
1930
1973
  },
1931
1974
  children: [
1932
- 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 }) }),
1975
+ !!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 }) }),
1933
1976
  /* @__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 }),
1934
- description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1977
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1935
1978
  KText,
1936
1979
  {
1937
1980
  style: {
@@ -1946,7 +1989,7 @@ function KjunEmpty({
1946
1989
  children: description
1947
1990
  }
1948
1991
  ),
1949
- children && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
1992
+ hasContent(children) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native15.View, { style: { marginTop: spec.actionGap, maxWidth: "100%" }, children: content(children) })
1950
1993
  ]
1951
1994
  }
1952
1995
  );
@@ -2024,7 +2067,7 @@ function KjunSpinner({ size = "md", text }) {
2024
2067
  )
2025
2068
  }
2026
2069
  ),
2027
- text && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KText, { style: { marginLeft: import_tokens12.tokens.dimension.value8, color: colors.textSecondary }, children: text })
2070
+ !!text && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(KText, { style: { marginLeft: import_tokens12.tokens.dimension.value8, color: colors.textSecondary }, children: text })
2028
2071
  ]
2029
2072
  }
2030
2073
  );
@@ -2035,6 +2078,7 @@ function KjunAlert({
2035
2078
  size = "md",
2036
2079
  title,
2037
2080
  closable = false,
2081
+ live,
2038
2082
  children,
2039
2083
  actions,
2040
2084
  onClose
@@ -2051,7 +2095,7 @@ function KjunAlert({
2051
2095
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2052
2096
  import_react_native15.View,
2053
2097
  {
2054
- role: tone === "danger" ? "alert" : "status",
2098
+ role: alertRole(tone, live),
2055
2099
  style: {
2056
2100
  flexDirection: "row",
2057
2101
  alignItems: "flex-start",
@@ -2084,7 +2128,7 @@ function KjunAlert({
2084
2128
  ...typeStyle(small ? "caption" : "body"),
2085
2129
  color: colors.textSecondary
2086
2130
  }) }),
2087
- 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 }) }) })
2131
+ 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) }) }) })
2088
2132
  ] }),
2089
2133
  closable && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2090
2134
  AccessiblePressable,
@@ -2134,7 +2178,7 @@ function KjunProgress({
2134
2178
  const { colors } = useKjunStyles();
2135
2179
  const percentage = Number.isFinite(value) && max > 0 ? Math.min(100, Math.max(0, Math.round(value / max * 100))) : 0;
2136
2180
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native15.View, { children: [
2137
- (label || showLabel) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2181
+ !!(label || showLabel) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2138
2182
  import_react_native15.View,
2139
2183
  {
2140
2184
  style: {
@@ -2549,7 +2593,7 @@ function ToastView({
2549
2593
  children: [
2550
2594
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KjunIcon, { name: icon, color: colors[`${tone}Accent`] }),
2551
2595
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_react_native17.View, { style: { flex: 1, minWidth: 0 }, children: [
2552
- toast.title && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: import_tokens15.tokens.extensions.toast.titleGap }, children: toast.title }),
2596
+ !!toast.title && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KText, { style: { fontWeight: typeStyle("control").fontWeight, marginBottom: import_tokens15.tokens.extensions.toast.titleGap }, children: toast.title }),
2553
2597
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(KText, { style: { color: colors.textSecondary }, children: toast.message }),
2554
2598
  toast.action && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2555
2599
  AccessiblePressable,
@@ -2727,7 +2771,7 @@ function FeedbackDialog({
2727
2771
  onConfirm: submit,
2728
2772
  confirmVariant: options.type === "danger" ? "danger" : "primary",
2729
2773
  children: [
2730
- options.message && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(KText, { style: { marginBottom: import_tokens17.tokens.dimension.value16 }, children: options.message }),
2774
+ !!options.message && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(KText, { style: { marginBottom: import_tokens17.tokens.dimension.value16 }, children: options.message }),
2731
2775
  request.kind === "prompt" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2732
2776
  KjunInput,
2733
2777
  {
@@ -2744,7 +2788,7 @@ function FeedbackDialog({
2744
2788
  }
2745
2789
  );
2746
2790
  }
2747
- function KjunFeedbackProvider({ children }) {
2791
+ function KjunFeedbackProvider({ children, style }) {
2748
2792
  const [controller] = (0, import_react28.useState)(import_tokens17.createFeedbackController);
2749
2793
  const life = (0, import_react28.useRef)(0);
2750
2794
  const snapshot = (0, import_react28.useSyncExternalStore)(
@@ -2774,7 +2818,7 @@ function KjunFeedbackProvider({ children }) {
2774
2818
  {
2775
2819
  value: { top: layers[layers.length - 1], register, render: renderToasts },
2776
2820
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(KjunFeedbackContext.Provider, { value: controller.api, children: [
2777
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: { position: "relative" }, children: [
2821
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native19.View, { style: [{ position: "relative", flexGrow: 1, flexShrink: 1, alignSelf: "stretch" }, style], children: [
2778
2822
  children,
2779
2823
  layers.length === 0 && renderToasts()
2780
2824
  ] }),
@@ -2856,7 +2900,7 @@ function KjunCheckbox({
2856
2900
  )
2857
2901
  }
2858
2902
  ),
2859
- (children || label) && content(children || label, {
2903
+ !!(children || label) && content(children || label, {
2860
2904
  marginLeft: import_tokens18.tokens.extensions.checkbox.labelGap,
2861
2905
  ...typeStyle("label")
2862
2906
  })
@@ -2967,7 +3011,7 @@ function KjunSwitch({
2967
3011
  ]
2968
3012
  }
2969
3013
  ),
2970
- (children || label) && content(children || label, {
3014
+ !!(children || label) && content(children || label, {
2971
3015
  marginLeft: import_tokens18.tokens.extensions.switch.labelGap,
2972
3016
  ...typeStyle("label")
2973
3017
  })
@@ -3044,7 +3088,7 @@ function KjunRadio({
3044
3088
  )
3045
3089
  }
3046
3090
  ),
3047
- (children || label) && content(children || label, {
3091
+ !!(children || label) && content(children || label, {
3048
3092
  marginLeft: geometry2.labelGap,
3049
3093
  ...typeStyle("label")
3050
3094
  })
@@ -3287,7 +3331,7 @@ function KjunButtonGroup({
3287
3331
  opacity: blocked ? import_tokens20.tokens.states.opacity.disabled : 1
3288
3332
  }),
3289
3333
  children: [
3290
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3334
+ !!option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3291
3335
  KjunIcon,
3292
3336
  {
3293
3337
  name: option.icon,
@@ -3384,7 +3428,7 @@ function KjunFilterGroup({
3384
3428
  }
3385
3429
  }
3386
3430
  ),
3387
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3431
+ !!option.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3388
3432
  KjunIcon,
3389
3433
  {
3390
3434
  name: option.icon,
@@ -3472,13 +3516,13 @@ var KjunTextarea = (0, import_react32.forwardRef)(
3472
3516
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3473
3517
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3474
3518
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3475
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3519
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3476
3520
  accessibilityState: { disabled },
3477
3521
  ...import_react_native24.Platform.OS === "web" ? {
3478
3522
  "aria-disabled": disabled,
3479
3523
  "aria-invalid": invalid || void 0,
3480
3524
  "aria-describedby": field == null ? void 0 : field.describedBy,
3481
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3525
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3482
3526
  "aria-required": (field == null ? void 0 : field.required) || void 0
3483
3527
  } : {},
3484
3528
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3515,8 +3559,50 @@ var KjunTextarea = (0, import_react32.forwardRef)(
3515
3559
  var import_tokens22 = require("@kjun-ui/tokens");
3516
3560
  var import_react33 = require("react");
3517
3561
  var import_react_native25 = require("react-native");
3562
+
3563
+ // shared/package-runtime/drawer-geometry.ts
3564
+ function keyboardEvents(os) {
3565
+ return os === "ios" ? { show: "keyboardWillShow", hide: "keyboardWillHide" } : null;
3566
+ }
3567
+ function keyboardOverlap(windowHeight, keyboardScreenY) {
3568
+ return Math.max(0, windowHeight - keyboardScreenY);
3569
+ }
3570
+ function drawerFrame({ position, windowHeight, safeAreaTop, safeAreaBottom, gap, keyboard, maxHeight }) {
3571
+ const visible = windowHeight - keyboard;
3572
+ const bottomInset = keyboard > 0 ? 0 : safeAreaBottom;
3573
+ if (position === "left" || position === "right")
3574
+ return { maxHeight: visible, height: visible, paddingTop: safeAreaTop, paddingBottom: bottomInset, lift: 0 };
3575
+ const sheet = maxHeight != null ? maxHeight : visible - (position === "bottom" ? safeAreaTop : bottomInset) - gap;
3576
+ return {
3577
+ maxHeight: Math.max(0, Math.min(sheet, visible)),
3578
+ height: void 0,
3579
+ paddingTop: position === "top" ? safeAreaTop : 0,
3580
+ paddingBottom: position === "bottom" ? bottomInset : 0,
3581
+ lift: position === "bottom" ? keyboard : 0
3582
+ };
3583
+ }
3584
+
3585
+ // packages/native/src/drawer.tsx
3518
3586
  var import_react_native26 = require("react-native");
3519
3587
  var import_jsx_runtime23 = require("react/jsx-runtime");
3588
+ function useKeyboardOverlap(enabled, windowHeight) {
3589
+ const [overlap, setOverlap] = (0, import_react33.useState)(0);
3590
+ (0, import_react33.useEffect)(() => {
3591
+ var _a, _b;
3592
+ setOverlap(0);
3593
+ const events = keyboardEvents(import_react_native26.Platform.OS);
3594
+ if (!enabled || !events) return;
3595
+ const metrics = (_b = (_a = import_react_native25.Keyboard).metrics) == null ? void 0 : _b.call(_a);
3596
+ if (metrics) setOverlap(keyboardOverlap(windowHeight, metrics.screenY));
3597
+ const show = import_react_native25.Keyboard.addListener(events.show, (event) => setOverlap(keyboardOverlap(windowHeight, event.endCoordinates.screenY)));
3598
+ const hide = import_react_native25.Keyboard.addListener(events.hide, () => setOverlap(0));
3599
+ return () => {
3600
+ show.remove();
3601
+ hide.remove();
3602
+ };
3603
+ }, [enabled, windowHeight]);
3604
+ return overlap;
3605
+ }
3520
3606
  function KjunDrawer({
3521
3607
  open,
3522
3608
  onOpenChange,
@@ -3531,9 +3617,24 @@ function KjunDrawer({
3531
3617
  footer,
3532
3618
  children,
3533
3619
  onClose,
3534
- ariaLabel
3620
+ ariaLabel,
3621
+ safeAreaTop = 0,
3622
+ safeAreaBottom = 0,
3623
+ maxHeight,
3624
+ avoidKeyboard = true
3535
3625
  }) {
3536
3626
  const { colors } = useKjunStyles(), window2 = (0, import_react_native26.useWindowDimensions)(), vertical = position === "left" || position === "right";
3627
+ const [rootHeight, setRootHeight] = (0, import_react33.useState)(0);
3628
+ const keyboard = useKeyboardOverlap(open && avoidKeyboard, window2.height);
3629
+ const frame = drawerFrame({
3630
+ position,
3631
+ windowHeight: rootHeight || window2.height,
3632
+ safeAreaTop: Math.max(0, safeAreaTop),
3633
+ safeAreaBottom: Math.max(0, safeAreaBottom),
3634
+ gap: import_tokens22.tokens.extensions.drawer.sheetGap,
3635
+ keyboard,
3636
+ maxHeight
3637
+ });
3537
3638
  const [panelHeight, setPanelHeight] = (0, import_react33.useState)(0);
3538
3639
  const motion = useNativePresence(open, import_tokens22.tokens.motion.layerEnter, import_tokens22.tokens.motion.layerExit, vertical || panelHeight > 0, 0, { enterEasing: motionEmphasized });
3539
3640
  const backdrop = useNativePresence(open, import_tokens22.tokens.motion.backdrop, import_tokens22.tokens.motion.backdropExit, true, 0, { fade: import_tokens22.tokens.motion.backdropExit });
@@ -3557,6 +3658,7 @@ function KjunDrawer({
3557
3658
  children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3558
3659
  import_react_native26.View,
3559
3660
  {
3661
+ onLayout: (event) => setRootHeight(event.nativeEvent.layout.height),
3560
3662
  style: {
3561
3663
  flex: 1,
3562
3664
  justifyContent: position === "bottom" ? "flex-end" : "flex-start",
@@ -3595,8 +3697,11 @@ function KjunDrawer({
3595
3697
  opacity: vertical || panelHeight > 0 ? 1 : 0,
3596
3698
  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] }) }],
3597
3699
  width: vertical ? Math.min(width, window2.width) : window2.width,
3598
- height: vertical ? window2.height : void 0,
3599
- maxHeight: window2.height,
3700
+ height: frame.height,
3701
+ maxHeight: frame.maxHeight,
3702
+ paddingTop: frame.paddingTop,
3703
+ paddingBottom: frame.paddingBottom,
3704
+ marginBottom: frame.lift,
3600
3705
  borderRadius: import_tokens22.tokens.extensions.drawer.radius,
3601
3706
  borderTopLeftRadius: position === "bottom" ? import_tokens22.tokens.extensions.drawer.bottomRadius : import_tokens22.tokens.extensions.drawer.radius,
3602
3707
  borderTopRightRadius: position === "bottom" ? import_tokens22.tokens.extensions.drawer.bottomRadius : import_tokens22.tokens.extensions.drawer.radius,
@@ -3604,7 +3709,7 @@ function KjunDrawer({
3604
3709
  backgroundColor: colors.surface
3605
3710
  },
3606
3711
  children: [
3607
- (title || header || closable) && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3712
+ !!(title || header || closable) && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
3608
3713
  import_react_native26.View,
3609
3714
  {
3610
3715
  style: {
@@ -3617,7 +3722,7 @@ function KjunDrawer({
3617
3722
  borderBottomColor: colors.border
3618
3723
  },
3619
3724
  children: [
3620
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.View, { style: { flex: 1 }, children: header || /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3725
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native26.View, { style: { flex: 1 }, children: hasContent(header) ? content(header) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3621
3726
  KText,
3622
3727
  {
3623
3728
  style: { ...typeStyle("sectionTitle") },
@@ -3642,12 +3747,12 @@ function KjunDrawer({
3642
3747
  import_react_native26.ScrollView,
3643
3748
  {
3644
3749
  keyboardShouldPersistTaps: "handled",
3645
- style: vertical ? { flex: 1 } : { flexGrow: 0 },
3750
+ style: vertical ? { flex: 1 } : { flexGrow: 0, flexShrink: 1 },
3646
3751
  contentContainerStyle: { padding: noPadding ? 0 : import_tokens22.tokens.extensions.drawer.bodyPadding },
3647
3752
  children: content(children)
3648
3753
  }
3649
3754
  ),
3650
- footer && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3755
+ hasContent(footer) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3651
3756
  import_react_native26.View,
3652
3757
  {
3653
3758
  style: {
@@ -3717,8 +3822,11 @@ function FloatingPanel({
3717
3822
  flip = true,
3718
3823
  minimumWidth = 0,
3719
3824
  focusOnOpen = false,
3720
- reveal
3825
+ reveal,
3826
+ header,
3827
+ nonModal = false
3721
3828
  }) {
3829
+ const [headerHeight, setHeaderHeight] = (0, import_react34.useState)(0);
3722
3830
  const panelRef = (0, import_react34.useRef)(null);
3723
3831
  const scroll = (0, import_react34.useRef)(null), viewport = (0, import_react34.useRef)(0);
3724
3832
  const { colors } = useKjunStyles(), window2 = (0, import_react_native28.useWindowDimensions)(), [anchored, setAnchored] = (0, import_react34.useState)(false), [sized, setSized] = (0, import_react34.useState)(false), [anchor, setAnchor] = (0, import_react34.useState)({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = (0, import_react34.useState)({ width: 0, height: 0 });
@@ -3773,7 +3881,7 @@ function FloatingPanel({
3773
3881
  maximumWidth,
3774
3882
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : import_tokens23.tokens.extensions.popover.width)
3775
3883
  );
3776
- const available = Math.min(maxHeight != null ? maxHeight : field ? import_tokens23.tokens.extensions.menu.listMaxHeight : import_tokens23.tokens.extensions.floating.maxHeight, window2.height - 2 * import_tokens23.tokens.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - import_tokens23.tokens.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - import_tokens23.tokens.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3884
+ const available = Math.min(maxHeight != null ? maxHeight : field ? import_tokens23.tokens.extensions.menu.listMaxHeight : import_tokens23.tokens.extensions.floating.maxHeight, window2.height - 2 * import_tokens23.tokens.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - import_tokens23.tokens.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - import_tokens23.tokens.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3777
3885
  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;
3778
3886
  if (placement === "left" || placement === "right") {
3779
3887
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -3857,7 +3965,7 @@ function FloatingPanel({
3857
3965
  }
3858
3966
  }
3859
3967
  ),
3860
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3968
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3861
3969
  import_react_native27.Animated.View,
3862
3970
  {
3863
3971
  ref: panelRef,
@@ -3868,7 +3976,7 @@ function FloatingPanel({
3868
3976
  pointerEvents: open && anchored && sized ? "auto" : "none",
3869
3977
  accessibilityElementsHidden: !open || !anchored || !sized,
3870
3978
  ...!open || !anchored || !sized ? { inert: true } : {},
3871
- accessibilityViewIsModal: true,
3979
+ accessibilityViewIsModal: !nonModal,
3872
3980
  accessibilityLabel: ariaLabel,
3873
3981
  style: {
3874
3982
  opacity: anchored && sized ? motion.opacity : 0,
@@ -3890,26 +3998,42 @@ function FloatingPanel({
3890
3998
  boxShadow: (tooltip ? import_tokens23.tokens.extensions.tooltip.elevation : popover ? import_tokens23.tokens.extensions.popover.elevation : import_tokens23.tokens.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
3891
3999
  overflow: "hidden"
3892
4000
  },
3893
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3894
- import_react_native28.ScrollView,
3895
- {
3896
- ref: scroll,
3897
- onLayout: (event) => {
3898
- viewport.current = event.nativeEvent.layout.height;
3899
- },
3900
- keyboardShouldPersistTaps: "handled",
3901
- style: { maxHeight: available },
3902
- contentContainerStyle: { padding: menu || field ? import_tokens23.tokens.extensions.menu.padding : 0 },
3903
- onContentSizeChange: (width2, height2) => {
3904
- if (contentSizedMenu) return;
3905
- setSized(true);
3906
- setPanel(
3907
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
3908
- );
3909
- },
3910
- children
3911
- }
3912
- )
4001
+ children: [
4002
+ header != null && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4003
+ import_react_native28.View,
4004
+ {
4005
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
4006
+ style: {
4007
+ paddingTop: import_tokens23.tokens.extensions.popover.padding,
4008
+ paddingHorizontal: import_tokens23.tokens.extensions.popover.padding,
4009
+ paddingBottom: import_tokens23.tokens.extensions.popover.headerPaddingBottom,
4010
+ borderBottomWidth: import_tokens23.tokens.border.defaultWidth,
4011
+ borderBottomColor: colors.divider
4012
+ },
4013
+ children: content(header, typeStyle("cardTitle"))
4014
+ }
4015
+ ),
4016
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4017
+ import_react_native28.ScrollView,
4018
+ {
4019
+ ref: scroll,
4020
+ onLayout: (event) => {
4021
+ viewport.current = event.nativeEvent.layout.height;
4022
+ },
4023
+ keyboardShouldPersistTaps: "handled",
4024
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
4025
+ contentContainerStyle: { padding: menu || field ? import_tokens23.tokens.extensions.menu.padding : 0 },
4026
+ onContentSizeChange: (width2, height2) => {
4027
+ if (contentSizedMenu) return;
4028
+ setSized(true);
4029
+ setPanel(
4030
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4031
+ );
4032
+ },
4033
+ children
4034
+ }
4035
+ )
4036
+ ]
3913
4037
  }
3914
4038
  )
3915
4039
  ]
@@ -3935,9 +4059,12 @@ function KjunPopover({
3935
4059
  focusOnOpen = false,
3936
4060
  ariaLabel,
3937
4061
  placement = "bottom",
4062
+ header,
4063
+ modal = true,
3938
4064
  trigger,
3939
4065
  children
3940
4066
  }) {
4067
+ const { colors } = useKjunStyles();
3941
4068
  const [internal, setInternal] = (0, import_react35.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react35.useRef)(null);
3942
4069
  const change = (v) => {
3943
4070
  setInternal(v);
@@ -3966,6 +4093,8 @@ function KjunPopover({
3966
4093
  popover: true,
3967
4094
  focusOnOpen,
3968
4095
  ariaLabel,
4096
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4097
+ nonModal: !modal,
3969
4098
  children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native29.View, { style: { padding: noPadding ? 0 : import_tokens24.tokens.extensions.popover.padding, gap: noPadding ? 0 : import_tokens24.tokens.extensions.popover.gap }, children: content(
3970
4099
  typeof children === "function" ? children(() => change(false)) : children,
3971
4100
  import_react_native29.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -3980,9 +4109,18 @@ function KjunTooltip({
3980
4109
  delay = 0,
3981
4110
  children,
3982
4111
  onPress,
3983
- disabled
4112
+ disabled,
4113
+ open: controlled,
4114
+ onOpenChange
3984
4115
  }) {
3985
- const { colors } = useKjunStyles(), [open, setOpen] = (0, import_react35.useState)(false), ref = (0, import_react35.useRef)(null), timer = (0, import_react35.useRef)(void 0);
4116
+ const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react35.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react35.useRef)({ open, onOpenChange }), setOpen = (value) => {
4117
+ var _a, _b;
4118
+ if (latest.current.open === value) return;
4119
+ latest.current.open = value;
4120
+ setInternal(value);
4121
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4122
+ }, ref = (0, import_react35.useRef)(null), timer = (0, import_react35.useRef)(void 0);
4123
+ latest.current = { open, onOpenChange };
3986
4124
  const scopeActive = useActiveLayerScope();
3987
4125
  const hide = () => {
3988
4126
  clearTimeout(timer.current);
@@ -5500,7 +5638,7 @@ function KjunTabs({
5500
5638
  opacity: tab.disabled ? import_tokens32.tokens.states.opacity.disabled : 1
5501
5639
  },
5502
5640
  children: [
5503
- tab.icon && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
5641
+ !!tab.icon && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
5504
5642
  KjunIcon,
5505
5643
  {
5506
5644
  name: tab.icon,
@@ -5545,11 +5683,11 @@ function KjunTabs({
5545
5683
  tab.name
5546
5684
  );
5547
5685
  }),
5548
- actions
5686
+ content(actions)
5549
5687
  ]
5550
5688
  }
5551
5689
  ),
5552
- children && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native37.View, { style: { marginTop: geometry2.contentGap }, children })
5690
+ hasContent(children) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native37.View, { style: { marginTop: geometry2.contentGap }, children: content(children) })
5553
5691
  ] }) });
5554
5692
  }
5555
5693
  function KjunTabPane({ name, children }) {
@@ -5677,7 +5815,7 @@ function KjunBreadcrumb({
5677
5815
  },
5678
5816
  children: items.map((item, i) => {
5679
5817
  const label = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens33.tokens.dimension.value4 }, children: [
5680
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(KjunIcon, { name: item.icon, size: import_tokens33.tokens.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
5818
+ !!item.icon && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(KjunIcon, { name: item.icon, size: import_tokens33.tokens.extensions.breadcrumb.iconSize, color: colors.textSecondary }),
5681
5819
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
5682
5820
  KText,
5683
5821
  {
@@ -5993,7 +6131,7 @@ function KjunCopyButton({
5993
6131
  color: copied ? colors.success : colors.textSecondary
5994
6132
  }
5995
6133
  ),
5996
- text && !inline && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
6134
+ !!text && !inline && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
5997
6135
  KText,
5998
6136
  {
5999
6137
  style: {
@@ -6181,7 +6319,7 @@ function KjunDataState({
6181
6319
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(KText, { style: { ...typeStyle("body"), color: colors.textSecondary, textAlign: "center", maxWidth: "100%" }, children: loadingText })
6182
6320
  ] }))
6183
6321
  }
6184
- ) : 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,
6322
+ ) : 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,
6185
6323
  (!concealed || preserveContent && state.hasQueryResult) && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
6186
6324
  import_react_native40.View,
6187
6325
  {
@@ -6654,12 +6792,12 @@ function KjunAnimatedNumber({
6654
6792
  style
6655
6793
  ],
6656
6794
  children: [
6657
- prefix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
6795
+ !!prefix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
6658
6796
  prefix,
6659
6797
  " "
6660
6798
  ] }),
6661
6799
  typeof n === "string" ? n : formatter ? formatter(n) : format(n, decimals),
6662
- suffix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
6800
+ !!suffix && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native43.Text, { style: typeStyle("caption"), children: [
6663
6801
  " ",
6664
6802
  suffix
6665
6803
  ] })
@@ -7233,7 +7371,7 @@ function TableToolbar({
7233
7371
  selectable && selected.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
7234
7372
  import_react_native45.View,
7235
7373
  {
7236
- accessibilityRole: "toolbar",
7374
+ role: "group",
7237
7375
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7238
7376
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7239
7377
  children: [
@@ -7542,7 +7680,7 @@ function TableCards({
7542
7680
  },
7543
7681
  children: [
7544
7682
  selectable && checkbox(row, i),
7545
- title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7683
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native47.View, { style: { flex: 1, minWidth: 0 }, children: cell(title, row, i, "title") })
7546
7684
  ]
7547
7685
  }
7548
7686
  ),
@@ -7563,7 +7701,7 @@ function TableCards({
7563
7701
  marginTop: index ? import_tokens46.tokens.dimension.value12 : 0
7564
7702
  },
7565
7703
  children: [
7566
- section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7704
+ !!section.label && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7567
7705
  KText,
7568
7706
  {
7569
7707
  style: {
@@ -7628,7 +7766,7 @@ function TableCards({
7628
7766
  children: content(renderExpand == null ? void 0 : renderExpand(row, i))
7629
7767
  }
7630
7768
  ),
7631
- actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7769
+ !!actions && !actions.inlineInCard && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
7632
7770
  import_react_native47.View,
7633
7771
  {
7634
7772
  style: {
@@ -7917,7 +8055,7 @@ function KjunKpiHero(props) {
7917
8055
  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: [
7918
8056
  deltaAbsolute !== null && delta(deltaAbsolute, "absolute", { ...numeric, color: direction(deltaAbsolute), ...typeStyle("numberMd") }),
7919
8057
  deltaPercent !== null && delta(deltaPercent, "percent", { ...numeric, color: direction(deltaPercent), ...typeStyle("numberMd"), opacity: 0.9 }),
7920
- deltaDescription && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8058
+ !!deltaDescription && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7921
8059
  KText,
7922
8060
  {
7923
8061
  style: {
@@ -7931,7 +8069,7 @@ function KjunKpiHero(props) {
7931
8069
  }
7932
8070
  )
7933
8071
  ] }),
7934
- aside && !mobile && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native50.View, { children: aside })
8072
+ hasContent(aside) && !mobile && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native50.View, { children: content(aside) })
7935
8073
  ]
7936
8074
  }
7937
8075
  ),
@@ -7993,7 +8131,7 @@ function KjunKpiHero(props) {
7993
8131
  children: kpiFormat(item)
7994
8132
  }
7995
8133
  ),
7996
- item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
8134
+ !!item.desc && (loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
7997
8135
  KjunSkeleton,
7998
8136
  {
7999
8137
  type: "block",
@@ -8200,7 +8338,7 @@ function KjunKpiRow({
8200
8338
  {
8201
8339
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }],
8202
8340
  children: [
8203
- item.prefix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8341
+ !!item.prefix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8204
8342
  import_react_native51.Text,
8205
8343
  {
8206
8344
  style: {
@@ -8218,7 +8356,7 @@ function KjunKpiRow({
8218
8356
  style: [valueStyle, { flexShrink: 1, minWidth: 0 }]
8219
8357
  }
8220
8358
  ) : kpiFormat(item),
8221
- item.suffix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8359
+ !!item.suffix && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
8222
8360
  import_react_native51.Text,
8223
8361
  {
8224
8362
  style: {
@@ -8234,7 +8372,7 @@ function KjunKpiRow({
8234
8372
  ]
8235
8373
  }
8236
8374
  ),
8237
- 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)(
8375
+ !!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)(
8238
8376
  KText,
8239
8377
  {
8240
8378
  style: {
@@ -9080,26 +9218,26 @@ var LastListRow = (0, import_react62.createContext)(false);
9080
9218
  function KjunListRow({ title, description, leading, trailing, actions, href, disabled = false, onPress }) {
9081
9219
  const { colors } = useKjunStyles(), last = (0, import_react62.useContext)(LastListRow);
9082
9220
  const parts = /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_jsx_runtime54.Fragment, { children: [
9083
- leading && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { children: content(leading) }),
9221
+ hasContent(leading) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { children: content(leading) }),
9084
9222
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flex: 1, minWidth: 0 }, children: [
9085
9223
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { ...typeStyle("label") }, children: title }),
9086
- description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens55.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9224
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens55.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9087
9225
  ] }),
9088
- trailing && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { style: { maxWidth: "35%" }, children: content(trailing) })
9226
+ hasContent(trailing) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native57.View, { style: { maxWidth: "35%" }, children: content(trailing) })
9089
9227
  ] });
9090
9228
  const style = { flex: 1, minWidth: 0, minHeight: import_tokens55.tokens.extensions.list.minimumHeight, flexDirection: "row", alignItems: "center", gap: import_tokens55.tokens.extensions.list.gap, padding: import_tokens55.tokens.extensions.list.padding };
9091
9229
  return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { role: "listitem", style: { flexDirection: "row", alignItems: "center", gap: import_tokens55.tokens.extensions.list.gap, borderBottomWidth: last ? 0 : import_tokens55.tokens.border.defaultWidth, borderBottomColor: colors.divider }, children: [
9092
9230
  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_tokens55.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 }),
9093
- 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_tokens55.tokens.extensions.list.actionsGap, paddingRight: import_tokens55.tokens.extensions.list.actionsPadding, opacity: disabled ? import_tokens55.tokens.states.opacity.disabled : 1 }, children: content(actions) })
9231
+ 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_tokens55.tokens.extensions.list.actionsGap, paddingRight: import_tokens55.tokens.extensions.list.actionsPadding, opacity: disabled ? import_tokens55.tokens.states.opacity.disabled : 1 }, children: content(actions) })
9094
9232
  ] });
9095
9233
  }
9096
9234
  function KjunListSection({ title, description, actions, children, ariaLabel }) {
9097
9235
  const { colors } = useKjunStyles();
9098
9236
  return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { minWidth: 0, backgroundColor: colors.surface }, children: [
9099
- (title || description || actions) && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens55.tokens.extensions.list.gap, padding: import_tokens55.tokens.extensions.list.padding }, children: [
9237
+ !!(title || description || actions) && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", gap: import_tokens55.tokens.extensions.list.gap, padding: import_tokens55.tokens.extensions.list.padding }, children: [
9100
9238
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native57.View, { style: { flex: 1 }, children: [
9101
- title && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9102
- description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens55.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9239
+ !!title && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { accessibilityRole: "header", style: { ...typeStyle("cardTitle") }, children: title }),
9240
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(KText, { style: { marginTop: import_tokens55.tokens.extensions.list.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9103
9241
  ] }),
9104
9242
  content(actions)
9105
9243
  ] }),
@@ -9127,12 +9265,12 @@ function KjunTopNavigation({ title, description, leading, actions, safeAreaTop =
9127
9265
  const flatten = (node) => import_react63.Children.toArray(node).flatMap((child) => (0, import_react63.isValidElement)(child) && child.type === import_react63.Fragment ? flatten(child.props.children) : [child]);
9128
9266
  const actionList = flatten(actions);
9129
9267
  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: [
9130
- 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) }) }),
9268
+ 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) }) }),
9131
9269
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { flexGrow: 1, flexShrink: 1, flexBasis: geometry2.titleMinimumWidth, minWidth: 0, paddingVertical: titleInset }, children: [
9132
9270
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { accessibilityRole: "header", style: { ...wrapping, ...typeStyle("sectionTitle"), fontWeight: typeStyle("control").fontWeight }, children: title }),
9133
- description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9271
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...wrapping, marginTop: geometry2.descriptionGap, color: colors.textSecondary, ...typeStyle("caption") }, children: description })
9134
9272
  ] }),
9135
- 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")) }) })
9273
+ 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")) }) })
9136
9274
  ] }) }) });
9137
9275
  }
9138
9276
  function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\uC0C9", safeAreaBottom = 0, keyboardVisible = false, hideOnKeyboard = true, onNavigate }) {
@@ -9182,7 +9320,7 @@ function KjunBottomNavigation({ items, value, ariaLabel = "\uC8FC\uC694 \uD0D0\u
9182
9320
  },
9183
9321
  children: [
9184
9322
  hasIcons && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { width: geometry2.iconSize, height: geometry2.iconSize, flexShrink: 0 }, children: [
9185
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9323
+ !!item.icon && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KjunIcon, { name: item.icon, size: geometry2.iconSize, color: ink }),
9186
9324
  badge
9187
9325
  ] }),
9188
9326
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native58.View, { style: { maxWidth: "100%" }, children: [
@@ -9214,7 +9352,7 @@ function KjunBottomActionBar({ description, children, safeAreaBottom = 0, keyboa
9214
9352
  flexDirection: wide ? "row" : "column",
9215
9353
  alignItems: wide ? "center" : "stretch"
9216
9354
  }, children: [
9217
- description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9355
+ !!description && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(KText, { style: { ...typeStyle("body"), ...wrapping, color: colors.textSecondary, flex: wide ? 1 : void 0, minWidth: 0 }, children: description }),
9218
9356
  /* @__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) })
9219
9357
  ] }) });
9220
9358
  }
@@ -9252,7 +9390,7 @@ var import_jsx_runtime57 = require("react/jsx-runtime");
9252
9390
  function KjunChip({ label, icon, removable = false, disabled = false, size = "md", removeLabel, onRemove }) {
9253
9391
  const { colors } = useKjunStyles();
9254
9392
  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_tokens58.tokens.extensions.chip.gap, minHeight: removable ? import_tokens58.tokens.extensions.chip.nativeRemoveSize : import_tokens58.tokens.extensions.chip[size], paddingVertical: removable ? 0 : import_tokens58.tokens.extensions.chip.paddingY, paddingLeft: import_tokens58.tokens.extensions.chip.paddingX, paddingRight: removable ? 0 : import_tokens58.tokens.extensions.chip.paddingX, borderRadius: import_tokens58.tokens.extensions.chip.radius, backgroundColor: colors.secondary, opacity: disabled ? import_tokens58.tokens.states.opacity.disabled : 1 }, children: [
9255
- icon && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KjunIcon, { name: icon, size: import_tokens58.tokens.extensions.chip.iconSizes[size] }),
9393
+ !!icon && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KjunIcon, { name: icon, size: import_tokens58.tokens.extensions.chip.iconSizes[size] }),
9256
9394
  /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KText, { style: { ...typeStyle(import_tokens58.tokens.extensions.chip.typography[size]), flexShrink: 1, minWidth: 0 }, children: label }),
9257
9395
  removable && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(AccessiblePressable, { accessibilityRole: "button", accessibilityLabel: removeLabel || label + " \uC0AD\uC81C", disabled, accessibilityState: { disabled }, onPress: onRemove, style: { width: import_tokens58.tokens.extensions.chip.nativeRemoveSize, height: import_tokens58.tokens.extensions.chip.nativeRemoveSize, borderRadius: import_tokens58.tokens.extensions.chip.radius, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(KjunIcon, { name: "x", size: import_tokens58.tokens.extensions.chip.removeIconSizes[size] }) })
9258
9396
  ] });
@@ -9380,7 +9518,7 @@ function RangeInput({
9380
9518
  minWidth: 0
9381
9519
  },
9382
9520
  children: [
9383
- label && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
9521
+ !!label && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
9384
9522
  KText,
9385
9523
  {
9386
9524
  style: {
@@ -9573,7 +9711,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9573
9711
  const { colors, fontFamily } = useKjunStyles();
9574
9712
  const field = useKjunField();
9575
9713
  const invalid = error || !!(field == null ? void 0 : field.error);
9576
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9714
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9577
9715
  const geometry2 = import_tokens60.tokens.extensions.compound;
9578
9716
  const buttonStyle = { width: import_tokens60.tokens.input[size].height, paddingHorizontal: 0 };
9579
9717
  const outer = import_tokens60.tokens.input[size].radius;
@@ -9681,7 +9819,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9681
9819
  nativeID: id || (field == null ? void 0 : field.id),
9682
9820
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9683
9821
  accessibilityLabelledBy: labelledBy,
9684
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
9822
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9685
9823
  role: "spinbutton",
9686
9824
  accessibilityValue: { min, max, now: value },
9687
9825
  accessibilityState: { disabled: blocked },
@@ -9741,6 +9879,6 @@ function KjunTimePicker({ value, precision = "minute", min, max, minuteStep = 1,
9741
9879
  change(null);
9742
9880
  (_a = first.current) == null ? void 0 : _a.focus();
9743
9881
  } }) }),
9744
- (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_tokens61.tokens.border.controlWidth : import_tokens61.tokens.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
9882
+ !!(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_tokens61.tokens.border.controlWidth : import_tokens61.tokens.states.focus.width, borderColor: error || (field == null ? void 0 : field.error) ? colors.danger : colors.focusRing } })
9745
9883
  ] });
9746
9884
  }