@kjun-ui/native 0.9.0 → 0.10.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 !== "";
@@ -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
  ]
@@ -1336,8 +1373,8 @@ var KjunInput = (0, import_react17.forwardRef)(function KjunInput2({
1336
1373
  editable: canEdit,
1337
1374
  readOnly,
1338
1375
  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,
1376
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1377
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1341
1378
  accessibilityState: { disabled },
1342
1379
  ...import_react_native8.Platform.OS === "web" ? {
1343
1380
  // Native Web TextInput does not translate accessibilityState.
@@ -1938,6 +1975,13 @@ function KjunCard({
1938
1975
  ] });
1939
1976
  }
1940
1977
 
1978
+ // shared/package-runtime/alert-live.ts
1979
+ function alertRole(tone, live) {
1980
+ if (live === "off") return void 0;
1981
+ if (live) return live === "assertive" ? "alert" : "status";
1982
+ return tone === "danger" ? "alert" : "status";
1983
+ }
1984
+
1941
1985
  // packages/native/src/display-primitives.tsx
1942
1986
  var import_tokens12 = require("@kjun-ui/tokens");
1943
1987
  var import_react23 = require("react");
@@ -2130,6 +2174,7 @@ function KjunAlert({
2130
2174
  size = "md",
2131
2175
  title,
2132
2176
  closable = false,
2177
+ live,
2133
2178
  children,
2134
2179
  actions,
2135
2180
  onClose
@@ -2146,7 +2191,7 @@ function KjunAlert({
2146
2191
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2147
2192
  import_react_native15.View,
2148
2193
  {
2149
- role: tone === "danger" ? "alert" : "status",
2194
+ role: alertRole(tone, live),
2150
2195
  style: {
2151
2196
  flexDirection: "row",
2152
2197
  alignItems: "flex-start",
@@ -3634,13 +3679,13 @@ var KjunTextarea = (0, import_react35.forwardRef)(
3634
3679
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3635
3680
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3636
3681
  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,
3682
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3638
3683
  accessibilityState: { disabled },
3639
3684
  ...import_react_native24.Platform.OS === "web" ? {
3640
3685
  "aria-disabled": disabled,
3641
3686
  "aria-invalid": invalid || void 0,
3642
3687
  "aria-describedby": field == null ? void 0 : field.describedBy,
3643
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3688
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3644
3689
  "aria-required": (field == null ? void 0 : field.required) || void 0
3645
3690
  } : {},
3646
3691
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3879,8 +3924,11 @@ function FloatingPanel({
3879
3924
  flip = true,
3880
3925
  minimumWidth = 0,
3881
3926
  focusOnOpen = false,
3882
- reveal
3927
+ reveal,
3928
+ header,
3929
+ nonModal = false
3883
3930
  }) {
3931
+ const [headerHeight, setHeaderHeight] = (0, import_react37.useState)(0);
3884
3932
  const panelRef = (0, import_react37.useRef)(null);
3885
3933
  const scroll = (0, import_react37.useRef)(null), viewport = (0, import_react37.useRef)(0);
3886
3934
  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 +3983,7 @@ function FloatingPanel({
3935
3983
  maximumWidth,
3936
3984
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : import_tokens24.tokens.extensions.popover.width)
3937
3985
  );
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;
3986
+ 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
3987
  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
3988
  if (placement === "left" || placement === "right") {
3941
3989
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -4019,7 +4067,7 @@ function FloatingPanel({
4019
4067
  }
4020
4068
  }
4021
4069
  ),
4022
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4070
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4023
4071
  import_react_native27.Animated.View,
4024
4072
  {
4025
4073
  ref: panelRef,
@@ -4030,7 +4078,7 @@ function FloatingPanel({
4030
4078
  pointerEvents: open && anchored && sized ? "auto" : "none",
4031
4079
  accessibilityElementsHidden: !open || !anchored || !sized,
4032
4080
  ...!open || !anchored || !sized ? { inert: true } : {},
4033
- accessibilityViewIsModal: true,
4081
+ accessibilityViewIsModal: !nonModal,
4034
4082
  accessibilityLabel: ariaLabel,
4035
4083
  style: {
4036
4084
  opacity: anchored && sized ? motion.opacity : 0,
@@ -4052,26 +4100,42 @@ function FloatingPanel({
4052
4100
  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
4101
  overflow: "hidden"
4054
4102
  },
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
- )
4103
+ children: [
4104
+ header != null && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4105
+ import_react_native28.View,
4106
+ {
4107
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
4108
+ style: {
4109
+ paddingTop: import_tokens24.tokens.extensions.popover.padding,
4110
+ paddingHorizontal: import_tokens24.tokens.extensions.popover.padding,
4111
+ paddingBottom: import_tokens24.tokens.extensions.popover.headerPaddingBottom,
4112
+ borderBottomWidth: import_tokens24.tokens.border.defaultWidth,
4113
+ borderBottomColor: colors.divider
4114
+ },
4115
+ children: header
4116
+ }
4117
+ ),
4118
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4119
+ import_react_native28.ScrollView,
4120
+ {
4121
+ ref: scroll,
4122
+ onLayout: (event) => {
4123
+ viewport.current = event.nativeEvent.layout.height;
4124
+ },
4125
+ keyboardShouldPersistTaps: "handled",
4126
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
4127
+ contentContainerStyle: { padding: menu || field ? import_tokens24.tokens.extensions.menu.padding : 0 },
4128
+ onContentSizeChange: (width2, height2) => {
4129
+ if (contentSizedMenu) return;
4130
+ setSized(true);
4131
+ setPanel(
4132
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4133
+ );
4134
+ },
4135
+ children
4136
+ }
4137
+ )
4138
+ ]
4075
4139
  }
4076
4140
  )
4077
4141
  ]
@@ -4097,9 +4161,12 @@ function KjunPopover({
4097
4161
  focusOnOpen = false,
4098
4162
  ariaLabel,
4099
4163
  placement = "bottom",
4164
+ header,
4165
+ modal = true,
4100
4166
  trigger,
4101
4167
  children
4102
4168
  }) {
4169
+ const { colors } = useKjunStyles();
4103
4170
  const [internal, setInternal] = (0, import_react38.useState)(false), open = controlled != null ? controlled : internal, ref = (0, import_react38.useRef)(null);
4104
4171
  const change = (v) => {
4105
4172
  setInternal(v);
@@ -4128,6 +4195,8 @@ function KjunPopover({
4128
4195
  popover: true,
4129
4196
  focusOnOpen,
4130
4197
  ariaLabel,
4198
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4199
+ nonModal: !modal,
4131
4200
  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
4201
  typeof children === "function" ? children(() => change(false)) : children,
4133
4202
  import_react_native29.Platform.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -4142,9 +4211,18 @@ function KjunTooltip({
4142
4211
  delay = 0,
4143
4212
  children,
4144
4213
  onPress,
4145
- disabled
4214
+ disabled,
4215
+ open: controlled,
4216
+ onOpenChange
4146
4217
  }) {
4147
- const { colors } = useKjunStyles(), [open, setOpen] = (0, import_react38.useState)(false), ref = (0, import_react38.useRef)(null), timer = (0, import_react38.useRef)(void 0);
4218
+ const { colors } = useKjunStyles(), [internal, setInternal] = (0, import_react38.useState)(false), open = controlled != null ? controlled : internal, latest = (0, import_react38.useRef)({ open, onOpenChange }), setOpen = (value) => {
4219
+ var _a, _b;
4220
+ if (latest.current.open === value) return;
4221
+ latest.current.open = value;
4222
+ setInternal(value);
4223
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4224
+ }, ref = (0, import_react38.useRef)(null), timer = (0, import_react38.useRef)(void 0);
4225
+ latest.current = { open, onOpenChange };
4148
4226
  const scopeActive = useActiveLayerScope();
4149
4227
  const hide = () => {
4150
4228
  clearTimeout(timer.current);
@@ -7488,7 +7566,7 @@ function TableToolbar({
7488
7566
  selectable && selected.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
7489
7567
  import_react_native45.View,
7490
7568
  {
7491
- accessibilityRole: "toolbar",
7569
+ role: "group",
7492
7570
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7493
7571
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7494
7572
  children: [
@@ -9828,7 +9906,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9828
9906
  const { colors, fontFamily } = useKjunStyles();
9829
9907
  const field = useKjunField();
9830
9908
  const invalid = error || !!(field == null ? void 0 : field.error);
9831
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9909
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9832
9910
  const geometry2 = import_tokens61.tokens.extensions.compound;
9833
9911
  const buttonStyle = { width: import_tokens61.tokens.input[size].height, paddingHorizontal: 0 };
9834
9912
  const outer = import_tokens61.tokens.input[size].radius;
@@ -9936,7 +10014,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9936
10014
  nativeID: id || (field == null ? void 0 : field.id),
9937
10015
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9938
10016
  accessibilityLabelledBy: labelledBy,
9939
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
10017
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9940
10018
  role: "spinbutton",
9941
10019
  accessibilityValue: { min, max, now: value },
9942
10020
  accessibilityState: { disabled: blocked },