@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.
package/dist/index.web.js CHANGED
@@ -26,9 +26,23 @@ function mergeIcons(parent, own) {
26
26
  function withFallbackIcons(registry, fallbacks) {
27
27
  return mergeIcons(fallbacks, registry);
28
28
  }
29
+ var warnedIcons = /* @__PURE__ */ new Set();
30
+ function developmentMode() {
31
+ try {
32
+ return true;
33
+ } catch (e) {
34
+ return false;
35
+ }
36
+ }
37
+ function warnMissingIcon(name) {
38
+ if (!developmentMode() || warnedIcons.has(name)) return;
39
+ warnedIcons.add(name);
40
+ console.warn(`KJUN Icon: "${name}" \uC544\uC774\uCF58\uC774 \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC544 help-circle\uB85C \uB300\uC2E0\uD569\uB2C8\uB2E4. @kjun-ui/icons/icons/${name}\uB97C import\uD574 KjunProvider\uC758 icons\uC5D0 \uB4F1\uB85D\uD558\uC138\uC694.`);
41
+ }
29
42
  function resolveIcon(registry, name, filled = false) {
30
43
  var _a;
31
44
  const definition = Object.prototype.hasOwnProperty.call(registry, name) ? registry[name] : void 0;
45
+ if (!definition && name) warnMissingIcon(name);
32
46
  const filledNodes = filled && (definition == null ? void 0 : definition.filled);
33
47
  return { nodes: filledNodes || (definition == null ? void 0 : definition.outline) || ((_a = registry["help-circle"]) == null ? void 0 : _a.outline) || defaultIcons["help-circle"].outline, filled: !!filledNodes };
34
48
  }
@@ -773,6 +787,7 @@ function KjunButton(input) {
773
787
  size: declaredSize = "md",
774
788
  loading = false,
775
789
  block = false,
790
+ wrap = false,
776
791
  disabled = false,
777
792
  prefixIcon,
778
793
  suffixIcon,
@@ -793,7 +808,7 @@ function KjunButton(input) {
793
808
  const formActions = useContext5(FormActionsContext);
794
809
  const cardActions = useContext5(CardActionsContext);
795
810
  const stateContent = useContext5(DataStateContentContext);
796
- const multiline = !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
811
+ const multiline = wrap || !!formActions || cardActions || !!stateContent || (navigation == null ? void 0 : navigation.slot) === "actions";
797
812
  const height = Math.max(tokens3.button.heights[size], (_a = navigation == null ? void 0 : navigation.controlSize) != null ? _a : 0);
798
813
  const iconSize = (_b = navigation == null ? void 0 : navigation.iconSize) != null ? _b : tokens3.button.iconSizes[size];
799
814
  const hasContent2 = children !== void 0 && children !== null && children !== false && children !== "";
@@ -954,6 +969,16 @@ function FieldClear({ size, disabled, label, onPress }) {
954
969
  );
955
970
  }
956
971
 
972
+ // shared/package-runtime/aria-ids.ts
973
+ function mergeIds(...lists) {
974
+ const ids = [...new Set(lists.flatMap((list) => (list != null ? list : "").split(/\s+/)).filter(Boolean))];
975
+ return ids.length ? ids.join(" ") : void 0;
976
+ }
977
+ function joinHints(...parts) {
978
+ const text = parts.filter(Boolean);
979
+ return text.length ? text.join(". ") : void 0;
980
+ }
981
+
957
982
  // packages/native/src/field-surface.ts
958
983
  import { useState as useState6 } from "react";
959
984
  import { Animated as Animated3, Platform as Platform5, TextInput } from "react-native";
@@ -1141,6 +1166,7 @@ function KjunFormGroup({
1141
1166
  required = false,
1142
1167
  error = "",
1143
1168
  hint = "",
1169
+ labelHidden = false,
1144
1170
  children,
1145
1171
  style
1146
1172
  }) {
@@ -1150,9 +1176,17 @@ function KjunFormGroup({
1150
1176
  return /* @__PURE__ */ jsx8(
1151
1177
  FieldContext.Provider,
1152
1178
  {
1153
- value: { id: fieldId, label, error, hint, required, describedBy: error ? fieldId + "-error" : hint ? fieldId + "-hint" : void 0 },
1179
+ value: {
1180
+ id: fieldId,
1181
+ label,
1182
+ error,
1183
+ hint,
1184
+ required,
1185
+ labelHidden,
1186
+ describedBy: mergeIds(hint ? fieldId + "-hint" : void 0, error ? fieldId + "-error" : void 0)
1187
+ },
1154
1188
  children: /* @__PURE__ */ jsxs2(View4, { style: [{ minWidth: 0, marginBottom: tokens7.extensions.form.itemGap }, style], children: [
1155
- label && /* @__PURE__ */ jsxs2(
1189
+ label && !labelHidden && /* @__PURE__ */ jsxs2(
1156
1190
  Text3,
1157
1191
  {
1158
1192
  nativeID: fieldId + "-label",
@@ -1169,7 +1203,15 @@ function KjunFormGroup({
1169
1203
  }
1170
1204
  ),
1171
1205
  children,
1172
- (error || hint) && /* @__PURE__ */ jsxs2(
1206
+ !!hint && /* @__PURE__ */ jsx8(
1207
+ Text3,
1208
+ {
1209
+ nativeID: fieldId + "-hint",
1210
+ style: { fontFamily, ...typeStyle("caption"), marginTop: tokens7.extensions.form.messageGap, color: c.textSecondary },
1211
+ children: hint
1212
+ }
1213
+ ),
1214
+ !!error && /* @__PURE__ */ jsxs2(
1173
1215
  View4,
1174
1216
  {
1175
1217
  style: {
@@ -1179,19 +1221,14 @@ function KjunFormGroup({
1179
1221
  marginTop: tokens7.extensions.form.messageGap
1180
1222
  },
1181
1223
  children: [
1182
- !!error && /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1224
+ /* @__PURE__ */ jsx8(KjunIcon, { name: "alert-circle", size: tokens7.extensions.form.messageIconSize, color: c.danger }),
1183
1225
  /* @__PURE__ */ jsx8(
1184
1226
  Text3,
1185
1227
  {
1186
- nativeID: fieldId + (error ? "-error" : "-hint"),
1187
- accessibilityRole: error ? "alert" : void 0,
1188
- style: {
1189
- fontFamily,
1190
- ...typeStyle("caption"),
1191
- flexShrink: 1,
1192
- color: error ? c.danger : c.textSecondary
1193
- },
1194
- children: error || hint
1228
+ nativeID: fieldId + "-error",
1229
+ accessibilityRole: "alert",
1230
+ style: { fontFamily, ...typeStyle("caption"), flexShrink: 1, color: c.danger },
1231
+ children: error
1195
1232
  }
1196
1233
  )
1197
1234
  ]
@@ -1265,8 +1302,8 @@ var KjunInput = forwardRef2(function KjunInput2({
1265
1302
  editable: canEdit,
1266
1303
  readOnly,
1267
1304
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
1268
- accessibilityLabelledBy: (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
1269
- accessibilityHint: errorMessage || (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
1305
+ accessibilityLabelledBy: (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
1306
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, errorMessage || (field == null ? void 0 : field.error)) || props.accessibilityHint,
1270
1307
  accessibilityState: { disabled },
1271
1308
  ...Platform6.OS === "web" ? {
1272
1309
  // Native Web TextInput does not translate accessibilityState.
@@ -1879,6 +1916,13 @@ function KjunCard({
1879
1916
  ] });
1880
1917
  }
1881
1918
 
1919
+ // shared/package-runtime/alert-live.ts
1920
+ function alertRole(tone, live) {
1921
+ if (live === "off") return void 0;
1922
+ if (live) return live === "assertive" ? "alert" : "status";
1923
+ return tone === "danger" ? "alert" : "status";
1924
+ }
1925
+
1882
1926
  // packages/native/src/display-primitives.tsx
1883
1927
  import { tokens as tokens10 } from "@kjun-ui/tokens";
1884
1928
  import { useContext as useContext8, useEffect as useEffect5, useRef as useRef6, useState as useState10 } from "react";
@@ -2076,6 +2120,7 @@ function KjunAlert({
2076
2120
  size = "md",
2077
2121
  title,
2078
2122
  closable = false,
2123
+ live,
2079
2124
  children,
2080
2125
  actions,
2081
2126
  onClose
@@ -2092,7 +2137,7 @@ function KjunAlert({
2092
2137
  return /* @__PURE__ */ jsxs7(
2093
2138
  View10,
2094
2139
  {
2095
- role: tone === "danger" ? "alert" : "status",
2140
+ role: alertRole(tone, live),
2096
2141
  style: {
2097
2142
  flexDirection: "row",
2098
2143
  alignItems: "flex-start",
@@ -3609,13 +3654,13 @@ var KjunTextarea = forwardRef3(
3609
3654
  editable: !disabled && !readonly && !props.readOnly && props.editable !== false,
3610
3655
  nativeID: props.nativeID || (field == null ? void 0 : field.id),
3611
3656
  accessibilityLabel: ariaLabel || props.accessibilityLabel || (field == null ? void 0 : field.label),
3612
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint) || props.accessibilityHint,
3657
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error) || props.accessibilityHint,
3613
3658
  accessibilityState: { disabled },
3614
3659
  ...Platform13.OS === "web" ? {
3615
3660
  "aria-disabled": disabled,
3616
3661
  "aria-invalid": invalid || void 0,
3617
3662
  "aria-describedby": field == null ? void 0 : field.describedBy,
3618
- "aria-labelledby": (field == null ? void 0 : field.label) ? field.id + "-label" : void 0,
3663
+ "aria-labelledby": (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0,
3619
3664
  "aria-required": (field == null ? void 0 : field.required) || void 0
3620
3665
  } : {},
3621
3666
  placeholderTextColor: disabled ? colors.textDisabled : colors.inputPlaceholder,
@@ -3885,8 +3930,11 @@ function FloatingPanel({
3885
3930
  flip = true,
3886
3931
  minimumWidth = 0,
3887
3932
  focusOnOpen = false,
3888
- reveal
3933
+ reveal,
3934
+ header,
3935
+ nonModal = false
3889
3936
  }) {
3937
+ const [headerHeight, setHeaderHeight] = useState17(0);
3890
3938
  const panelRef = useRef17(null);
3891
3939
  const scroll = useRef17(null), viewport = useRef17(0);
3892
3940
  const { colors } = useKjunStyles(), window2 = useWindowDimensions4(), [anchored, setAnchored] = useState17(false), [sized, setSized] = useState17(false), [anchor, setAnchor] = useState17({ x: 0, y: 0, width: 0, height: 0 }), [panel, setPanel] = useState17({ width: 0, height: 0 });
@@ -3941,7 +3989,7 @@ function FloatingPanel({
3941
3989
  maximumWidth,
3942
3990
  Math.max(minimumPanelWidth, contentSizedMenu ? panel.width : matchTriggerWidth ? anchor.width : tokens22.extensions.popover.width)
3943
3991
  );
3944
- const available = Math.min(maxHeight != null ? maxHeight : field ? tokens22.extensions.menu.listMaxHeight : tokens22.extensions.floating.maxHeight, window2.height - 2 * tokens22.extensions.floating.viewportInset), height = Math.min(panel.height, available), roomAbove = anchor.y - gap - tokens22.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens22.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3992
+ const available = Math.min(maxHeight != null ? maxHeight : field ? tokens22.extensions.menu.listMaxHeight : tokens22.extensions.floating.maxHeight, window2.height - 2 * tokens22.extensions.floating.viewportInset), height = Math.min(panel.height + (header != null ? headerHeight : 0), available), roomAbove = anchor.y - gap - tokens22.extensions.floating.viewportInset, roomBelow = window2.height - anchor.y - anchor.height - gap - tokens22.extensions.floating.viewportInset, above = placement.startsWith("top") ? !(flip && height > roomAbove && roomBelow > roomAbove) : flip && height > roomBelow && roomAbove > roomBelow;
3945
3993
  let left = placement.endsWith("end") ? anchor.x + anchor.width - width : placement === "top" || placement === "bottom" ? anchor.x + (anchor.width - width) / 2 : anchor.x, top = above ? anchor.y - height - gap : anchor.y + anchor.height + gap;
3946
3994
  if (placement === "left" || placement === "right") {
3947
3995
  left = placement === "left" ? anchor.x - width - gap : anchor.x + anchor.width + gap;
@@ -4025,7 +4073,7 @@ function FloatingPanel({
4025
4073
  }
4026
4074
  }
4027
4075
  ),
4028
- /* @__PURE__ */ jsx24(
4076
+ /* @__PURE__ */ jsxs15(
4029
4077
  Animated13.View,
4030
4078
  {
4031
4079
  ref: panelRef,
@@ -4036,7 +4084,7 @@ function FloatingPanel({
4036
4084
  pointerEvents: open && anchored && sized ? "auto" : "none",
4037
4085
  accessibilityElementsHidden: !open || !anchored || !sized,
4038
4086
  ...!open || !anchored || !sized ? { inert: true } : {},
4039
- accessibilityViewIsModal: true,
4087
+ accessibilityViewIsModal: !nonModal,
4040
4088
  accessibilityLabel: ariaLabel,
4041
4089
  style: {
4042
4090
  opacity: anchored && sized ? motion.opacity : 0,
@@ -4058,26 +4106,42 @@ function FloatingPanel({
4058
4106
  boxShadow: (tooltip ? tokens22.extensions.tooltip.elevation : popover ? tokens22.extensions.popover.elevation : tokens22.extensions.menu.elevation).map(({ colorRole, ...layer2 }) => ({ ...layer2, color: colors[colorRole] })),
4059
4107
  overflow: "hidden"
4060
4108
  },
4061
- children: /* @__PURE__ */ jsx24(
4062
- ScrollView4,
4063
- {
4064
- ref: scroll,
4065
- onLayout: (event) => {
4066
- viewport.current = event.nativeEvent.layout.height;
4067
- },
4068
- keyboardShouldPersistTaps: "handled",
4069
- style: { maxHeight: available },
4070
- contentContainerStyle: { padding: menu || field ? tokens22.extensions.menu.padding : 0 },
4071
- onContentSizeChange: (width2, height2) => {
4072
- if (contentSizedMenu) return;
4073
- setSized(true);
4074
- setPanel(
4075
- (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4076
- );
4077
- },
4078
- children
4079
- }
4080
- )
4109
+ children: [
4110
+ header != null && /* @__PURE__ */ jsx24(
4111
+ View19,
4112
+ {
4113
+ onLayout: ({ nativeEvent: { layout } }) => setHeaderHeight(layout.height),
4114
+ style: {
4115
+ paddingTop: tokens22.extensions.popover.padding,
4116
+ paddingHorizontal: tokens22.extensions.popover.padding,
4117
+ paddingBottom: tokens22.extensions.popover.headerPaddingBottom,
4118
+ borderBottomWidth: tokens22.border.defaultWidth,
4119
+ borderBottomColor: colors.divider
4120
+ },
4121
+ children: header
4122
+ }
4123
+ ),
4124
+ /* @__PURE__ */ jsx24(
4125
+ ScrollView4,
4126
+ {
4127
+ ref: scroll,
4128
+ onLayout: (event) => {
4129
+ viewport.current = event.nativeEvent.layout.height;
4130
+ },
4131
+ keyboardShouldPersistTaps: "handled",
4132
+ style: { maxHeight: available - (header != null ? headerHeight : 0), flexShrink: 1 },
4133
+ contentContainerStyle: { padding: menu || field ? tokens22.extensions.menu.padding : 0 },
4134
+ onContentSizeChange: (width2, height2) => {
4135
+ if (contentSizedMenu) return;
4136
+ setSized(true);
4137
+ setPanel(
4138
+ (previous) => previous.width === width2 && previous.height === height2 ? previous : { width: width2, height: height2 }
4139
+ );
4140
+ },
4141
+ children
4142
+ }
4143
+ )
4144
+ ]
4081
4145
  }
4082
4146
  )
4083
4147
  ]
@@ -4112,9 +4176,12 @@ function KjunPopover({
4112
4176
  focusOnOpen = false,
4113
4177
  ariaLabel,
4114
4178
  placement = "bottom",
4179
+ header,
4180
+ modal = true,
4115
4181
  trigger,
4116
4182
  children
4117
4183
  }) {
4184
+ const { colors } = useKjunStyles();
4118
4185
  const [internal, setInternal] = useState18(false), open = controlled != null ? controlled : internal, ref = useRef18(null);
4119
4186
  const change = (v) => {
4120
4187
  setInternal(v);
@@ -4143,6 +4210,8 @@ function KjunPopover({
4143
4210
  popover: true,
4144
4211
  focusOnOpen,
4145
4212
  ariaLabel,
4213
+ header: header != null ? content(header, { ...typeStyle("cardTitle"), color: colors.text }) : void 0,
4214
+ nonModal: !modal,
4146
4215
  children: /* @__PURE__ */ jsx25(View20, { style: { padding: noPadding ? 0 : tokens23.extensions.popover.padding, gap: noPadding ? 0 : tokens23.extensions.popover.gap }, children: content(
4147
4216
  typeof children === "function" ? children(() => change(false)) : children,
4148
4217
  Platform16.OS === "web" ? { wordBreak: "keep-all", overflowWrap: "anywhere" } : void 0
@@ -4157,9 +4226,18 @@ function KjunTooltip({
4157
4226
  delay = 0,
4158
4227
  children,
4159
4228
  onPress,
4160
- disabled
4229
+ disabled,
4230
+ open: controlled,
4231
+ onOpenChange
4161
4232
  }) {
4162
- const { colors } = useKjunStyles(), [open, setOpen] = useState18(false), ref = useRef18(null), timer = useRef18(void 0);
4233
+ const { colors } = useKjunStyles(), [internal, setInternal] = useState18(false), open = controlled != null ? controlled : internal, latest = useRef18({ open, onOpenChange }), setOpen = (value) => {
4234
+ var _a, _b;
4235
+ if (latest.current.open === value) return;
4236
+ latest.current.open = value;
4237
+ setInternal(value);
4238
+ (_b = (_a = latest.current).onOpenChange) == null ? void 0 : _b.call(_a, value);
4239
+ }, ref = useRef18(null), timer = useRef18(void 0);
4240
+ latest.current = { open, onOpenChange };
4163
4241
  const scopeActive = useActiveLayerScope();
4164
4242
  const hide = () => {
4165
4243
  clearTimeout(timer.current);
@@ -7527,7 +7605,7 @@ function TableToolbar({
7527
7605
  selectable && selected.length > 0 && /* @__PURE__ */ jsxs30(
7528
7606
  View36,
7529
7607
  {
7530
- accessibilityRole: "toolbar",
7608
+ role: "group",
7531
7609
  accessibilityLabel: "\uC120\uD0DD \uC791\uC5C5",
7532
7610
  style: [toolbarStyle, { backgroundColor: colors.selectedBg }],
7533
7611
  children: [
@@ -9879,7 +9957,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9879
9957
  const { colors, fontFamily } = useKjunStyles();
9880
9958
  const field = useKjunField();
9881
9959
  const invalid = error || !!(field == null ? void 0 : field.error);
9882
- const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) ? field.id + "-label" : void 0;
9960
+ const labelledBy = !ariaLabel && (field == null ? void 0 : field.label) && !field.labelHidden ? field.id + "-label" : void 0;
9883
9961
  const geometry2 = tokens58.extensions.compound;
9884
9962
  const buttonStyle = { width: tokens58.input[size].height, paddingHorizontal: 0 };
9885
9963
  const outer = tokens58.input[size].radius;
@@ -9987,7 +10065,7 @@ function KjunQuantityStepper({ value, min = 0, max, step = 1, precision = 0, dis
9987
10065
  nativeID: id || (field == null ? void 0 : field.id),
9988
10066
  accessibilityLabel: ariaLabel || (field == null ? void 0 : field.label) || "\uC218\uB7C9",
9989
10067
  accessibilityLabelledBy: labelledBy,
9990
- accessibilityHint: (field == null ? void 0 : field.error) || (field == null ? void 0 : field.hint),
10068
+ accessibilityHint: joinHints(field == null ? void 0 : field.hint, field == null ? void 0 : field.error),
9991
10069
  role: "spinbutton",
9992
10070
  accessibilityValue: { min, max, now: value },
9993
10071
  accessibilityState: { disabled: blocked },
package/dist/styles.css CHANGED
@@ -250,6 +250,7 @@
250
250
  --_kjun-extension-popover-radius: 12px;
251
251
  --_kjun-extension-popover-padding: 16px;
252
252
  --_kjun-extension-popover-gap: 12px;
253
+ --_kjun-extension-popover-header-padding-bottom: 12px;
253
254
  --_kjun-extension-state-title-size: 1.25rem;
254
255
  --_kjun-extension-state-description-size: 0.75rem;
255
256
  --_kjun-extension-state-padding-sm: 24px;
@@ -946,28 +947,30 @@
946
947
  /* Baseline for kjun-owned markup only: an element with a class that starts with kjun- (kjun- is reserved;
947
948
  my-kjun-logo is app markup). Component roots carry a kjun- class (Vue adds kjun-root, the Vue layer host is kjun-layers);
948
949
  portal roots pair kjun-scope with a kjun- class. The bare Provider root (kjun-scope) is excluded, so app markup inside the Provider keeps its own styles.
949
- Slot content placed inside a kjun component follows this baseline; style it with classes. */
950
+ Slot content placed inside a kjun component follows this baseline; style it with classes.
951
+ App markup under [data-kjun-content] opts out of the descendant resets, while kjun elements inside it get them back;
952
+ both branches keep the same specificity as before (the exclusion sits in :where). */
950
953
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]),
951
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *,
954
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *:not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) *,
952
955
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"])::before,
953
956
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"])::after,
954
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *::before,
955
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *::after {
957
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *:not(:where([data-kjun-content] *))::before, .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) *::before,
958
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) *:not(:where([data-kjun-content] *))::after, .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) *::after {
956
959
  box-sizing: border-box;
957
960
  border-width: 0;
958
961
  border-style: solid;
959
962
  border-color: var(--_kjun-color-border);
960
963
  }
961
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre),
964
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre),
962
965
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(h1, h2, h3, h4, h5, h6, p, blockquote, dl, dd, figure, pre) {
963
966
  margin: 0;
964
967
  }
965
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6),
968
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(h1, h2, h3, h4, h5, h6):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(h1, h2, h3, h4, h5, h6),
966
969
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(h1, h2, h3, h4, h5, h6) {
967
970
  font-size: inherit;
968
971
  font-weight: inherit;
969
972
  }
970
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, input, optgroup, select, textarea),
973
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, input, optgroup, select, textarea):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, input, optgroup, select, textarea),
971
974
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, input, optgroup, select, textarea) {
972
975
  font: inherit;
973
976
  letter-spacing: inherit;
@@ -975,33 +978,33 @@
975
978
  margin: 0;
976
979
  padding: 0;
977
980
  }
978
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, select),
981
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, select):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, select),
979
982
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, select) { text-transform: none; }
980
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [type="button"], [type="reset"], [type="submit"]),
983
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [type="button"], [type="reset"], [type="submit"]):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, [type="button"], [type="reset"], [type="submit"]),
981
984
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, [type="button"], [type="reset"], [type="submit"]) {
982
985
  -webkit-appearance: button;
983
986
  background-color: transparent;
984
987
  background-image: none;
985
988
  }
986
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [role="button"]),
989
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(button, [role="button"]):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(button, [role="button"]),
987
990
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(button, [role="button"]) { cursor: pointer; }
988
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(:disabled),
991
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(:disabled):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(:disabled),
989
992
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(:disabled) { cursor: default; }
990
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(a),
993
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(a):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(a),
991
994
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(a) { color: inherit; text-decoration: inherit; }
992
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(ol, ul, menu),
995
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(ol, ul, menu):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(ol, ul, menu),
993
996
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(ol, ul, menu) { list-style: none; margin: 0; padding: 0; }
994
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(table),
997
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(table):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(table),
995
998
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(table) { text-indent: 0; border-color: inherit; border-collapse: collapse; }
996
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(fieldset, legend),
999
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(fieldset, legend):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(fieldset, legend),
997
1000
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(fieldset, legend) { margin: 0; padding: 0; }
998
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(textarea),
1001
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(textarea):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(textarea),
999
1002
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(textarea) { resize: vertical; }
1000
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(input, textarea)::placeholder,
1003
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(input, textarea):not(:where([data-kjun-content] *))::placeholder, .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(input, textarea)::placeholder,
1001
1004
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(input, textarea)::placeholder { opacity: 1; }
1002
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(img, video),
1005
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where(img, video):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where(img, video),
1003
1006
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where(img, video) { max-width: 100%; height: auto; }
1004
- .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where([hidden]:not([hidden="until-found"])),
1007
+ .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]) :where([hidden]:not([hidden="until-found"])):not(:where([data-kjun-content] *)), .kjun-scope :where([data-kjun-content] :is([class^="kjun-"], [class*=" kjun-"])) :where([hidden]:not([hidden="until-found"])),
1005
1008
  .kjun-scope :where(:is([class^="kjun-"], [class*=" kjun-"]):not(.kjun-scope), .kjun-scope[class*=" kjun-"]):where([hidden]:not([hidden="until-found"])) { display: none; }
1006
1009
  /* Last so it wins over the element resets above (e.g. font: inherit on a root <button>).
1007
1010
  The outermost kjun element takes the scope's text style; nested kjun elements inherit it.
@@ -1770,6 +1773,9 @@
1770
1773
  justify-content: flex-end;
1771
1774
  gap: var(--_kjun-geometry-modal-actions-gap);
1772
1775
  }
1776
+ /* wrap: the inline height becomes a minimum (set by the component) and the label may break lines. */
1777
+ .kjun-button[data-wrap] { flex-shrink: 1; max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: center; }
1778
+ .kjun-button[data-wrap] > .kjun-button-label { min-width: 0; }
1773
1779
  .kjun-form-actions > .kjun-button {
1774
1780
  --_kjun-action-height: var(--_kjun-geometry-button-heights-lg);
1775
1781
  height: auto !important;
@@ -2424,6 +2430,11 @@
2424
2430
  font-weight:var(--_kjun-type-body-weight); letter-spacing:var(--_kjun-type-body-tracking);
2425
2431
  }
2426
2432
  .kjun-popover-dialog { outline:none; }
2433
+ /* With a header only the body scrolls: the panel and dialog become clipping flex columns. */
2434
+ .kjun-popover-has-header { display:flex; flex-direction:column; overflow:hidden; }
2435
+ .kjun-popover-has-header .kjun-popover-dialog { display:flex; flex-direction:column; flex:1 1 auto; min-height:0; }
2436
+ .kjun-popover-header { flex:none; padding:var(--_kjun-extension-popover-padding) var(--_kjun-extension-popover-padding) var(--_kjun-extension-popover-header-padding-bottom); border-bottom:var(--_kjun-border-default-width) solid var(--_kjun-color-divider); font-size:var(--_kjun-type-card-title-size); line-height:var(--_kjun-type-card-title-line); font-weight:var(--_kjun-type-card-title-weight); letter-spacing:var(--_kjun-type-card-title-tracking); word-break:keep-all; overflow-wrap:anywhere; }
2437
+ .kjun-popover-body { flex:1 1 auto; min-height:0; overflow-y:auto; }
2427
2438
  .kjun-popover-content { display:flex; flex-direction:column; gap:var(--_kjun-extension-popover-gap); padding:var(--_kjun-extension-popover-padding); min-width:0; word-break:keep-all; overflow-wrap:anywhere; }
2428
2439
  .kjun-popover-content[data-no-padding="true"] { padding:0; gap:0; }
2429
2440
  .kjun-tooltip {
@@ -14,6 +14,8 @@ export interface KjunButtonProps extends Omit<PressableProps, "children" | "styl
14
14
  size?: ButtonSize;
15
15
  loading?: boolean;
16
16
  block?: boolean;
17
+ /** Long labels wrap: the size's height becomes a minimum and the button grows instead of overflowing. */
18
+ wrap?: boolean;
17
19
  prefixIcon?: string;
18
20
  suffixIcon?: string;
19
21
  prefixIconFilled?: boolean;
@@ -14,6 +14,8 @@ export interface KjunButtonProps extends Omit<PressableProps, "children" | "styl
14
14
  size?: ButtonSize;
15
15
  loading?: boolean;
16
16
  block?: boolean;
17
+ /** Long labels wrap: the size's height becomes a minimum and the button grows instead of overflowing. */
18
+ wrap?: boolean;
17
19
  prefixIcon?: string;
18
20
  suffixIcon?: string;
19
21
  prefixIconFilled?: boolean;
@@ -1,3 +1,4 @@
1
+ import { type AlertLive } from "../../../shared/package-runtime/alert-live.cjs";
1
2
  import { type ReactNode } from "react";
2
3
  import { type StyleProp, type ViewStyle } from "react-native";
3
4
  export type DisplaySize = "xs" | "sm" | "md" | "lg" | "xl";
@@ -32,12 +33,14 @@ export interface KjunAlertProps {
32
33
  size?: "sm" | "md" | "lg";
33
34
  title?: ReactNode;
34
35
  closable?: boolean;
36
+ /** How screen readers announce it: assertive (role alert), polite (status) or off; defaults to alert for danger, status otherwise. */
37
+ live?: AlertLive;
35
38
  children?: ReactNode;
36
39
  /** Buttons placed under the message; unsized KjunButtons follow the alert size. */
37
40
  actions?: ReactNode;
38
41
  onClose?: () => void;
39
42
  }
40
- export declare function KjunAlert({ type, variant, size, title, closable, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
43
+ export declare function KjunAlert({ type, variant, size, title, closable, live, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
41
44
  export interface KjunProgressProps {
42
45
  value?: number;
43
46
  max?: number;
@@ -1,3 +1,4 @@
1
+ import { type AlertLive } from "../../../shared/package-runtime/alert-live.js";
1
2
  import { type ReactNode } from "react";
2
3
  import { type StyleProp, type ViewStyle } from "react-native";
3
4
  export type DisplaySize = "xs" | "sm" | "md" | "lg" | "xl";
@@ -32,12 +33,14 @@ export interface KjunAlertProps {
32
33
  size?: "sm" | "md" | "lg";
33
34
  title?: ReactNode;
34
35
  closable?: boolean;
36
+ /** How screen readers announce it: assertive (role alert), polite (status) or off; defaults to alert for danger, status otherwise. */
37
+ live?: AlertLive;
35
38
  children?: ReactNode;
36
39
  /** Buttons placed under the message; unsized KjunButtons follow the alert size. */
37
40
  actions?: ReactNode;
38
41
  onClose?: () => void;
39
42
  }
40
- export declare function KjunAlert({ type, variant, size, title, closable, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
43
+ export declare function KjunAlert({ type, variant, size, title, closable, live, children, actions, onClose, }: KjunAlertProps): import("react/jsx-runtime").JSX.Element | null;
41
44
  export interface KjunProgressProps {
42
45
  value?: number;
43
46
  max?: number;
@@ -6,7 +6,7 @@ export declare function LayerTrigger({ trigger, onPress, disabled, }: {
6
6
  disabled?: boolean;
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
8
  export type Placement = "top" | "bottom" | "left" | "right" | "bottom-start" | "bottom-end" | "top-start" | "top-end";
9
- export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, }: {
9
+ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, header, nonModal, }: {
10
10
  open: boolean;
11
11
  onOpenChange: (v: boolean) => void;
12
12
  triggerRef: RefObject<View | null>;
@@ -28,4 +28,8 @@ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children
28
28
  y: number;
29
29
  height: number;
30
30
  } | null>;
31
+ /** Popover header drawn above the scroll view, so only the body scrolls. */
32
+ header?: ReactNode;
33
+ /** Popover modal={false}: screen readers are not confined to the panel. */
34
+ nonModal?: boolean;
31
35
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -6,7 +6,7 @@ export declare function LayerTrigger({ trigger, onPress, disabled, }: {
6
6
  disabled?: boolean;
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
8
  export type Placement = "top" | "bottom" | "left" | "right" | "bottom-start" | "bottom-end" | "top-start" | "top-end";
9
- export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, }: {
9
+ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children, placement, matchTriggerWidth, maxHeight, ariaLabel, menu, field, tooltip, popover, flip, minimumWidth, focusOnOpen, reveal, header, nonModal, }: {
10
10
  open: boolean;
11
11
  onOpenChange: (v: boolean) => void;
12
12
  triggerRef: RefObject<View | null>;
@@ -28,4 +28,8 @@ export declare function FloatingPanel({ open, onOpenChange, triggerRef, children
28
28
  y: number;
29
29
  height: number;
30
30
  } | null>;
31
+ /** Popover header drawn above the scroll view, so only the body scrolls. */
32
+ header?: ReactNode;
33
+ /** Popover modal={false}: screen readers are not confined to the panel. */
34
+ nonModal?: boolean;
31
35
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -8,6 +8,7 @@ interface FieldValue {
8
8
  hint?: string;
9
9
  required?: boolean;
10
10
  describedBy?: string;
11
+ labelHidden?: boolean;
11
12
  }
12
13
  export declare const FieldContext: import("react").Context<FieldValue | null>;
13
14
  export declare const useKjunField: () => FieldValue | null;
@@ -17,10 +18,12 @@ export interface KjunFormGroupProps {
17
18
  required?: boolean;
18
19
  error?: string;
19
20
  hint?: string;
21
+ /** Hides the label visually while it still names the field. */
22
+ labelHidden?: boolean;
20
23
  children: ReactNode;
21
24
  style?: StyleProp<ViewStyle>;
22
25
  }
23
- export declare function KjunFormGroup({ label, id, required, error, hint, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function KjunFormGroup({ label, id, required, error, hint, labelHidden, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
24
27
  export interface KjunInputProps extends Omit<TextInputProps, "value" | "onChange"> {
25
28
  value: string | number;
26
29
  size?: InputSize;
@@ -8,6 +8,7 @@ interface FieldValue {
8
8
  hint?: string;
9
9
  required?: boolean;
10
10
  describedBy?: string;
11
+ labelHidden?: boolean;
11
12
  }
12
13
  export declare const FieldContext: import("react").Context<FieldValue | null>;
13
14
  export declare const useKjunField: () => FieldValue | null;
@@ -17,10 +18,12 @@ export interface KjunFormGroupProps {
17
18
  required?: boolean;
18
19
  error?: string;
19
20
  hint?: string;
21
+ /** Hides the label visually while it still names the field. */
22
+ labelHidden?: boolean;
20
23
  children: ReactNode;
21
24
  style?: StyleProp<ViewStyle>;
22
25
  }
23
- export declare function KjunFormGroup({ label, id, required, error, hint, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function KjunFormGroup({ label, id, required, error, hint, labelHidden, children, style, }: KjunFormGroupProps): import("react/jsx-runtime").JSX.Element;
24
27
  export interface KjunInputProps extends Omit<TextInputProps, "value" | "onChange"> {
25
28
  value: string | number;
26
29
  size?: InputSize;