origam 2.8.0 → 2.9.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/dist/src/assets/css/main.css +1 -1
  4. package/dist/src/assets/css/tokens/dark.css +32 -0
  5. package/dist/src/assets/css/tokens/light.css +16 -0
  6. package/dist/src/assets/scss/tokens/_dark.scss +16 -0
  7. package/dist/src/assets/scss/tokens/_light.scss +16 -0
  8. package/dist/src/components/Alert/OrigamAlert.vue +4 -2
  9. package/dist/src/components/Avatar/OrigamAvatarGroup.vue +31 -12
  10. package/dist/src/components/Btn/OrigamBtn.vue +11 -2
  11. package/dist/src/components/Btn/OrigamBtnGroup.vue +204 -22
  12. package/dist/src/components/Btn/OrigamBtnToggle.vue +5 -1
  13. package/dist/src/components/Card/OrigamCard.vue +5 -2
  14. package/dist/src/components/Checkbox/OrigamCheckbox.vue +3 -1
  15. package/dist/src/components/Chip/OrigamChip.vue +2 -0
  16. package/dist/src/components/Code/OrigamCode.vue +4 -2
  17. package/dist/src/components/ColorPickerField/OrigamColorPickerField.vue +19 -2
  18. package/dist/src/components/DatePickerField/OrigamDatePickerField.vue +3 -2
  19. package/dist/src/components/Dialog/OrigamDialog.vue +1 -1
  20. package/dist/src/components/Field/OrigamField.vue +7 -0
  21. package/dist/src/components/Layout/OrigamLayout.vue +10 -1
  22. package/dist/src/components/Menu/OrigamMenu.vue +3 -1
  23. package/dist/src/components/NumberField/OrigamNumberField.vue +3 -2
  24. package/dist/src/components/Overlay/OrigamOverlayScrim.vue +2 -0
  25. package/dist/src/components/Radio/OrigamRadio.vue +3 -1
  26. package/dist/src/components/Select/OrigamSelect.vue +3 -1
  27. package/dist/src/components/SelectionControl/OrigamSelectionControl.vue +2 -0
  28. package/dist/src/components/SliderField/OrigamSliderField.vue +3 -1
  29. package/dist/src/components/Snackbar/OrigamSnackbar.vue +3 -1
  30. package/dist/src/components/Snackbar/OrigamSnackbarItem.vue +2 -0
  31. package/dist/src/components/Switch/OrigamSwitch.vue +42 -1
  32. package/dist/src/components/Switch/OrigamSwitchTrack.vue +35 -2
  33. package/dist/src/components/Table/OrigamTable.vue +4 -2
  34. package/dist/src/components/Tabs/OrigamTabs.vue +3 -1
  35. package/dist/src/components/Tooltip/OrigamTooltip.vue +2 -0
  36. package/dist/src/composables/Commons/defaults.composable.cjs +10 -6
  37. package/dist/src/composables/Commons/defaults.composable.d.ts +30 -0
  38. package/dist/src/composables/Commons/defaults.composable.js +9 -6
  39. package/dist/src/composables/Theme/theme.composable.cjs +49 -30
  40. package/dist/src/composables/Theme/theme.composable.js +41 -22
  41. package/dist/src/interfaces/App/app.interface.d.ts +1 -1
  42. package/dist/src/interfaces/Btn/btn-group.interface.d.ts +2 -2
  43. package/dist/src/interfaces/Switch/switch-track.interface.d.ts +23 -6
  44. package/dist/src/themes/origam.theme.cjs +2 -4
  45. package/dist/src/themes/origam.theme.js +2 -2
  46. package/dist/src/types/tokens.type.d.ts +1 -1
  47. package/dist/src/utils/Commons/commons.util.cjs +10 -0
  48. package/dist/src/utils/Commons/commons.util.d.ts +18 -0
  49. package/dist/src/utils/Commons/commons.util.js +9 -0
  50. package/package.json +1 -1
@@ -289,6 +289,7 @@ import {
289
289
  OrigamVirtualScroll
290
290
  } from "../../components";
291
291
  import {
292
+ useDefaults,
292
293
  useFilter,
293
294
  useItems,
294
295
  useLocale,
@@ -310,7 +311,7 @@ import {
310
311
  TEXT_FIELD_TYPE
311
312
  } from "../../enums";
312
313
  import { deepEqual, forwardRefs, getUid, matchesSelector, noop, wrapInArray } from "../../utils";
313
- const props = defineProps({
314
+ const _props = defineProps({
314
315
  chips: { type: Boolean, required: false },
315
316
  closableChips: { type: Boolean, required: false },
316
317
  hideNoData: { type: Boolean, required: false },
@@ -454,6 +455,7 @@ const props = defineProps({
454
455
  noFilter: { type: Boolean, required: false },
455
456
  eager: { type: Boolean, required: false }
456
457
  });
458
+ const props = useDefaults(_props);
457
459
  defineEmits(["click:control", "mousedown:control", "update:menu", "update:modelValue", "update:focused", "click:append", "click:prepend", "click:appendInner", "click:prependInner", "click:clear"]);
458
460
  const { filterProps } = useProps(props);
459
461
  const { t } = useLocale();
@@ -283,6 +283,8 @@ defineExpose(forwardRefs({
283
283
  justify-content: center;
284
284
  position: relative;
285
285
  border-radius: 50%;
286
+ backdrop-filter: var(--origam-selection-control__input---backdrop-filter, none);
287
+ -webkit-backdrop-filter: var(--origam-selection-control__input---backdrop-filter, none);
286
288
 
287
289
  > .origam-icon {
288
290
  opacity: 0.7;
@@ -423,6 +423,7 @@ import { computed, ref, StyleValue, useSlots, WritableComputedRef } from "vue";
423
423
  import { OrigamInput, OrigamLabel, OrigamSliderFieldTrack } from "../../components";
424
424
  import {
425
425
  useBackgroundColor,
426
+ useDefaults,
426
427
  useFocus,
427
428
  useProps,
428
429
  useRtl,
@@ -433,7 +434,7 @@ import {
433
434
  } from "../../composables";
434
435
  import { DENSITY, DIRECTION, SLIDER_FIELD_VARIANT } from "../../enums";
435
436
  import { clamp, convertToUnit, createRange, omit } from "../../utils";
436
- const props = defineProps({
437
+ const _props = defineProps({
437
438
  disabled: { type: Boolean, required: false },
438
439
  required: { type: Boolean, required: false },
439
440
  label: { type: String, required: false },
@@ -527,6 +528,7 @@ const props = defineProps({
527
528
  letterSpacing: { type: String, required: false },
528
529
  ripple: { type: [Boolean, Object], required: false }
529
530
  });
531
+ const props = useDefaults(_props);
530
532
  const emits = defineEmits(["start", "end", "update:modelValue", "update:focused"]);
531
533
  const { filterProps } = useProps(props);
532
534
  const origamInputRef = ref();
@@ -91,6 +91,7 @@ import { OrigamOverlay, OrigamSnack } from "../../components";
91
91
  import OrigamSnackbarItem from "./OrigamSnackbarItem.vue";
92
92
  import {
93
93
  useBothColor,
94
+ useDefaults,
94
95
  useLayout,
95
96
  usePosition,
96
97
  useProps,
@@ -104,7 +105,7 @@ import {
104
105
  import { ORIGAM_LAYOUT_KEY } from "../../consts";
105
106
  import { SCROLL_STRATEGIES } from "../../enums";
106
107
  import { forwardRefs } from "../../utils";
107
- const props = defineProps({
108
+ const _props = defineProps({
108
109
  multiLine: { type: Boolean, required: false },
109
110
  text: { type: String, required: false },
110
111
  timer: { type: [Boolean, String], required: false },
@@ -200,6 +201,7 @@ const props = defineProps({
200
201
  activeClass: { type: String, required: false },
201
202
  hover: { type: [Boolean, Object], required: false }
202
203
  });
204
+ const props = useDefaults(_props);
203
205
  const { filterProps } = useProps(props);
204
206
  const slots = useSlots();
205
207
  const isActive = useVModel(props, "modelValue");
@@ -168,6 +168,8 @@ defineExpose({ filterProps });
168
168
  border-color: var(--origam-snackbar-item---border-color, var(--origam-color__border---subtle, rgba(0, 0, 0, 0.12)));
169
169
  color: var(--origam-snackbar-item---color, var(--origam-color__text---primary, #1a1a1a));
170
170
  box-shadow: var(--origam-snackbar-item---box-shadow, 0 4px 12px rgba(0, 0, 0, 0.12));
171
+ backdrop-filter: var(--origam-snackbar-item---backdrop-filter, none);
172
+ -webkit-backdrop-filter: var(--origam-snackbar-item---backdrop-filter, none);
171
173
  font-size: var(--origam-snackbar-item---font-size, 0.875rem);
172
174
  line-height: 1.4;
173
175
 
@@ -39,6 +39,7 @@
39
39
  >
40
40
  <template #default="{bgColor: scBgColor}">
41
41
  <origam-switch-track
42
+ ref="origamSwitchTrackRef"
42
43
  :bg-color="scBgColor"
43
44
  :disabled="isDisabled"
44
45
  :error="isValid === false"
@@ -46,6 +47,7 @@
46
47
  :is-valid="isValid"
47
48
  :model-value="model"
48
49
  :readonly="isReadonly"
50
+ v-bind="{ ...trackProps }"
49
51
  @click="handleTrackClick"
50
52
  >
51
53
  <template
@@ -97,6 +99,7 @@
97
99
  />
98
100
  <div
99
101
  :class="getSwitchThumbClasses(icon)"
102
+ :style="switchThumbStyles"
100
103
  >
101
104
  <origam-translate-scale>
102
105
  <div
@@ -142,17 +145,19 @@ import {
142
145
  OrigamTranslateScale
143
146
  } from "../../components";
144
147
  import {
148
+ useDefaults,
145
149
  useFocus,
146
150
  useHover,
147
151
  useLoader,
148
152
  useProps,
153
+ useRounded,
149
154
  useStateEffect,
150
155
  useStyle,
151
156
  useVModel
152
157
  } from "../../composables";
153
158
  import { DENSITY, PROGRESS_TYPE, SIZES } from "../../enums";
154
159
  import { filterInputAttrs, getUid } from "../../utils";
155
- const props = defineProps({
160
+ const _props = defineProps({
156
161
  indeterminate: { type: Boolean, required: false },
157
162
  inset: { type: Boolean, required: false },
158
163
  flat: { type: Boolean, required: false },
@@ -250,12 +255,14 @@ const props = defineProps({
250
255
  loading: { type: [Boolean, Number, Object], required: false },
251
256
  loadingText: { type: String, required: false }
252
257
  });
258
+ const props = useDefaults(_props);
253
259
  defineEmits(["update:modelValue", "update:focused", "update:indeterminate", "click:label"]);
254
260
  const { isHover, hoverState } = useHover(props);
255
261
  useStateEffect(props, isHover, void 0, hoverState, void 0);
256
262
  const { filterProps } = useProps(props);
257
263
  const origamSelectionControlRef = ref();
258
264
  const origamInputRef = ref();
265
+ const origamSwitchTrackRef = ref();
259
266
  const indeterminate = useVModel(props, "indeterminate");
260
267
  const model = useVModel(props, "modelValue");
261
268
  const { isFocused, onFocus: handleFocus, onBlur: handleBlur } = useFocus(props);
@@ -282,6 +289,9 @@ const inputProps = computed(() => {
282
289
  const controlProps = computed(() => {
283
290
  return origamSelectionControlRef.value?.filterProps(props, ["modelValue", "type", "disabled", "readonly", "class", "style", "id"]);
284
291
  });
292
+ const trackProps = computed(() => {
293
+ return origamSwitchTrackRef.value?.filterProps(props, ["modelValue", "disabled", "readonly", "error", "inset", "isValid", "bgColor", "color", "class", "style", "id"]);
294
+ });
285
295
  const isSkeletonLoading = computed(() => {
286
296
  return loaderConfig.value.isActive && loaderConfig.value.kind === "skeleton";
287
297
  });
@@ -298,6 +308,12 @@ const switchStyles = computed(() => {
298
308
  props.style
299
309
  ];
300
310
  });
311
+ const { roundedStyles: thumbRoundedStyles } = useRounded(props, "origam-switch__thumb");
312
+ const switchThumbStyles = computed(() => {
313
+ return [
314
+ thumbRoundedStyles.value
315
+ ];
316
+ });
301
317
  const getSwitchThumbClasses = (icon) => {
302
318
  return [
303
319
  "origam-switch__thumb",
@@ -446,6 +462,31 @@ defineExpose({
446
462
  .origam-selection-control {
447
463
  min-height: calc(var(--origam-switch__selection-control---min-height, 56px) + var(--origam-input---density, 0px));
448
464
 
465
+ /*
466
+ * The base SelectionControl wrapper reserves a fixed
467
+ * `calc(40px + 1.5 * density)` box sized for a checkbox/radio
468
+ * input glyph. Switch instead renders a variable-width track
469
+ * (36–52px depending on `--origam-switch__track---width` /
470
+ * the `inset` variant, plus its own horizontal padding) as
471
+ * the wrapper's only in-flow child — `__input` is forced
472
+ * `position: absolute` below so it no longer participates in
473
+ * the flex layout. That fixed formula was never track-aware:
474
+ * under `density="compact"` (-8px) the wrapper shrinks to
475
+ * 28px while the standard track needs ~46px, so the track
476
+ * visually overflows into the neighbouring label ("Flat"
477
+ * rendering as "lat"). Let the wrapper size to its actual
478
+ * content (`max-content`) instead, keeping the original
479
+ * density formula only as a floor via `min-width`/`min-height`
480
+ * so the checkbox-sized footprint is preserved whenever the
481
+ * track is smaller than it (never smaller than before).
482
+ */
483
+ :deep(.origam-selection-control__wrapper) {
484
+ width: max-content;
485
+ height: max-content;
486
+ min-width: calc(40px + 1.5 * var(--origam-selection-control--density, 0px));
487
+ min-height: calc(40px + 1.5 * var(--origam-selection-control--density, 0px));
488
+ }
489
+
449
490
  :deep(.origam-selection-control__input) {
450
491
  border-radius: 50%;
451
492
  transition: 0.2s transform cubic-bezier(0.4, 0, 0.2, 1);
@@ -37,7 +37,10 @@
37
37
  import { computed, StyleValue, useSlots } from "vue";
38
38
  import {
39
39
  useBackgroundColor,
40
+ useBorder,
41
+ useElevation,
40
42
  useProps,
43
+ useRounded,
41
44
  useStyle
42
45
  } from "../../composables";
43
46
  const props = defineProps({
@@ -45,13 +48,32 @@ const props = defineProps({
45
48
  isValid: { type: [Boolean, null], required: false, default: null },
46
49
  disabled: { type: Boolean, required: false, default: false },
47
50
  readonly: { type: Boolean, required: false, default: false },
48
- error: { type: Boolean, required: false, default: false },
51
+ error: { type: [String, Boolean], required: false, default: false },
49
52
  inset: { type: Boolean, required: false, default: false },
50
53
  id: { type: String, required: false },
51
54
  class: { type: [String, Array, Object], required: false },
52
55
  style: { type: [String, Array, Object, Boolean, null], required: false, skipCheck: true },
53
56
  color: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
54
- bgColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true }
57
+ bgColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
58
+ border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
59
+ borderTop: { type: [Boolean, Number, String], required: false },
60
+ borderLeft: { type: [Boolean, Number, String], required: false },
61
+ borderBottom: { type: [Boolean, Number, String], required: false },
62
+ borderRight: { type: [Boolean, Number, String], required: false },
63
+ borderBlock: { type: [Boolean, Number, String], required: false },
64
+ borderInline: { type: [Boolean, Number, String], required: false },
65
+ borderColor: { type: String, required: false },
66
+ borderStyle: { type: String, required: false },
67
+ borderTopColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
68
+ borderRightColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
69
+ borderBottomColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
70
+ borderLeftColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
71
+ rounded: { type: [Boolean, Number, String, null], required: false, skipCheck: true },
72
+ roundedTopRight: { type: [Boolean, Number, String], required: false },
73
+ roundedTopLeft: { type: [Boolean, Number, String], required: false },
74
+ roundedBottomLeft: { type: [Boolean, Number, String], required: false },
75
+ roundedBottomRight: { type: [Boolean, Number, String], required: false },
76
+ elevation: { type: [Number, String], required: false }
55
77
  });
56
78
  const emits = defineEmits(["click", "update:modelValue"]);
57
79
  defineSlots();
@@ -62,6 +84,9 @@ const trackBgColor = computed(() => {
62
84
  return props.bgColor;
63
85
  });
64
86
  const { backgroundColorStyles } = useBackgroundColor(trackBgColor);
87
+ const { borderClasses, borderStyles } = useBorder(props);
88
+ const { roundedClasses, roundedStyles } = useRounded(props);
89
+ const { elevationClasses, elevationStyles } = useElevation(props);
65
90
  const slotProps = computed(() => ({
66
91
  model: props.modelValue,
67
92
  isValid: props.isValid
@@ -75,6 +100,9 @@ const handleClick = (e) => {
75
100
  const switchTrackStyles = computed(() => {
76
101
  return [
77
102
  backgroundColorStyles.value,
103
+ borderStyles.value,
104
+ roundedStyles.value,
105
+ elevationStyles.value,
78
106
  props.style
79
107
  ];
80
108
  });
@@ -88,6 +116,9 @@ const switchTrackClasses = computed(() => {
88
116
  "origam-switch-track--error": props.error,
89
117
  "origam-switch-track--inset": props.inset
90
118
  },
119
+ borderClasses.value,
120
+ roundedClasses.value,
121
+ elevationClasses.value,
91
122
  props.class
92
123
  ];
93
124
  });
@@ -115,6 +146,8 @@ defineExpose({
115
146
  border-radius: var(--origam-switch__track---border-radius, 9999px);
116
147
  height: var(--origam-switch__track---height, 14px);
117
148
  min-width: var(--origam-switch__track---width, 36px);
149
+ backdrop-filter: var(--origam-switch__track---backdrop-filter, none);
150
+ -webkit-backdrop-filter: var(--origam-switch__track---backdrop-filter, none);
118
151
  cursor: pointer;
119
152
  transition: 0.2s background-color cubic-bezier(0.4, 0, 0.2, 1);
120
153
  overflow: hidden;
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <component
3
- :is="tag"
3
+ :is="props.tag"
4
4
  :class="tableClasses"
5
5
  :style="tableStyles"
6
6
  @mouseenter="onMouseenter"
@@ -31,6 +31,7 @@
31
31
  <script lang="ts" setup>
32
32
  import { computed, StyleValue, useSlots } from "vue";
33
33
  import {
34
+ useDefaults,
34
35
  useDensity,
35
36
  useDimension,
36
37
  useHover,
@@ -40,7 +41,7 @@ import {
40
41
  useTypography
41
42
  } from "../../composables";
42
43
  import { DENSITY } from "../../enums";
43
- const props = defineProps({
44
+ const _props = defineProps({
44
45
  fixedHeader: { type: Boolean, required: false },
45
46
  fixedFooter: { type: Boolean, required: false },
46
47
  caption: { type: String, required: false },
@@ -97,6 +98,7 @@ const props = defineProps({
97
98
  lineHeight: { type: String, required: false },
98
99
  letterSpacing: { type: String, required: false }
99
100
  });
101
+ const props = useDefaults(_props);
100
102
  const { filterProps } = useProps(props);
101
103
  const slots = useSlots();
102
104
  const { densityClasses } = useDensity(props);
@@ -22,6 +22,7 @@
22
22
  import { computed, ref, StyleValue } from "vue";
23
23
  import { OrigamDefaultsProvider } from "../../components";
24
24
  import {
25
+ useDefaults,
25
26
  useDensity,
26
27
  useGroup,
27
28
  useProps,
@@ -30,7 +31,7 @@ import {
30
31
  } from "../../composables";
31
32
  import { ORIGAM_TABS_KEY } from "../../consts";
32
33
  import { DENSITY, DIRECTION, TAB_VARIANT } from "../../enums";
33
- const props = defineProps({
34
+ const _props = defineProps({
34
35
  tag: { type: String, required: false, default: "div" },
35
36
  variant: { type: null, required: false, default: TAB_VARIANT.DEFAULT },
36
37
  fixed: { type: Boolean, required: false, default: false },
@@ -54,6 +55,7 @@ const props = defineProps({
54
55
  max: { type: Number, required: false },
55
56
  selectedClass: { type: String, required: false, default: "origam-tab--active" }
56
57
  });
58
+ const props = useDefaults(_props);
57
59
  defineEmits(["update:modelValue"]);
58
60
  const { filterProps } = useProps(props);
59
61
  const { isSelected, select, next, prev, selected, items } = useGroup(props, ORIGAM_TABS_KEY);
@@ -165,6 +165,8 @@ defineExpose(forwardRefs({
165
165
  .origam-tooltip__content {
166
166
  background-color: var(--origam-tooltip---background-color, var(--origam-color__neutral---800));
167
167
  color: var(--origam-tooltip---color, var(--origam-color__text---inverse));
168
+ backdrop-filter: var(--origam-tooltip---backdrop-filter, none);
169
+ -webkit-backdrop-filter: var(--origam-tooltip---backdrop-filter, none);
168
170
  font-size: var(--origam-tooltip---font-size, 0.75rem);
169
171
  font-weight: var(--origam-tooltip---font-weight, 500);
170
172
  line-height: var(--origam-tooltip---line-height, 1.5);
@@ -6,24 +6,28 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.createDefaults = createDefaults;
7
7
  exports.provideDefaults = provideDefaults;
8
8
  exports.useDefaults = useDefaults;
9
+ exports.usePassedProps = usePassedProps;
9
10
  var _vue = require("vue");
10
11
  var _consts = require("../../consts/index.cjs");
11
12
  var _utils = require("../../utils/index.cjs");
12
13
  function camelize(str) {
13
14
  return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
14
15
  }
15
- function useDefaults(props, name = (0, _utils.getCurrentInstanceName)()) {
16
- const defaults = (0, _vue.inject)(_consts.ORIGAM_DEFAULTS_KEY, (0, _vue.ref)({}));
17
- const vm = (0, _utils.getCurrentInstance)("useDefaults");
18
- const propNames = Object.keys(props);
19
- if (!propNames.length) return props;
20
- const wasPropPassed = key => {
16
+ function usePassedProps(_props, instanceLabel = "usePassedProps") {
17
+ const vm = (0, _utils.getCurrentInstance)(instanceLabel);
18
+ return key => {
21
19
  const vnodeProps = vm.vnode.props || {};
22
20
  for (const k in vnodeProps) {
23
21
  if (k === key || camelize(k) === key) return true;
24
22
  }
25
23
  return false;
26
24
  };
25
+ }
26
+ function useDefaults(props, name = (0, _utils.getCurrentInstanceName)()) {
27
+ const defaults = (0, _vue.inject)(_consts.ORIGAM_DEFAULTS_KEY, (0, _vue.ref)({}));
28
+ const propNames = Object.keys(props);
29
+ if (!propNames.length) return props;
30
+ const wasPropPassed = usePassedProps(props, "useDefaults");
27
31
  const result = {};
28
32
  for (const key of propNames) {
29
33
  const c = (0, _vue.computed)(() => {
@@ -1,5 +1,35 @@
1
1
  import { type Ref } from 'vue';
2
2
  import type { IDefault } from '../../interfaces';
3
+ /**
4
+ * Was-prop-passed factory.
5
+ *
6
+ * @description
7
+ * Component-side primitive: for the CURRENT component instance, returns a
8
+ * predicate telling whether a given prop key was explicitly written by the
9
+ * parent template (`vnode.props`) — as opposed to resolved from a default
10
+ * (`withDefaults()`, or Vue's own boolean-prop coercion).
11
+ *
12
+ * This matters beyond `useDefaults()` itself: any component that FORWARDS
13
+ * its own props down to descendants as `<OrigamDefaultsProvider>` entries
14
+ * (e.g. `OrigamAvatarGroup` → `origam-avatar`, `OrigamBtnGroup` → `origam-btn`)
15
+ * must use this — not a plain `!== undefined` check — to decide whether to
16
+ * forward a value. Reason: Vue resolves an UNSET prop whose declared type
17
+ * *includes* `boolean` (e.g. `border?: boolean | string`, `rounded?: boolean
18
+ * | TRounded`) to the concrete value `false`, never to `undefined`. A naive
19
+ * `omitUndefined()` over the forwarded map therefore still ships an explicit
20
+ * `false` for `border`/`rounded` even when the consumer never set them,
21
+ * which then wins the `mergeDeep` against an ancestor/theme default (e.g.
22
+ * `origam-avatar: { border: true }`) — see #263.
23
+ *
24
+ * MUST be re-read on every resolution (not captured once), for the same
25
+ * reason `useDefaults()` re-reads it: a parent binding through a dynamic
26
+ * `v-bind` whose object starts empty (`childRef?.filterProps(...)` before
27
+ * mount) only fills `vnode.props` on a later render.
28
+ */
29
+ /*********************************************************
30
+ * usePassedProps
31
+ ********************************************************/
32
+ export declare function usePassedProps<T extends Record<string, any>>(_props: T, instanceLabel?: string): (key: Extract<keyof T, string> | string) => boolean;
3
33
  /**
4
34
  * Component-side hook: resolve `props` against the closest DefaultsProvider.
5
35
  *
@@ -4,18 +4,21 @@ import { getCurrentInstance, getCurrentInstanceName, mergeDeep } from "../../uti
4
4
  function camelize(str) {
5
5
  return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
6
6
  }
7
- export function useDefaults(props, name = getCurrentInstanceName()) {
8
- const defaults = inject(ORIGAM_DEFAULTS_KEY, ref({}));
9
- const vm = getCurrentInstance("useDefaults");
10
- const propNames = Object.keys(props);
11
- if (!propNames.length) return props;
12
- const wasPropPassed = (key) => {
7
+ export function usePassedProps(_props, instanceLabel = "usePassedProps") {
8
+ const vm = getCurrentInstance(instanceLabel);
9
+ return (key) => {
13
10
  const vnodeProps = vm.vnode.props || {};
14
11
  for (const k in vnodeProps) {
15
12
  if (k === key || camelize(k) === key) return true;
16
13
  }
17
14
  return false;
18
15
  };
16
+ }
17
+ export function useDefaults(props, name = getCurrentInstanceName()) {
18
+ const defaults = inject(ORIGAM_DEFAULTS_KEY, ref({}));
19
+ const propNames = Object.keys(props);
20
+ if (!propNames.length) return props;
21
+ const wasPropPassed = usePassedProps(props, "useDefaults");
19
22
  const result = {};
20
23
  for (const key of propNames) {
21
24
  const c = computed(() => {
@@ -10,30 +10,47 @@ exports.readPersistedMode = readPersistedMode;
10
10
  exports.readPersistedTheme = readPersistedTheme;
11
11
  exports.useTheme = useTheme;
12
12
  var _vue = require("vue");
13
- var _theme2 = require("../../consts/Theme/theme.const.cjs");
14
- let _theme = null;
15
- let _mode = null;
16
- let _systemPrefersDark = null;
17
- let _mediaInitDone = false;
13
+ var _theme = require("../../consts/Theme/theme.const.cjs");
14
+ const ORIGAM_THEME_SINGLETON_KEY = "__origamThemeSingleton__";
15
+ const _serverSingleton = {
16
+ theme: null,
17
+ mode: null,
18
+ systemPrefersDark: null,
19
+ mediaInitDone: false
20
+ };
21
+ function themeSingleton() {
22
+ if (typeof window === "undefined") return _serverSingleton;
23
+ const globalScope = globalThis;
24
+ if (!globalScope[ORIGAM_THEME_SINGLETON_KEY]) {
25
+ globalScope[ORIGAM_THEME_SINGLETON_KEY] = {
26
+ theme: null,
27
+ mode: null,
28
+ systemPrefersDark: null,
29
+ mediaInitDone: false
30
+ };
31
+ }
32
+ return globalScope[ORIGAM_THEME_SINGLETON_KEY];
33
+ }
18
34
  function ensureSystemPreference() {
19
- if (_systemPrefersDark === null) {
20
- _systemPrefersDark = (0, _vue.ref)(false);
35
+ const state = themeSingleton();
36
+ if (state.systemPrefersDark === null) {
37
+ state.systemPrefersDark = (0, _vue.ref)(false);
21
38
  }
22
- if (_mediaInitDone || typeof window === "undefined" || typeof window.matchMedia !== "function") {
23
- return _systemPrefersDark;
39
+ if (state.mediaInitDone || typeof window === "undefined" || typeof window.matchMedia !== "function") {
40
+ return state.systemPrefersDark;
24
41
  }
25
- _mediaInitDone = true;
42
+ state.mediaInitDone = true;
26
43
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
27
- _systemPrefersDark.value = mq.matches;
44
+ state.systemPrefersDark.value = mq.matches;
28
45
  mq.addEventListener?.("change", e => {
29
- if (_systemPrefersDark) _systemPrefersDark.value = e.matches;
46
+ if (state.systemPrefersDark) state.systemPrefersDark.value = e.matches;
30
47
  });
31
- return _systemPrefersDark;
48
+ return state.systemPrefersDark;
32
49
  }
33
50
  function readPersisted() {
34
51
  if (typeof window === "undefined") return "auto";
35
52
  try {
36
- const v = window.localStorage?.getItem(_theme2.ORIGAM_THEME_STORAGE_KEY);
53
+ const v = window.localStorage?.getItem(_theme.ORIGAM_THEME_STORAGE_KEY);
37
54
  if (v === "auto" || v === "light" || v === "dark") return v;
38
55
  if (v && typeof v === "string") return v;
39
56
  } catch {}
@@ -42,21 +59,21 @@ function readPersisted() {
42
59
  function writePersisted(theme) {
43
60
  if (typeof window === "undefined") return;
44
61
  try {
45
- window.localStorage?.setItem(_theme2.ORIGAM_THEME_STORAGE_KEY, theme);
62
+ window.localStorage?.setItem(_theme.ORIGAM_THEME_STORAGE_KEY, theme);
46
63
  } catch {}
47
64
  }
48
65
  function applyToDocument(theme) {
49
66
  if (typeof document === "undefined") return;
50
67
  if (theme === "auto") {
51
- document.documentElement.removeAttribute(_theme2.ORIGAM_THEME_ATTR);
68
+ document.documentElement.removeAttribute(_theme.ORIGAM_THEME_ATTR);
52
69
  } else {
53
- document.documentElement.setAttribute(_theme2.ORIGAM_THEME_ATTR, theme);
70
+ document.documentElement.setAttribute(_theme.ORIGAM_THEME_ATTR, theme);
54
71
  }
55
72
  }
56
73
  function readPersistedModeValue() {
57
74
  if (typeof window === "undefined") return "auto";
58
75
  try {
59
- const v = window.localStorage?.getItem(_theme2.ORIGAM_MODE_STORAGE_KEY);
76
+ const v = window.localStorage?.getItem(_theme.ORIGAM_MODE_STORAGE_KEY);
60
77
  if (v === "auto" || v === "light" || v === "dark") return v;
61
78
  } catch {}
62
79
  return "auto";
@@ -64,22 +81,23 @@ function readPersistedModeValue() {
64
81
  function writePersistedMode(mode) {
65
82
  if (typeof window === "undefined") return;
66
83
  try {
67
- window.localStorage?.setItem(_theme2.ORIGAM_MODE_STORAGE_KEY, mode);
84
+ window.localStorage?.setItem(_theme.ORIGAM_MODE_STORAGE_KEY, mode);
68
85
  } catch {}
69
86
  }
70
87
  function applyModeToDocument(resolvedMode) {
71
88
  if (typeof document === "undefined") return;
72
- document.documentElement.setAttribute(_theme2.ORIGAM_MODE_ATTR, resolvedMode);
89
+ document.documentElement.setAttribute(_theme.ORIGAM_MODE_ATTR, resolvedMode);
73
90
  }
74
91
  function useTheme() {
75
- if (_theme === null) {
76
- _theme = (0, _vue.ref)(readPersisted());
92
+ const state = themeSingleton();
93
+ if (state.theme === null) {
94
+ state.theme = (0, _vue.ref)(readPersisted());
77
95
  }
78
- if (_mode === null) {
79
- _mode = (0, _vue.ref)(readPersistedModeValue());
96
+ if (state.mode === null) {
97
+ state.mode = (0, _vue.ref)(readPersistedModeValue());
80
98
  }
81
- const theme = _theme;
82
- const mode = _mode;
99
+ const theme = state.theme;
100
+ const mode = state.mode;
83
101
  const systemPrefersDark = ensureSystemPreference();
84
102
  const resolved = (0, _vue.computed)(() => {
85
103
  if (theme.value === "auto") return systemPrefersDark.value ? "dark" : "light";
@@ -146,8 +164,9 @@ function readPersistedMode() {
146
164
  return readPersistedModeValue();
147
165
  }
148
166
  function _resetThemeForTesting() {
149
- _theme = null;
150
- _mode = null;
151
- _systemPrefersDark = null;
152
- _mediaInitDone = false;
167
+ const state = themeSingleton();
168
+ state.theme = null;
169
+ state.mode = null;
170
+ state.systemPrefersDark = null;
171
+ state.mediaInitDone = false;
153
172
  }