ropav 0.0.11 → 0.0.13

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.
Files changed (126) hide show
  1. package/README.md +3 -0
  2. package/dist/checkbox.css +32 -32
  3. package/dist/checkbox.js +33 -14
  4. package/dist/color-input.css +19 -19
  5. package/dist/color-input.js +35 -17
  6. package/dist/components/checkbox/types.d.ts +5 -1
  7. package/dist/components/checkbox/useCheckbox.d.ts +1 -1
  8. package/dist/components/color-input/types.d.ts +4 -1
  9. package/dist/components/color-input/useColorInput.d.ts +1 -1
  10. package/dist/components/color-input/useColorInputValue.d.ts +1 -1
  11. package/dist/components/dialog/dialog-close.d.ts +21 -0
  12. package/dist/components/dialog/dialog-content.d.ts +32 -0
  13. package/dist/components/dialog/dialog-core.d.ts +27 -0
  14. package/dist/components/dialog/dialog-description.d.ts +21 -0
  15. package/dist/components/dialog/dialog-overlay.d.ts +21 -0
  16. package/dist/components/dialog/dialog-portal.d.ts +21 -0
  17. package/dist/components/dialog/dialog-root.d.ts +28 -0
  18. package/dist/components/dialog/dialog-title.d.ts +21 -0
  19. package/dist/components/dialog/dialog-trigger.d.ts +23 -0
  20. package/dist/components/dialog/index.d.ts +9 -0
  21. package/dist/components/dialog/index.js +2 -0
  22. package/dist/components/dialog/types.d.ts +61 -0
  23. package/dist/components/dropdown-menu/dropdown-menu-checkbox-item.d.ts +24 -0
  24. package/dist/components/dropdown-menu/dropdown-menu-content.d.ts +34 -0
  25. package/dist/components/dropdown-menu/dropdown-menu-context-trigger.d.ts +23 -0
  26. package/dist/components/dropdown-menu/dropdown-menu-item-indicator.d.ts +24 -0
  27. package/dist/components/dropdown-menu/dropdown-menu-item-row.d.ts +28 -0
  28. package/dist/components/dropdown-menu/dropdown-menu-item.d.ts +23 -0
  29. package/dist/components/dropdown-menu/dropdown-menu-items.d.ts +24 -22
  30. package/dist/components/dropdown-menu/dropdown-menu-label.d.ts +21 -0
  31. package/dist/components/dropdown-menu/dropdown-menu-outside.d.ts +3 -0
  32. package/dist/components/dropdown-menu/dropdown-menu-portal.d.ts +21 -0
  33. package/dist/components/dropdown-menu/dropdown-menu-primitive-core.d.ts +110 -0
  34. package/dist/components/dropdown-menu/dropdown-menu-primitives.d.ts +15 -724
  35. package/dist/components/dropdown-menu/dropdown-menu-radio-group.d.ts +25 -0
  36. package/dist/components/dropdown-menu/dropdown-menu-radio-item.d.ts +23 -0
  37. package/dist/components/dropdown-menu/dropdown-menu-root.d.ts +30 -0
  38. package/dist/components/dropdown-menu/dropdown-menu-separator.d.ts +19 -0
  39. package/dist/components/dropdown-menu/dropdown-menu-sub-content.d.ts +34 -0
  40. package/dist/components/dropdown-menu/dropdown-menu-sub-trigger.d.ts +28 -0
  41. package/dist/components/dropdown-menu/dropdown-menu-sub.d.ts +28 -0
  42. package/dist/components/dropdown-menu/dropdown-menu-trigger.d.ts +23 -0
  43. package/dist/components/dropdown-menu/dropdown-menu.d.ts +5 -2
  44. package/dist/components/dropdown-menu/index.d.ts +28 -49
  45. package/dist/components/dropdown-menu/index.js +2 -2
  46. package/dist/components/dropdown-menu/types.d.ts +8 -3
  47. package/dist/components/dropdown-menu/useDropdownMenu.d.ts +8 -4
  48. package/dist/components/dropdown-menu/useDropdownMenuDisclosure.d.ts +7 -2
  49. package/dist/components/dropdown-menu/useDropdownMenuPortalPosition.d.ts +3 -3
  50. package/dist/components/dropdown-menu/useDropdownMenuPrimitiveContent.d.ts +27 -0
  51. package/dist/components/dropdown-menu/useDropdownMenuRenderItems.d.ts +2 -0
  52. package/dist/components/floating/index.d.ts +3 -1
  53. package/dist/components/floating/index.js +3 -0
  54. package/dist/components/floating/types.d.ts +86 -1
  55. package/dist/components/floating/useFloatingPosition.d.ts +3 -26
  56. package/dist/components/floating/useHoverDisclosure.d.ts +2 -0
  57. package/dist/components/input/input.d.ts +4 -1
  58. package/dist/components/input/types.d.ts +3 -1
  59. package/dist/components/modal/index.d.ts +1 -1
  60. package/dist/components/modal/modal.d.ts +3 -1
  61. package/dist/components/modal/types.d.ts +3 -0
  62. package/dist/components/modal/useModal.d.ts +7 -4
  63. package/dist/components/number-input/types.d.ts +3 -1
  64. package/dist/components/number-input/useNumberInput.d.ts +2 -1
  65. package/dist/components/popover/usePopover.d.ts +2 -2
  66. package/dist/components/radio/types.d.ts +17 -1
  67. package/dist/components/radio/useRadio.d.ts +3 -0
  68. package/dist/components/select/select.d.ts +4 -1
  69. package/dist/components/select/types.d.ts +6 -1
  70. package/dist/components/select/useSelect.d.ts +3 -2
  71. package/dist/components/slider/index.d.ts +1 -1
  72. package/dist/components/slider/range-slider.d.ts +3 -3
  73. package/dist/components/slider/types.d.ts +11 -3
  74. package/dist/components/slider/useRangeSlider.d.ts +1 -1
  75. package/dist/components/slider/useSlider.d.ts +1 -1
  76. package/dist/components/switch/types.d.ts +5 -1
  77. package/dist/components/switch/useSwitch.d.ts +1 -1
  78. package/dist/components/teleport-provider/index.d.ts +1 -0
  79. package/dist/components/teleport-provider/index.js +2 -1
  80. package/dist/components/teleport-provider/useTeleportTarget.d.ts +4 -3
  81. package/dist/components/textarea/types.d.ts +4 -1
  82. package/dist/components/textarea/useTextarea.d.ts +1 -1
  83. package/dist/components/toast/index.d.ts +2 -1
  84. package/dist/components/toast/index.js +2 -2
  85. package/dist/components/toast/toast-store.d.ts +2 -0
  86. package/dist/components/toast/types.d.ts +10 -4
  87. package/dist/components/toast/useToast.d.ts +2 -2
  88. package/dist/components/tooltip/useTooltip.d.ts +3 -3
  89. package/dist/composables/useOverlayLayer.d.ts +32 -0
  90. package/dist/dialog.js +510 -0
  91. package/dist/dropdown-menu.css +4 -5
  92. package/dist/dropdown-menu.js +1155 -886
  93. package/dist/floating.js +403 -0
  94. package/dist/index.d.ts +1 -0
  95. package/dist/index.js +7 -4
  96. package/dist/input.css +24 -24
  97. package/dist/input.js +21 -11
  98. package/dist/legacy-unlayered.css +424 -503
  99. package/dist/modal.css +28 -28
  100. package/dist/modal.js +141 -200
  101. package/dist/number-input.css +20 -20
  102. package/dist/number-input.js +44 -16
  103. package/dist/popover.css +4 -4
  104. package/dist/popover.js +29 -40
  105. package/dist/radio.css +18 -18
  106. package/dist/radio.js +96 -18
  107. package/dist/select.css +54 -43
  108. package/dist/select.js +185 -67
  109. package/dist/slider.css +183 -202
  110. package/dist/slider.js +138 -37
  111. package/dist/switch.css +17 -17
  112. package/dist/switch.js +31 -13
  113. package/dist/textarea.css +26 -26
  114. package/dist/textarea.js +19 -9
  115. package/dist/toast.js +110 -99
  116. package/dist/tooltip.css +5 -80
  117. package/dist/tooltip.js +11 -3
  118. package/dist/useControlState.js +4 -0
  119. package/dist/useFloatingPosition.js +86 -39
  120. package/dist/useFormControl.js +107 -0
  121. package/dist/useOverlayLayer.js +262 -0
  122. package/dist/useTeleportTarget.js +21 -10
  123. package/docs/public-floating-api.md +159 -0
  124. package/docs/public-styles-api.md +2 -1
  125. package/package.json +10 -6
  126. package/dist/useClickOutside.js +0 -24
@@ -3,9 +3,10 @@ import { t as bem } from "./bem.js";
3
3
  import { n as useStylesApi, t as presence } from "./styles-api.js";
4
4
  import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper.js";
5
5
  import { t as minus_default } from "./minus.js";
6
+ import { i as useFormControl, n as provideNestedFormControlOwner, o as useControllableValue } from "./useFormControl.js";
6
7
  import { t as useControlState } from "./useControlState.js";
7
8
  import { t as input_default } from "./input.js";
8
- import { child, computed, createComponent, createFor, createIf, createSlot, defineVaporComponent, delegateEvents, markRaw, on, renderEffect, setDynamicProps, setInsertionState, template, unref, withModifiers, withVaporModifiers } from "vue";
9
+ import { child, computed, createComponent, createFor, createIf, createSlot, defineVaporComponent, delegateEvents, markRaw, on, ref, renderEffect, setDynamicProps, setInsertionState, setStaticTemplateRef, template, unref, withModifiers, withVaporModifiers } from "vue";
9
10
  //#region ~icons/lucide/plus
10
11
  var t0$1 = template("<svg viewBox=\"0 0 24 24\"width=1.2em height=1.2em><path fill=none stroke=currentColor stroke-linecap=round stroke-linejoin=round stroke-width=2 d=\"M5 12h14m-7-7v14\">", 3, 1);
11
12
  function render(_ctx) {
@@ -76,11 +77,12 @@ function normalizeNumberInputTextAlign(textAlign) {
76
77
  function getModelInputValue(value) {
77
78
  return value !== null && Number.isFinite(value) ? String(normalizeZero(value)) : "";
78
79
  }
79
- function useNumberInput(props, emitUpdate) {
80
+ function useNumberInput(props, emitUpdate, getValue = () => props.modelValue ?? null) {
80
81
  const control = useControlState(props);
81
82
  const bounds = computed(() => normalizeNumberInputBounds(props.min, props.max));
82
83
  const nativeStep = computed(() => normalizeNumberInputStep(props.step));
83
- const modelInputValue = computed(() => getModelInputValue(props.modelValue));
84
+ const currentValue = computed(getValue);
85
+ const modelInputValue = computed(() => getModelInputValue(currentValue.value));
84
86
  const isInteractive = computed(() => !control.disabled && !props.readonly);
85
87
  const controlsPosition = computed(() => normalizeNumberInputControlsPosition(props.controlsPosition));
86
88
  const textAlign = computed(() => normalizeNumberInputTextAlign(props.textAlign));
@@ -107,17 +109,17 @@ function useNumberInput(props, emitUpdate) {
107
109
  }));
108
110
  const canIncrement = computed(() => {
109
111
  if (!isInteractive.value) return false;
110
- if (props.modelValue === null || !Number.isFinite(props.modelValue)) return true;
111
- return bounds.value.max === void 0 || props.modelValue < bounds.value.max;
112
+ if (currentValue.value === null || !Number.isFinite(currentValue.value)) return true;
113
+ return bounds.value.max === void 0 || currentValue.value < bounds.value.max;
112
114
  });
113
115
  const canDecrement = computed(() => {
114
116
  if (!isInteractive.value) return false;
115
- if (props.modelValue === null || !Number.isFinite(props.modelValue)) return true;
116
- return bounds.value.min === void 0 || props.modelValue > bounds.value.min;
117
+ if (currentValue.value === null || !Number.isFinite(currentValue.value)) return true;
118
+ return bounds.value.min === void 0 || currentValue.value > bounds.value.min;
117
119
  });
118
120
  function emitStep(direction) {
119
121
  if (!(direction === 1 ? canIncrement.value : canDecrement.value)) return false;
120
- const current = Number.isFinite(props.modelValue) ? props.modelValue : null;
122
+ const current = Number.isFinite(currentValue.value) ? currentValue.value : null;
121
123
  const next = stepNumberInputValue(current, direction, nativeStep.value, bounds.value);
122
124
  if (current !== null && next === current) return false;
123
125
  emitUpdate(next);
@@ -187,10 +189,10 @@ function useNumberInput(props, emitUpdate) {
187
189
  }
188
190
  //#endregion
189
191
  //#region src/components/number-input/number-input.vue?vue&type=script&setup=true&vapor=true&lang.ts
190
- var t0 = template("<button data-v-3cef367f><span data-v-3cef367f class=rp-number-input__control-icon aria-hidden=true></button>");
191
- var t1 = template("<span data-v-3cef367f class=\"rp-number-input__controls rp-number-input__controls--left\">");
192
- var t2 = template("<button data-v-3cef367f><span data-v-3cef367f class=rp-number-input__control-icon aria-hidden=true>");
193
- var t3 = template("<span data-v-3cef367f class=\"rp-number-input__controls rp-number-input__controls--right\">");
192
+ var t0 = template("<button data-v-eb2db673><span data-v-eb2db673 class=rp-number-input__control-icon aria-hidden=true></button>");
193
+ var t1 = template("<span data-v-eb2db673 class=\"rp-number-input__controls rp-number-input__controls--left\">");
194
+ var t2 = template("<button data-v-eb2db673><span data-v-eb2db673 class=rp-number-input__control-icon aria-hidden=true>");
195
+ var t3 = template("<span data-v-eb2db673 class=\"rp-number-input__controls rp-number-input__controls--right\">");
194
196
  delegateEvents("mousedown");
195
197
  //#endregion
196
198
  //#region src/components/number-input/number-input.vue
@@ -201,7 +203,9 @@ var number_input_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
201
203
  props: {
202
204
  id: {},
203
205
  name: {},
204
- modelValue: {},
206
+ form: {},
207
+ modelValue: { default: void 0 },
208
+ defaultValue: { default: null },
205
209
  min: {},
206
210
  max: {},
207
211
  step: { default: 1 },
@@ -252,7 +256,28 @@ var number_input_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
252
256
  setup(__props, { emit: __emit }) {
253
257
  const props = __props;
254
258
  const emit = __emit;
255
- const { control, rootClass, nativeInputAttrs, modelInputValue, leftControls, rightControls, canIncrement, canDecrement, increment, decrement, onInputUpdate } = useNumberInput(props, (value) => emit("update:modelValue", value));
259
+ const controllable = useControllableValue({
260
+ modelValue: () => props.modelValue,
261
+ defaultValue: () => props.defaultValue,
262
+ onChange: (value) => emit("update:modelValue", value)
263
+ });
264
+ const inputComponentRef = ref(null);
265
+ provideNestedFormControlOwner();
266
+ const { control, rootClass, nativeInputAttrs, modelInputValue, leftControls, rightControls, canIncrement, canDecrement, increment, decrement, onInputUpdate } = useNumberInput(props, (value) => controllable.setValue(value), () => controllable.value.value);
267
+ useFormControl({
268
+ elements: () => [inputComponentRef.value?.nativeElement],
269
+ isControlled: () => controllable.isControlled.value,
270
+ initializeDefault(element) {
271
+ element.defaultValue = getModelInputValue(controllable.initialValue);
272
+ },
273
+ validationMessage: () => props.validationMessage,
274
+ readResetValue(elements) {
275
+ controllable.resetValue(parseNumberInputValue(elements[0].value));
276
+ },
277
+ syncControlledValue(elements) {
278
+ elements[0].value = getModelInputValue(controllable.value.value);
279
+ }
280
+ });
256
281
  const { attrs, getPartAttrs } = useStylesApi(props, "root");
257
282
  const inputClassNames = computed(() => ({
258
283
  root: [rootClass.value, props.classNames?.root],
@@ -283,9 +308,10 @@ var number_input_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
283
308
  function onControlClick(event, controlType) {
284
309
  if (controlType === "increment" ? increment() : decrement()) focusNativeInput(event);
285
310
  }
286
- return createComponent(input_default, { $: [() => unref(attrs), {
311
+ const n30 = createComponent(input_default, { $: [() => unref(attrs), {
287
312
  id: () => unref(control).id,
288
313
  name: () => __props.name,
314
+ form: () => unref(control).form,
289
315
  "model-value": () => unref(modelInputValue),
290
316
  type: "number",
291
317
  size: () => __props.size,
@@ -381,8 +407,10 @@ var number_input_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
381
407
  return n28;
382
408
  }
383
409
  } : void 0] }, true);
410
+ setStaticTemplateRef(n30, inputComponentRef, null, "inputComponentRef");
411
+ return n30;
384
412
  }
385
- }), [["__scopeId", "data-v-3cef367f"]]);
413
+ }), [["__scopeId", "data-v-eb2db673"]]);
386
414
  //#endregion
387
415
  //#region src/components/number-input/types.ts
388
416
  var numberInputParts = [
package/dist/popover.css CHANGED
@@ -1,15 +1,15 @@
1
1
  @layer ropav.components {
2
2
  .rp-popover[data-v-8af57bff] {
3
- --_rp-popover-main-axis-offset: var(--rp-spacing-2);
4
- --_rp-popover-cross-axis-offset: 0px;
5
- --_rp-popover-radius: var(--rp-radius-sm);
6
- --_rp-popover-arrow-size: 0.5rem;
7
3
  position: relative;
8
4
  display: inline-flex;
9
5
  font-family: var(--rp-font-family);
10
6
  vertical-align: middle;
11
7
  }
12
8
  .rp-popover__content[data-v-8af57bff] {
9
+ --_rp-popover-main-axis-offset: var(--rp-spacing-2);
10
+ --_rp-popover-cross-axis-offset: 0px;
11
+ --_rp-popover-radius: var(--rp-radius-sm);
12
+ --_rp-popover-arrow-size: 0.5rem;
13
13
  position: absolute;
14
14
  z-index: 100;
15
15
  box-sizing: border-box;
package/dist/popover.js CHANGED
@@ -2,9 +2,10 @@ import './popover.css';
2
2
  import { t as bem } from "./bem.js";
3
3
  import { n as useStylesApi, t as presence } from "./styles-api.js";
4
4
  import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper.js";
5
+ import { t as useOverlayLayer } from "./useOverlayLayer.js";
5
6
  import { t as useFocusTrap } from "./useFocusTrap.js";
6
7
  import { n as useFloatingPosition, r as useFloatingTarget, t as isElementReference } from "./useFloatingPosition.js";
7
- import { n as useTeleportTarget } from "./useTeleportTarget.js";
8
+ import { n as useTeleportPositioningKey, r as useTeleportTarget } from "./useTeleportTarget.js";
8
9
  import { VaporTeleport, VaporTransition, applyVShow, computed, createComponent, createIf, createInvoker, createSlot, createTemplateRefSetter, defineVaporComponent, delegateEvents, extend, isRef, nextTick, ref, renderEffect, setAttr, setDynamicProps, setInsertionState, setStaticTemplateRef, setStyle, template, unref, useAttrs, useId, useSlots, watch } from "vue";
9
10
  //#region src/components/popover/usePopover.ts
10
11
  var DEFAULT_PLACEMENT = "bottom";
@@ -14,14 +15,6 @@ var TARGET_ATTRIBUTES = [
14
15
  "aria-expanded",
15
16
  "aria-haspopup"
16
17
  ];
17
- var TABBABLE_SELECTOR = [
18
- "a[href]",
19
- "button:not([disabled])",
20
- "input:not([disabled])",
21
- "select:not([disabled])",
22
- "textarea:not([disabled])",
23
- "[tabindex]:not([tabindex=\"-1\"])"
24
- ].join(",");
25
18
  function addIdReference(currentValue, id) {
26
19
  const ids = (currentValue ?? "").split(/\s+/).filter(Boolean);
27
20
  if (!ids.includes(id)) ids.push(id);
@@ -67,12 +60,18 @@ function usePopover(props, emitOpenChange) {
67
60
  const placement = computed(() => props.placement ?? DEFAULT_PLACEMENT);
68
61
  const popoverRole = computed(() => props.role ?? DEFAULT_ROLE);
69
62
  const teleportTo = useTeleportTarget(() => props.teleportTo);
63
+ const teleportPositioningKey = useTeleportPositioningKey();
70
64
  const { isExplicitTarget, reference, resolvedTarget } = useFloatingTarget(() => props.target, rootRef);
71
65
  const targetElement = computed(() => isElementReference(resolvedTarget.value) ? resolvedTarget.value : null);
72
66
  const hasContent = computed(() => Boolean(slots.content || isExplicitTarget.value && slots.default));
73
67
  const isDisabled = computed(() => Boolean(props.disabled || !hasContent.value));
74
68
  const isOpen = computed(() => props.open ?? uncontrolledOpen.value);
75
69
  const isVisible = computed(() => isOpen.value && !isDisabled.value);
70
+ const layer = useOverlayLayer({
71
+ active: isVisible,
72
+ element: contentRef,
73
+ baseZIndex: 100
74
+ });
76
75
  const shouldRenderContent = computed(() => !isDisabled.value && (Boolean(props.keepMounted) || isVisible.value));
77
76
  const shouldShowContent = computed(() => !props.keepMounted || isVisible.value);
78
77
  const ariaLabel = computed(() => props.ariaLabel || void 0);
@@ -82,7 +81,11 @@ function usePopover(props, emitOpenChange) {
82
81
  const content = contentRef.value;
83
82
  if (!content) return null;
84
83
  const trigger = targetElement.value ?? rootRef.value;
85
- return isFocusTrapContainer(trigger) ? [trigger, content] : content;
84
+ return isFocusTrapContainer(trigger) ? [
85
+ trigger,
86
+ content,
87
+ ...layer.focusBranches.value
88
+ ] : [content, ...layer.focusBranches.value];
86
89
  });
87
90
  const floating = useFloatingPosition({
88
91
  reference,
@@ -93,10 +96,15 @@ function usePopover(props, emitOpenChange) {
93
96
  strategy: () => props.strategy ?? "absolute",
94
97
  offset: () => props.offset,
95
98
  flip: () => props.flip !== false,
99
+ flipOptions: () => props.flipOptions,
96
100
  shift: () => props.shift !== false,
97
101
  collisionPadding: () => props.collisionPadding ?? 8,
98
- arrowEnabled: () => Boolean(props.arrow),
99
- restartKey: () => teleportTo.value
102
+ autoUpdateOptions: () => props.autoUpdateOptions,
103
+ restartKey: () => [
104
+ Boolean(props.teleport),
105
+ teleportTo.value,
106
+ teleportPositioningKey.value
107
+ ]
100
108
  });
101
109
  const placementSide = computed(() => floating.actualPlacement.value.split("-")[0]);
102
110
  const rootClass = computed(() => bem("rp-popover", {
@@ -108,7 +116,8 @@ function usePopover(props, emitOpenChange) {
108
116
  }));
109
117
  const contentStyle = computed(() => ({
110
118
  ...floating.floatingStyle.value,
111
- ...resolveOffsetStyle(props.offset)
119
+ ...resolveOffsetStyle(props.offset),
120
+ zIndex: layer.zIndex.value
112
121
  }));
113
122
  const triggerProps = computed(() => ({
114
123
  "aria-controls": isDisabled.value ? void 0 : popoverId.value,
@@ -167,14 +176,12 @@ function usePopover(props, emitOpenChange) {
167
176
  togglePopover();
168
177
  }
169
178
  function onTriggerKeydown(event) {
170
- if (event.key !== "Escape") return;
179
+ if (event.key !== "Escape" || !isVisible.value) return;
171
180
  event.stopPropagation();
172
181
  closePopover();
173
182
  }
174
183
  function isEventInsidePopover(event) {
175
- const eventTarget = event.target;
176
- if (typeof Node === "undefined" || !(eventTarget instanceof Node)) return false;
177
- return Boolean(rootRef.value?.contains(eventTarget) || contentRef.value?.contains(eventTarget) || targetElement.value?.contains(eventTarget));
184
+ return layer.isInside(event, [rootRef.value, targetElement.value]);
178
185
  }
179
186
  function onCompositeFocusout(event) {
180
187
  const nextTarget = event.relatedTarget;
@@ -184,37 +191,17 @@ function usePopover(props, emitOpenChange) {
184
191
  for (const current of listeners) if (typeof current === "function") current(event);
185
192
  }
186
193
  function onDocumentClick(event) {
194
+ if (!layer.isTopLayer()) return;
187
195
  if (props.closeOnOutsideClick === false || isEventInsidePopover(event)) return;
188
196
  closePopoverWithoutReturnFocus();
189
197
  }
190
198
  function onDocumentPointerDown(event) {
191
- if (!props.trapFocus) return;
199
+ if (!layer.isTopLayer()) return;
192
200
  if (props.closeOnOutsideClick === false || isEventInsidePopover(event)) return;
193
201
  closePopoverWithoutReturnFocus();
194
202
  }
195
203
  function onDocumentKeydown(event) {
196
- if (props.trapFocus && event.key === "Tab") {
197
- const containers = [targetElement.value ?? rootRef.value, contentRef.value];
198
- const tabbables = [];
199
- for (const container of containers) {
200
- if (!container) continue;
201
- if (container instanceof HTMLElement && container.matches(TABBABLE_SELECTOR)) tabbables.push(container);
202
- tabbables.push(...container.querySelectorAll(TABBABLE_SELECTOR));
203
- }
204
- const active = event.target;
205
- const first = tabbables[0];
206
- const last = tabbables[tabbables.length - 1];
207
- if (event.shiftKey && active === first && last) {
208
- event.preventDefault();
209
- last.focus();
210
- return;
211
- }
212
- if (!event.shiftKey && active === last && first) {
213
- event.preventDefault();
214
- first.focus();
215
- return;
216
- }
217
- }
204
+ if (!layer.isTopLayer()) return;
218
205
  if (props.closeOnEscape === false || event.key !== "Escape") return;
219
206
  closePopover();
220
207
  }
@@ -359,11 +346,13 @@ var popover_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
359
346
  type: Boolean,
360
347
  default: true
361
348
  },
349
+ flipOptions: {},
362
350
  shift: {
363
351
  type: Boolean,
364
352
  default: true
365
353
  },
366
354
  collisionPadding: { default: 8 },
355
+ autoUpdateOptions: {},
367
356
  arrow: {
368
357
  type: Boolean,
369
358
  default: false
package/dist/radio.css CHANGED
@@ -1,5 +1,5 @@
1
1
  @layer ropav.components {
2
- .rp-radio[data-v-d9c68210] {
2
+ .rp-radio[data-v-0cdcdcfa] {
3
3
  --_rp-radio-color: var(--rp-color-control-selected-bg);
4
4
  --_rp-radio-on-color: var(--rp-color-control-selected-fg);
5
5
  --_rp-radio-default-size: 24px;
@@ -16,15 +16,15 @@
16
16
  font-family: var(--rp-font-family);
17
17
  user-select: none;
18
18
  }
19
- .rp-radio--disabled[data-v-d9c68210] {
19
+ .rp-radio--disabled[data-v-0cdcdcfa] {
20
20
  opacity: var(--rp-opacity-disabled);
21
21
  cursor: not-allowed;
22
22
  }
23
- .rp-radio[data-v-d9c68210] {
23
+ .rp-radio[data-v-0cdcdcfa] {
24
24
  gap: var(--rp-spacing-2);
25
25
  font-size: var(--_rp-radio-font-size);
26
26
  }
27
- .rp-radio__native[data-v-d9c68210] {
27
+ .rp-radio__native[data-v-0cdcdcfa] {
28
28
  position: absolute;
29
29
  width: 1px;
30
30
  height: 1px;
@@ -36,11 +36,11 @@
36
36
  border-width: 0;
37
37
  pointer-events: none;
38
38
  }
39
- .rp-radio__native:focus-visible + .rp-radio__dot[data-v-d9c68210] {
39
+ .rp-radio__native:focus-visible + .rp-radio__dot[data-v-0cdcdcfa] {
40
40
  border-color: var(--rp-color-control-border-focus);
41
41
  box-shadow: 0 0 0 var(--rp-border-width-medium) var(--rp-color-focus-ring);
42
42
  }
43
- .rp-radio__dot[data-v-d9c68210] {
43
+ .rp-radio__dot[data-v-0cdcdcfa] {
44
44
  display: inline-flex;
45
45
  align-items: center;
46
46
  justify-content: center;
@@ -52,7 +52,7 @@
52
52
  background-color: var(--rp-color-control-bg);
53
53
  transition: background-color var(--rp-transition-fast), border-color var(--rp-transition-fast);
54
54
  }
55
- .rp-radio__dot[data-v-d9c68210]::after {
55
+ .rp-radio__dot[data-v-0cdcdcfa]::after {
56
56
  content: "";
57
57
  width: var(--_rp-radio-dot);
58
58
  height: var(--_rp-radio-dot);
@@ -61,55 +61,55 @@
61
61
  transform: scale(0);
62
62
  transition: transform var(--rp-transition-fast);
63
63
  }
64
- .rp-radio--checked .rp-radio__dot[data-v-d9c68210] {
64
+ .rp-radio--checked .rp-radio__dot[data-v-0cdcdcfa] {
65
65
  background-color: var(--_rp-radio-checked-bg);
66
66
  border-color: var(--_rp-radio-checked-border);
67
67
  }
68
- .rp-radio--checked .rp-radio__dot[data-v-d9c68210]::after {
68
+ .rp-radio--checked .rp-radio__dot[data-v-0cdcdcfa]::after {
69
69
  transform: scale(1);
70
70
  }
71
- .rp-radio__label[data-v-d9c68210] {
71
+ .rp-radio__label[data-v-0cdcdcfa] {
72
72
  line-height: var(--rp-line-height-md);
73
73
  color: var(--rp-color-text);
74
74
  }
75
- .rp-radio--outline[data-v-d9c68210] {
75
+ .rp-radio--outline[data-v-0cdcdcfa] {
76
76
  --_rp-radio-checked-bg: var(--rp-color-control-bg);
77
77
  --_rp-radio-checked-border: var(--_rp-radio-color);
78
78
  --_rp-radio-checked-dot: var(--_rp-radio-color);
79
79
  }
80
- .rp-radio--size-xs[data-v-d9c68210] {
80
+ .rp-radio--size-xs[data-v-0cdcdcfa] {
81
81
  --_rp-radio-default-size: 16px;
82
82
  --_rp-radio-default-dot: 6px;
83
83
  --_rp-radio-font-size: var(--rp-font-size-xs);
84
84
  }
85
- .rp-radio--size-sm[data-v-d9c68210] {
85
+ .rp-radio--size-sm[data-v-0cdcdcfa] {
86
86
  --_rp-radio-default-size: 20px;
87
87
  --_rp-radio-default-dot: 8px;
88
88
  --_rp-radio-font-size: var(--rp-font-size-sm);
89
89
  }
90
- .rp-radio--size-md[data-v-d9c68210] {
90
+ .rp-radio--size-md[data-v-0cdcdcfa] {
91
91
  --_rp-radio-default-size: 24px;
92
92
  --_rp-radio-default-dot: 10px;
93
93
  --_rp-radio-font-size: var(--rp-font-size-md);
94
94
  }
95
- .rp-radio--size-lg[data-v-d9c68210] {
95
+ .rp-radio--size-lg[data-v-0cdcdcfa] {
96
96
  --_rp-radio-default-size: 30px;
97
97
  --_rp-radio-default-dot: 14px;
98
98
  --_rp-radio-font-size: var(--rp-font-size-lg);
99
99
  }
100
- .rp-radio--size-xl[data-v-d9c68210] {
100
+ .rp-radio--size-xl[data-v-0cdcdcfa] {
101
101
  --_rp-radio-default-size: 36px;
102
102
  --_rp-radio-default-dot: 16px;
103
103
  --_rp-radio-font-size: var(--rp-font-size-xl);
104
104
  }
105
105
  }@layer ropav.components {
106
- .rp-radio-group[data-v-a0fcf376] {
106
+ .rp-radio-group[data-v-fc40a22d] {
107
107
  display: flex;
108
108
  flex-direction: column;
109
109
  gap: var(--rp-spacing-3);
110
110
  font-family: var(--rp-font-family);
111
111
  }
112
- .rp-radio-group[data-orientation=horizontal][data-v-a0fcf376] {
112
+ .rp-radio-group[data-orientation=horizontal][data-v-fc40a22d] {
113
113
  flex-flow: row wrap;
114
114
  }
115
115
  }
package/dist/radio.js CHANGED
@@ -3,8 +3,9 @@ import { t as bem } from "./bem.js";
3
3
  import { n as useStylesApi, t as presence } from "./styles-api.js";
4
4
  import { n as getComponentColorValue, r as getComponentContrastColor } from "./componentColors.js";
5
5
  import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper.js";
6
+ import { i as useFormControl, o as useControllableValue } from "./useFormControl.js";
6
7
  import { t as useControlState } from "./useControlState.js";
7
- import { child, computed, createIf, createSlot, defineVaporComponent, inject, next, provide, ref, renderEffect, setDynamicProps, setInsertionState, setStaticTemplateRef, template, unref, useId } from "vue";
8
+ import { child, computed, createIf, createSlot, defineVaporComponent, inject, next, onBeforeUnmount, onMounted, provide, ref, renderEffect, setDynamicProps, setInsertionState, setStaticTemplateRef, shallowRef, template, unref, useId } from "vue";
8
9
  //#region src/components/radio/types.ts
9
10
  var radioParts = [
10
11
  "root",
@@ -34,6 +35,7 @@ function useRadio(props, emitChange) {
34
35
  const isRequired = computed(() => props.required ?? group?.required ?? false);
35
36
  const isInvalid = computed(() => props.invalid ?? group?.invalid ?? false);
36
37
  const name = computed(() => props.name ?? group?.name);
38
+ const form = computed(() => props.form ?? group?.form);
37
39
  const variant = computed(() => props.variant ?? group?.variant);
38
40
  const color = computed(() => props.color ?? group?.color);
39
41
  const autoContrast = computed(() => props.autoContrast ?? group?.autoContrast);
@@ -54,10 +56,35 @@ function useRadio(props, emitChange) {
54
56
  function focus(options) {
55
57
  inputRef.value?.focus(options);
56
58
  }
59
+ let unregister;
60
+ onMounted(() => {
61
+ if (group && inputRef.value) unregister = group.register({
62
+ input: inputRef.value,
63
+ value: () => props.value,
64
+ disabled: () => isDisabled.value,
65
+ checked: () => isChecked.value,
66
+ validationMessage: () => props.validationMessage
67
+ });
68
+ });
69
+ onBeforeUnmount(() => unregister?.());
70
+ useFormControl({
71
+ elements: () => group ? [] : [inputRef.value],
72
+ isControlled: () => true,
73
+ initializeDefault(element) {
74
+ element.defaultChecked = isChecked.value;
75
+ },
76
+ validationMessage: () => props.validationMessage,
77
+ readResetValue() {},
78
+ syncControlledValue(elements) {
79
+ elements[0].checked = isChecked.value;
80
+ }
81
+ });
57
82
  return {
58
83
  inputRef,
59
84
  control,
60
85
  name,
86
+ form,
87
+ groupInputAttrs: computed(() => group?.inputAttrs),
61
88
  isChecked,
62
89
  isDisabled,
63
90
  isRequired,
@@ -70,19 +97,54 @@ function useRadio(props, emitChange) {
70
97
  }
71
98
  function useRadioGroup(props, emitUpdate) {
72
99
  const control = useControlState(props);
100
+ const registrations = shallowRef([]);
101
+ const controllable = useControllableValue({
102
+ modelValue: () => props.modelValue,
103
+ defaultValue: () => props.defaultValue ?? null,
104
+ onChange: emitUpdate
105
+ });
73
106
  const rootClass = computed(() => bem("rp-radio-group", {
74
107
  disabled: control.disabled,
75
108
  invalid: control.invalid
76
109
  }));
77
110
  const generatedName = useId();
78
111
  const groupName = computed(() => props.name ?? `${control.id ?? generatedName}-radio`);
112
+ function register(registration) {
113
+ registrations.value = [...registrations.value, registration];
114
+ return () => {
115
+ registrations.value = registrations.value.filter((item) => item !== registration);
116
+ };
117
+ }
118
+ useFormControl({
119
+ elements: () => registrations.value.map((registration) => registration.input),
120
+ isControlled: () => controllable.isControlled.value,
121
+ initializeDefault(element) {
122
+ element.defaultChecked = registrations.value.find((item) => item.input === element)?.value() === controllable.initialValue;
123
+ },
124
+ validationMessage(element) {
125
+ const enabled = registrations.value.filter((registration) => !registration.disabled());
126
+ const anchor = enabled.find((registration) => registration.checked()) ?? enabled[0];
127
+ const registration = registrations.value.find((item) => item.input === element);
128
+ return registration?.validationMessage() ?? (registration === anchor ? props.validationMessage : void 0);
129
+ },
130
+ readResetValue(elements) {
131
+ const checked = registrations.value.find((registration) => elements.includes(registration.input) && registration.input.checked);
132
+ controllable.resetValue(checked?.value() ?? null);
133
+ },
134
+ syncControlledValue() {
135
+ for (const registration of registrations.value) registration.input.checked = registration.value() === controllable.value.value;
136
+ }
137
+ });
79
138
  provide(radioGroupKey, {
80
139
  get modelValue() {
81
- return props.modelValue;
140
+ return controllable.value.value;
82
141
  },
83
142
  get name() {
84
143
  return groupName.value;
85
144
  },
145
+ get form() {
146
+ return control.form;
147
+ },
86
148
  get disabled() {
87
149
  return control.disabled;
88
150
  },
@@ -104,19 +166,24 @@ function useRadioGroup(props, emitUpdate) {
104
166
  get size() {
105
167
  return props.size;
106
168
  },
169
+ get inputAttrs() {
170
+ return props.inputAttrs;
171
+ },
107
172
  select(value) {
108
- emitUpdate(value);
109
- }
173
+ controllable.setValue(value);
174
+ },
175
+ register
110
176
  });
111
177
  return {
112
178
  control,
113
- rootClass
179
+ rootClass,
180
+ value: controllable.value
114
181
  };
115
182
  }
116
183
  //#endregion
117
184
  //#region src/components/radio/radio.vue?vue&type=script&setup=true&vapor=true&lang.ts
118
- var t0$1 = template("<span data-v-d9c68210>");
119
- var t1 = template("<label data-v-d9c68210><input data-v-d9c68210><span data-v-d9c68210></span>", 1);
185
+ var t0$1 = template("<span data-v-0cdcdcfa>");
186
+ var t1 = template("<label data-v-0cdcdcfa><input data-v-0cdcdcfa><span data-v-0cdcdcfa></span>", 1);
120
187
  //#endregion
121
188
  //#region src/components/radio/radio.vue
122
189
  var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineVaporComponent({
@@ -126,6 +193,7 @@ var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
126
193
  props: {
127
194
  id: {},
128
195
  name: {},
196
+ form: {},
129
197
  value: {},
130
198
  checked: {
131
199
  type: Boolean,
@@ -154,6 +222,7 @@ var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
154
222
  describedby: {},
155
223
  labelledby: {},
156
224
  inputAttrs: {},
225
+ validationMessage: {},
157
226
  classNames: {},
158
227
  styles: {}
159
228
  },
@@ -161,7 +230,7 @@ var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
161
230
  setup(__props, { expose: __expose, emit: __emit, slots: $slots }) {
162
231
  const props = __props;
163
232
  const emit = __emit;
164
- const { inputRef, control, name, isChecked, isDisabled, isRequired, isInvalid, rootClass, rootStyle, onSelect, focus } = useRadio(props, (event) => emit("change", event));
233
+ const { inputRef, control, name, form, groupInputAttrs, isChecked, isDisabled, isRequired, isInvalid, rootClass, rootStyle, onSelect, focus } = useRadio(props, (event) => emit("change", event));
165
234
  const { getPartAttrs, getRootAttrs } = useStylesApi(props, "root");
166
235
  const rootAttrs = computed(() => getRootAttrs({
167
236
  class: rootClass.value,
@@ -171,17 +240,20 @@ var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
171
240
  "data-state": isChecked.value ? "checked" : "unchecked"
172
241
  }));
173
242
  const nativeInputAttrs = computed(() => {
174
- const { class: compatibilityClass, style: compatibilityStyle, onChange: compatibilityOnChange, ...attrs } = props.inputAttrs ?? {};
243
+ const { class: groupClass, style: groupStyle, onChange: groupOnChange, ...groupForwardedAttrs } = groupInputAttrs.value ?? {};
244
+ const { class: itemClass, style: itemStyle, onChange: itemOnChange, ...itemForwardedAttrs } = props.inputAttrs ?? {};
175
245
  return {
176
- ...attrs,
246
+ ...groupForwardedAttrs,
247
+ ...itemForwardedAttrs,
177
248
  ...getPartAttrs("input", {
178
249
  class: "rp-radio__native",
179
- compatibilityClass,
180
- compatibilityStyle
250
+ compatibilityClass: [groupClass, itemClass],
251
+ compatibilityStyle: [groupStyle, itemStyle]
181
252
  }),
182
253
  onChange(event) {
183
254
  onSelect(event);
184
- compatibilityOnChange?.(event);
255
+ groupOnChange?.(event);
256
+ itemOnChange?.(event);
185
257
  }
186
258
  };
187
259
  });
@@ -194,16 +266,18 @@ var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
194
266
  const n1 = next(n0, 1);
195
267
  setStaticTemplateRef(n0, inputRef, null, "inputRef");
196
268
  renderEffect(() => {
269
+ const _nativeInputAttrs = nativeInputAttrs.value;
197
270
  const _control = unref(control);
198
271
  const _isChecked = unref(isChecked);
199
272
  const _isDisabled = unref(isDisabled);
200
273
  const _isInvalid = unref(isInvalid);
201
274
  const _presence = unref(presence);
202
275
  setDynamicProps(n6, [rootAttrs.value]);
203
- setDynamicProps(n0, [nativeInputAttrs.value, {
276
+ setDynamicProps(n0, [_nativeInputAttrs, {
204
277
  id: _control.id,
205
278
  type: "radio",
206
279
  name: unref(name),
280
+ form: unref(form) ?? _nativeInputAttrs.form,
207
281
  value: __props.value,
208
282
  checked: _isChecked,
209
283
  disabled: _isDisabled || void 0,
@@ -229,10 +303,10 @@ var radio_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
229
303
  });
230
304
  return n6;
231
305
  }
232
- }), [["__scopeId", "data-v-d9c68210"]]);
306
+ }), [["__scopeId", "data-v-0cdcdcfa"]]);
233
307
  //#endregion
234
308
  //#region src/components/radio/radio-group.vue?vue&type=script&setup=true&vapor=true&lang.ts
235
- var t0 = template("<div data-v-a0fcf376>", 1);
309
+ var t0 = template("<div data-v-fc40a22d>", 1);
236
310
  //#endregion
237
311
  //#region src/components/radio/radio-group.vue
238
312
  var radio_group_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineVaporComponent({
@@ -242,7 +316,9 @@ var radio_group_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
242
316
  props: {
243
317
  id: {},
244
318
  name: {},
245
- modelValue: {},
319
+ form: {},
320
+ modelValue: { default: void 0 },
321
+ defaultValue: { default: null },
246
322
  variant: { default: void 0 },
247
323
  color: { default: void 0 },
248
324
  autoContrast: {
@@ -266,6 +342,8 @@ var radio_group_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
266
342
  ariaLabel: {},
267
343
  describedby: {},
268
344
  labelledby: {},
345
+ inputAttrs: {},
346
+ validationMessage: {},
269
347
  classNames: {},
270
348
  styles: {}
271
349
  },
@@ -297,6 +375,6 @@ var radio_group_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__
297
375
  createSlot("default", null, null, 1);
298
376
  return n1;
299
377
  }
300
- }), [["__scopeId", "data-v-a0fcf376"]]);
378
+ }), [["__scopeId", "data-v-fc40a22d"]]);
301
379
  //#endregion
302
380
  export { radioParts as i, radio_default as n, radioGroupParts as r, radio_group_default as t };