origam 2.27.0 → 2.29.1

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 (40) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/src/assets/css/main.css +1 -1
  3. package/dist/src/assets/css/tokens/dark.css +2 -0
  4. package/dist/src/assets/css/tokens/light.css +1 -0
  5. package/dist/src/assets/scss/tokens/_dark.scss +2 -0
  6. package/dist/src/assets/scss/tokens/_light.scss +1 -0
  7. package/dist/src/components/Btn/OrigamBtn.vue +53 -14
  8. package/dist/src/components/Chart/OrigamChartCartesian.vue +1 -1
  9. package/dist/src/components/Chart/OrigamChartPolar.vue +1 -1
  10. package/dist/src/components/Chip/OrigamChip.vue +1 -1
  11. package/dist/src/components/Code/OrigamCode.vue +0 -2
  12. package/dist/src/composables/Commons/style.composable.cjs +14 -1
  13. package/dist/src/composables/Commons/style.composable.d.ts +0 -14
  14. package/dist/src/composables/Commons/style.composable.js +13 -2
  15. package/dist/src/composables/Commons/styleTag.composable.cjs +10 -4
  16. package/dist/src/composables/Commons/styleTag.composable.js +11 -5
  17. package/dist/src/consts/Commons/ssr-style.const.cjs +8 -0
  18. package/dist/src/consts/Commons/ssr-style.const.d.ts +19 -0
  19. package/dist/src/consts/Commons/ssr-style.const.js +2 -0
  20. package/dist/src/consts/index.cjs +11 -0
  21. package/dist/src/consts/index.d.ts +1 -0
  22. package/dist/src/consts/index.js +1 -0
  23. package/dist/src/directives/Contrast/contrast.directive.cjs +58 -6
  24. package/dist/src/directives/Contrast/contrast.directive.d.ts +10 -0
  25. package/dist/src/directives/Contrast/contrast.directive.js +61 -9
  26. package/dist/src/interfaces/Btn/btn.interface.d.ts +1 -0
  27. package/dist/src/interfaces/Commons/commons.interface.d.ts +14 -0
  28. package/dist/src/interfaces/Commons/style.interface.d.ts +21 -0
  29. package/dist/src/nuxt/plugin.server.cjs +18 -0
  30. package/dist/src/nuxt/plugin.server.js +9 -0
  31. package/dist/src/origam.cjs +3 -0
  32. package/dist/src/origam.js +5 -1
  33. package/dist/src/types/tokens.type.d.ts +1 -1
  34. package/dist/src/utils/Commons/ssr-styles.util.cjs +41 -0
  35. package/dist/src/utils/Commons/ssr-styles.util.d.ts +6 -0
  36. package/dist/src/utils/Commons/ssr-styles.util.js +26 -0
  37. package/dist/src/utils/index.cjs +11 -0
  38. package/dist/src/utils/index.d.ts +1 -0
  39. package/dist/src/utils/index.js +1 -0
  40. package/package.json +1 -1
@@ -319,6 +319,7 @@
319
319
  --origam-btn---padding-md: var(--origam-space---4);
320
320
  --origam-btn---padding-lg: var(--origam-space---5);
321
321
  --origam-btn---padding-xl: var(--origam-space---6);
322
+ --origam-btn---padding-block: 0px;
322
323
  --origam-btn---font-size: var(--origam-font__size---md);
323
324
  --origam-btn---font-weight: var(--origam-font__weight---medium);
324
325
  --origam-btn---letter-spacing: var(--origam-font__letterSpacing---widest);
@@ -3332,6 +3333,7 @@
3332
3333
  --origam-btn---padding-md: var(--origam-space---4);
3333
3334
  --origam-btn---padding-lg: var(--origam-space---5);
3334
3335
  --origam-btn---padding-xl: var(--origam-space---6);
3336
+ --origam-btn---padding-block: 0px;
3335
3337
  --origam-btn---font-size: var(--origam-font__size---md);
3336
3338
  --origam-btn---font-weight: var(--origam-font__weight---medium);
3337
3339
  --origam-btn---letter-spacing: var(--origam-font__letterSpacing---widest);
@@ -219,6 +219,7 @@
219
219
  --origam-btn---padding-md: var(--origam-space---4);
220
220
  --origam-btn---padding-lg: var(--origam-space---5);
221
221
  --origam-btn---padding-xl: var(--origam-space---6);
222
+ --origam-btn---padding-block: 0px;
222
223
  --origam-btn---font-size: var(--origam-font__size---md);
223
224
  --origam-btn---font-weight: var(--origam-font__weight---medium);
224
225
  --origam-btn---letter-spacing: var(--origam-font__letterSpacing---widest);
@@ -319,6 +319,7 @@
319
319
  --origam-btn---padding-md: var(--origam-space---4);
320
320
  --origam-btn---padding-lg: var(--origam-space---5);
321
321
  --origam-btn---padding-xl: var(--origam-space---6);
322
+ --origam-btn---padding-block: 0px;
322
323
  --origam-btn---font-size: var(--origam-font__size---md);
323
324
  --origam-btn---font-weight: var(--origam-font__weight---medium);
324
325
  --origam-btn---letter-spacing: var(--origam-font__letterSpacing---widest);
@@ -3332,6 +3333,7 @@
3332
3333
  --origam-btn---padding-md: var(--origam-space---4);
3333
3334
  --origam-btn---padding-lg: var(--origam-space---5);
3334
3335
  --origam-btn---padding-xl: var(--origam-space---6);
3336
+ --origam-btn---padding-block: 0px;
3335
3337
  --origam-btn---font-size: var(--origam-font__size---md);
3336
3338
  --origam-btn---font-weight: var(--origam-font__weight---medium);
3337
3339
  --origam-btn---letter-spacing: var(--origam-font__letterSpacing---widest);
@@ -219,6 +219,7 @@
219
219
  --origam-btn---padding-md: var(--origam-space---4);
220
220
  --origam-btn---padding-lg: var(--origam-space---5);
221
221
  --origam-btn---padding-xl: var(--origam-space---6);
222
+ --origam-btn---padding-block: 0px;
222
223
  --origam-btn---font-size: var(--origam-font__size---md);
223
224
  --origam-btn---font-weight: var(--origam-font__weight---medium);
224
225
  --origam-btn---letter-spacing: var(--origam-font__letterSpacing---widest);
@@ -145,6 +145,7 @@ const props = defineProps({
145
145
  block: { type: Boolean, required: false },
146
146
  slim: { type: Boolean, required: false },
147
147
  stacked: { type: Boolean, required: false },
148
+ wrap: { type: Boolean, required: false },
148
149
  text: { type: String, required: false },
149
150
  status: { type: null, required: false },
150
151
  statusIconPosition: { type: null, required: false },
@@ -396,7 +397,8 @@ const btnClasses = computed(() => {
396
397
  // which stays in the DOM (width and name preserved).
397
398
  [`origam-btn--loader-${loaderKind.value}`]: isLoading.value,
398
399
  "origam-btn--slim": props.slim,
399
- "origam-btn--stacked": props.stacked
400
+ "origam-btn--stacked": props.stacked,
401
+ "origam-btn--wrap": props.wrap
400
402
  },
401
403
  variantClasses.value,
402
404
  borderClasses.value,
@@ -459,6 +461,10 @@ defineExpose({
459
461
  // blockifies to `block`, i.e. byte-for-byte today's computed value, so
460
462
  // no existing layout moves. `&--block` still overrides it with `flex`.
461
463
  display: inline-block;
464
+ // #1094 — inoperant tant que la racine est `inline-block` ; prend effet
465
+ // sur `&--block` (`display: flex`), ou `height: auto` + `min-height`
466
+ // laisseraient sinon le contenu colle en haut.
467
+ align-items: center;
462
468
 
463
469
  position: var(--origam-btn---position, relative);
464
470
  vertical-align: middle;
@@ -475,12 +481,22 @@ defineExpose({
475
481
  transition-duration: var(--origam-btn---transition-duration, 0.28s);
476
482
  transition-timing-function: var(--origam-btn---transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
477
483
 
478
- padding: 0 calc(16px + var(--origam-btn---density-padding-x, 0px));
484
+ // ⛔ #929 — le padding vertical est LU depuis `--origam-btn---padding-block`
485
+ // (repli `0` : un bouton qui ne le pose pas ne bouge pas). Il n'etait
486
+ // qu'ecrit, jamais lu. Les rubriques `&--size-*` / `&--slim` ne posent
487
+ // plus que `padding-inline`, sans quoi leur shorthand `padding: 0 …`
488
+ // remettrait ce bloc a zero.
489
+ padding-block: var(--origam-btn---padding-block, 0);
490
+ padding-inline: calc(16px + var(--origam-btn---density-padding-x, 0px));
479
491
 
480
492
  width: var(--origam-btn---width, auto);
481
493
  min-width: calc(var(--origam-btn---min-width, 64px) + var(--origam-btn---density, 0px));
482
494
  max-width: var(--origam-btn---max-width, 100%);
483
- height: calc(var(--origam-btn---height, 36px) + var(--origam-btn---density, 0px));
495
+ // ⛔ #1094 — la hauteur est un MINIMUM, plus une valeur fixe : `height: auto`
496
+ // laisse un contenu a deux lignes (`wrap`) agrandir le bouton, et le
497
+ // `min-height` ci-dessous garde la taille du token pour un contenu d'une
498
+ // ligne. La prop `height` reste prioritaire (declaration inline).
499
+ height: auto;
484
500
  // ⛔ C2 (#597) — `--origam-btn---min-height` reste NON DECLARE, a dessein.
485
501
  // Son repli est `calc(height + density)`, deux vars reglees PAR INSTANCE
486
502
  // (`&--size-*` / `&--density-*` plus bas dans ce bloc). Une custom property
@@ -488,6 +504,13 @@ defineExpose({
488
504
  // figerait la hauteur minimale a la taille/densite racine et les props
489
505
  // `size` / `density` cesseraient d'agir sur elle. Faux positif de la garde,
490
506
  // qui compare des noms et non des chaines de repli.
507
+ //
508
+ // ⛔ #1094 — ce `min-height` est desormais le SEUL garant de la hauteur au repos
509
+ // (la hauteur est `auto`). `--origam-btn---min-height` peut le RELEVER (le site
510
+ // marketing le fait : 34 / 38 / 44 px) ; le poser a `0` SUPPRIME le plancher et le
511
+ // bouton retombe a la hauteur de son contenu. Avec une hauteur fixe, ce `0` etait
512
+ // sans effet : c'est pourquoi Chip / Code / graphes l'ecrivaient en pure perte, et
513
+ // pourquoi ces ecritures ont ete retirees dans le meme lot.
491
514
  min-height: var(--origam-btn---min-height, calc(var(--origam-btn---height, 36px) + var(--origam-btn---density, 0px)));
492
515
  max-height: var(--origam-btn---max-height, 100%);
493
516
 
@@ -559,7 +582,7 @@ defineExpose({
559
582
  --origam-btn---height: var(--origam-btn---height-xs, 20px);
560
583
  --origam-btn---font-size: 0.625rem;
561
584
  --origam-btn---min-width: 36px;
562
- padding: 0 calc(var(--origam-btn---padding-xs, 8px) + var(--origam-btn---density-padding-x, 0px));
585
+ padding-inline: calc(var(--origam-btn---padding-xs, 8px) + var(--origam-btn---density-padding-x, 0px));
563
586
 
564
587
  :deep(.origam-icon) {
565
588
  --origam-btn---font-size: 16px;
@@ -570,7 +593,7 @@ defineExpose({
570
593
  --origam-btn---height: var(--origam-btn---height-sm, 28px);
571
594
  --origam-btn---font-size: 0.75rem;
572
595
  --origam-btn---min-width: 50px;
573
- padding: 0 calc(var(--origam-btn---padding-sm, 12px) + var(--origam-btn---density-padding-x, 0px));
596
+ padding-inline: calc(var(--origam-btn---padding-sm, 12px) + var(--origam-btn---density-padding-x, 0px));
574
597
 
575
598
  :deep(.origam-icon) {
576
599
  --origam-btn---font-size: 20px;
@@ -578,7 +601,7 @@ defineExpose({
578
601
  }
579
602
 
580
603
  &--size-default {
581
- padding: 0 calc(var(--origam-btn---padding-md, 16px) + var(--origam-btn---density-padding-x, 0px));
604
+ padding-inline: calc(var(--origam-btn---padding-md, 16px) + var(--origam-btn---density-padding-x, 0px));
582
605
 
583
606
  :deep(.origam-icon) {
584
607
  --origam-btn---font-size: 24px;
@@ -589,7 +612,7 @@ defineExpose({
589
612
  --origam-btn---height: var(--origam-btn---height-lg, 44px);
590
613
  --origam-btn---font-size: 1rem;
591
614
  --origam-btn---min-width: 78px;
592
- padding: 0 calc(var(--origam-btn---padding-lg, 20px) + var(--origam-btn---density-padding-x, 0px));
615
+ padding-inline: calc(var(--origam-btn---padding-lg, 20px) + var(--origam-btn---density-padding-x, 0px));
593
616
 
594
617
  :deep(.origam-icon) {
595
618
  --origam-btn---font-size: 28px;
@@ -600,7 +623,7 @@ defineExpose({
600
623
  --origam-btn---height: var(--origam-btn---height-xl, 52px);
601
624
  --origam-btn---font-size: 1.125rem;
602
625
  --origam-btn---min-width: 92px;
603
- padding: 0 calc(var(--origam-btn---padding-xl, 24px) + var(--origam-btn---density-padding-x, 0px));
626
+ padding-inline: calc(var(--origam-btn---padding-xl, 24px) + var(--origam-btn---density-padding-x, 0px));
604
627
 
605
628
  :deep(.origam-icon) {
606
629
  --origam-btn---font-size: 32px;
@@ -728,6 +751,11 @@ defineExpose({
728
751
  --origam-btn---width: calc(var(--origam-btn---height, 36px) + var(--origam-btn---density, 0px));
729
752
 
730
753
  padding: 0;
754
+ // #1094 — un bouton icone est CARRE : sa largeur derive du meme token que sa
755
+ // hauteur (`--origam-btn---width` ci-dessus). Aucun libelle ne peut deborder,
756
+ // donc la hauteur reste fixe — sans quoi un contenu plus haut que le carre
757
+ // (icone de Chip, `min-height: 0` des consommateurs) le deformerait.
758
+ height: calc(var(--origam-btn---height, 36px) + var(--origam-btn---density, 0px));
731
759
 
732
760
  #{$this}__loader {
733
761
  display: flex;
@@ -1016,35 +1044,35 @@ defineExpose({
1016
1044
  --origam-btn---height: 56px;
1017
1045
  font-size: 0.625rem;
1018
1046
  min-width: 56px;
1019
- padding: 0 calc(12px + var(--origam-btn---density-padding-x, 0px));
1047
+ padding-inline: calc(12px + var(--origam-btn---density-padding-x, 0px));
1020
1048
  }
1021
1049
 
1022
1050
  &#{$this}--size-small {
1023
1051
  --origam-btn---height: 64px;
1024
1052
  font-size: 0.75rem;
1025
1053
  min-width: 64px;
1026
- padding: 0 calc(14px + var(--origam-btn---density-padding-x, 0px));
1054
+ padding-inline: calc(14px + var(--origam-btn---density-padding-x, 0px));
1027
1055
  }
1028
1056
 
1029
1057
  &#{$this}--size-default {
1030
1058
  --origam-btn---height: 72px;
1031
1059
  font-size: 0.875rem;
1032
1060
  min-width: 72px;
1033
- padding: 0 calc(16px + var(--origam-btn---density-padding-x, 0px));
1061
+ padding-inline: calc(16px + var(--origam-btn---density-padding-x, 0px));
1034
1062
  }
1035
1063
 
1036
1064
  &#{$this}--size-large {
1037
1065
  --origam-btn---height: 80px;
1038
1066
  font-size: 1rem;
1039
1067
  min-width: 80px;
1040
- padding: 0 calc(18px + var(--origam-btn---density-padding-x, 0px));
1068
+ padding-inline: calc(18px + var(--origam-btn---density-padding-x, 0px));
1041
1069
  }
1042
1070
 
1043
1071
  &#{$this}--size-x-large {
1044
1072
  --origam-btn---height: 88px;
1045
1073
  font-size: 1.125rem;
1046
1074
  min-width: 88px;
1047
- padding: 0 calc(20px + var(--origam-btn---density-padding-x, 0px));
1075
+ padding-inline: calc(20px + var(--origam-btn---density-padding-x, 0px));
1048
1076
  }
1049
1077
 
1050
1078
  &#{$this}--density-comfortable {
@@ -1057,7 +1085,18 @@ defineExpose({
1057
1085
  }
1058
1086
 
1059
1087
  &--slim {
1060
- padding: 0 8px;
1088
+ padding-inline: 8px;
1089
+ }
1090
+
1091
+ // #1093 — le libelle reste sur UNE ligne (`__content { white-space: nowrap }`)
1092
+ // sauf demande : `wrap` le laisse passer a la ligne. Le bouton grandit alors
1093
+ // (sa hauteur est un minimum, #1094) au lieu de laisser le texte deborder.
1094
+ // `text-align: center` : un `<a>` / `<span>` ne centre pas ses lignes seul.
1095
+ &--wrap {
1096
+ #{$this}__content {
1097
+ white-space: normal;
1098
+ text-align: center;
1099
+ }
1061
1100
  }
1062
1101
 
1063
1102
  &--rounded {
@@ -48,7 +48,7 @@
48
48
  variant="outlined"
49
49
  :text="drilldownBackLabel"
50
50
  :aria-label="drilldownBackLabel"
51
- :style="{ '--origam-btn---min-height': '0', '--origam-btn---min-width': '0' }"
51
+ :style="{ '--origam-btn---min-width': '0' }"
52
52
  data-cy="origam-chart-cartesian-breadcrumb-back"
53
53
  @click="onDrillUp"
54
54
  />
@@ -39,7 +39,7 @@
39
39
  variant="outlined"
40
40
  :text="drilldownBackLabel"
41
41
  :aria-label="drilldownBackLabel"
42
- :style="{ '--origam-btn---min-height': '0', '--origam-btn---min-width': '0' }"
42
+ :style="{ '--origam-btn---min-width': '0' }"
43
43
  data-cy="origam-chart-polar-breadcrumb-back"
44
44
  @click="onDrillUp"
45
45
  />
@@ -109,7 +109,7 @@
109
109
  :icon="true"
110
110
  size="x-small"
111
111
  density="compact"
112
- :style="{'--origam-btn---min-width': '0', '--origam-btn---min-height': '0'}"
112
+ :style="{'--origam-btn---min-width': '0'}"
113
113
  @click.stop="handleClickClose"
114
114
  >
115
115
  <slot
@@ -421,7 +421,6 @@ defineExpose({ rebuild, copied, codeRef });
421
421
 
422
422
  .origam-code__copy--compact {
423
423
  flex: none;
424
- --origam-btn---min-height: 0;
425
424
  --origam-btn---min-width: 0px;
426
425
  --origam-btn---height: auto;
427
426
  --origam-btn---padding-block: var(--origam-code__compact-copy---padding-block);
@@ -464,7 +463,6 @@ defineExpose({ rebuild, copied, codeRef });
464
463
  }
465
464
 
466
465
  .origam-code__copy {
467
- --origam-btn---min-height: 0;
468
466
  --origam-btn---min-width: 0px;
469
467
  --origam-btn---border-radius: var(--origam-code__copy---border-radius);
470
468
  --origam-btn---font-size: var(--origam-code__copy---font-size);
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useStyle = useStyle;
7
7
  var _vue = require("vue");
8
+ var _ssrStyle = require("../../consts/Commons/ssr-style.const.cjs");
8
9
  var _dom = require("../../utils/Commons/dom.util.cjs");
9
10
  var _getCurrentInstance = require("../../utils/Commons/getCurrentInstance.util.cjs");
10
11
  var _styleTag = require("./styleTag.composable.cjs");
@@ -21,6 +22,14 @@ function toDeclarations(value) {
21
22
  }
22
23
  return [];
23
24
  }
25
+ const styleSlots = /* @__PURE__ */new WeakMap();
26
+ function nextStyleSlot() {
27
+ const vm = (0, _vue.getCurrentInstance)();
28
+ if (!vm) return 0;
29
+ const slot = styleSlots.get(vm) ?? 0;
30
+ styleSlots.set(vm, slot + 1);
31
+ return slot;
32
+ }
24
33
  function useStyle(styles, uniq = void 0, name = (0, _getCurrentInstance.getCurrentInstanceName)()) {
25
34
  const fallbackId = `${name}-${(0, _getCurrentInstance.getUid)()}`;
26
35
  const id = (0, _vue.computed)(() => {
@@ -31,13 +40,17 @@ function useStyle(styles, uniq = void 0, name = (0, _getCurrentInstance.getCurre
31
40
  const stylesArray = toDeclarations(styles.value);
32
41
  return `#${(0, _dom.escapeCssIdent)(id.value)} {${stylesArray.join(";")}}`;
33
42
  });
43
+ const tagId = `${_ssrStyle.SSR_STYLE_TAG_PREFIX}${fallbackId}-${nextStyleSlot()}`;
44
+ (0, _vue.inject)(_ssrStyle.ORIGAM_SSR_STYLES_KEY, null)?.add(tagId, () => customCss.value);
34
45
  const {
35
46
  id: styleTagId,
36
47
  css,
37
48
  load,
38
49
  unload,
39
50
  isLoaded
40
- } = (0, _styleTag.useStyleTag)(customCss);
51
+ } = (0, _styleTag.useStyleTag)(customCss, {
52
+ id: tagId
53
+ });
41
54
  (0, _vue.onMounted)(() => {
42
55
  load();
43
56
  });
@@ -1,18 +1,4 @@
1
1
  import type { ComputedRef, MaybeRefOrGetter } from 'vue';
2
- /**
3
- * Build a per-instance stylesheet out of a computed style bag and inject it
4
- * in `<head>`, scoped to the root element's id.
5
- *
6
- * `uniq` is the id the CALLER wants that root element to carry. Passing
7
- * `() => props.id` is what makes a consumer-supplied `id` win over the
8
- * generated one: the returned `id` is what the component binds to its root,
9
- * and the generated rule targets that same id — so honouring the consumer's
10
- * `id` never orphans the rule. Reactive (ref / getter) so that an `id` that
11
- * appears on a later render is picked up.
12
- *
13
- * When the caller passes nothing (or an empty value) the generated
14
- * `<name>-<uid>` id is used, exactly as before.
15
- */
16
2
  /*********************************************************
17
3
  * useStyle
18
4
  *
@@ -1,4 +1,5 @@
1
- import { computed, onMounted, toValue } from "vue";
1
+ import { computed, getCurrentInstance, inject, onMounted, toValue } from "vue";
2
+ import { ORIGAM_SSR_STYLES_KEY, SSR_STYLE_TAG_PREFIX } from "../../consts/Commons/ssr-style.const.js";
2
3
  import { escapeCssIdent } from "../../utils/Commons/dom.util.js";
3
4
  import { getCurrentInstanceName, getUid } from "../../utils/Commons/getCurrentInstance.util.js";
4
5
  import { useStyleTag } from "./styleTag.composable.js";
@@ -15,6 +16,14 @@ function toDeclarations(value) {
15
16
  }
16
17
  return [];
17
18
  }
19
+ const styleSlots = /* @__PURE__ */ new WeakMap();
20
+ function nextStyleSlot() {
21
+ const vm = getCurrentInstance();
22
+ if (!vm) return 0;
23
+ const slot = styleSlots.get(vm) ?? 0;
24
+ styleSlots.set(vm, slot + 1);
25
+ return slot;
26
+ }
18
27
  export function useStyle(styles, uniq = void 0, name = getCurrentInstanceName()) {
19
28
  const fallbackId = `${name}-${getUid()}`;
20
29
  const id = computed(() => {
@@ -25,7 +34,9 @@ export function useStyle(styles, uniq = void 0, name = getCurrentInstanceName())
25
34
  const stylesArray = toDeclarations(styles.value);
26
35
  return `#${escapeCssIdent(id.value)} {${stylesArray.join(";")}}`;
27
36
  });
28
- const { id: styleTagId, css, load, unload, isLoaded } = useStyleTag(customCss);
37
+ const tagId = `${SSR_STYLE_TAG_PREFIX}${fallbackId}-${nextStyleSlot()}`;
38
+ inject(ORIGAM_SSR_STYLES_KEY, null)?.add(tagId, () => customCss.value);
39
+ const { id: styleTagId, css, load, unload, isLoaded } = useStyleTag(customCss, { id: tagId });
29
40
  onMounted(() => {
30
41
  load();
31
42
  });
@@ -18,15 +18,20 @@ function useStyleTag(css, options = {}) {
18
18
  } = options;
19
19
  const cssRef = (0, _vue.shallowRef)(css);
20
20
  let stop = () => {};
21
+ let tag;
21
22
  const load = () => {
22
23
  if (!document) return;
23
- const el = document.getElementById(id) || document.createElement("style");
24
+ if (isLoaded.value) return;
25
+ const existing = document.getElementById(id);
26
+ const adoptable = existing && existing.dataset.origamStyleOwned === void 0 ? existing : void 0;
27
+ const el = adoptable ?? document.createElement("style");
24
28
  if (!el.isConnected) {
25
- el.id = id;
29
+ el.id = existing ? `${id}-${++_id}` : id;
26
30
  if (options.media) el.media = options.media;
27
31
  document.head.appendChild(el);
28
32
  }
29
- if (isLoaded.value) return;
33
+ el.dataset.origamStyleOwned = "";
34
+ tag = el;
30
35
  stop = (0, _vue.watch)(cssRef, value => {
31
36
  el.textContent = value;
32
37
  }, {
@@ -37,7 +42,8 @@ function useStyleTag(css, options = {}) {
37
42
  const unload = () => {
38
43
  if (!document || !isLoaded.value) return;
39
44
  stop();
40
- document.head.removeChild(document.getElementById(id));
45
+ tag?.remove();
46
+ tag = void 0;
41
47
  isLoaded.value = false;
42
48
  };
43
49
  if (immediate && !manual) (0, _commons2.tryOnMounted)(load);
@@ -13,18 +13,23 @@ export function useStyleTag(css, options = {}) {
13
13
  const cssRef = shallowRef(css);
14
14
  let stop = () => {
15
15
  };
16
+ let tag;
16
17
  const load = () => {
17
18
  if (!document)
18
19
  return;
19
- const el = document.getElementById(id) || document.createElement("style");
20
+ if (isLoaded.value)
21
+ return;
22
+ const existing = document.getElementById(id);
23
+ const adoptable = existing && existing.dataset.origamStyleOwned === void 0 ? existing : void 0;
24
+ const el = adoptable ?? document.createElement("style");
20
25
  if (!el.isConnected) {
21
- el.id = id;
26
+ el.id = existing ? `${id}-${++_id}` : id;
22
27
  if (options.media)
23
28
  el.media = options.media;
24
29
  document.head.appendChild(el);
25
30
  }
26
- if (isLoaded.value)
27
- return;
31
+ el.dataset.origamStyleOwned = "";
32
+ tag = el;
28
33
  stop = watch(
29
34
  cssRef,
30
35
  (value) => {
@@ -38,7 +43,8 @@ export function useStyleTag(css, options = {}) {
38
43
  if (!document || !isLoaded.value)
39
44
  return;
40
45
  stop();
41
- document.head.removeChild(document.getElementById(id));
46
+ tag?.remove();
47
+ tag = void 0;
42
48
  isLoaded.value = false;
43
49
  };
44
50
  if (immediate && !manual)
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SSR_STYLE_TAG_PREFIX = exports.ORIGAM_SSR_STYLES_KEY = void 0;
7
+ const ORIGAM_SSR_STYLES_KEY = exports.ORIGAM_SSR_STYLES_KEY = /* @__PURE__ */Symbol.for("origam:ssr-styles");
8
+ const SSR_STYLE_TAG_PREFIX = exports.SSR_STYLE_TAG_PREFIX = "origam-style-";
@@ -0,0 +1,19 @@
1
+ import type { InjectionKey } from 'vue';
2
+ import type { ISsrStyleCollector } from '../../interfaces/Commons/style.interface';
3
+ /*********************************************************
4
+ * ORIGAM_SSR_STYLES_KEY / SSR_STYLE_TAG_PREFIX (#1092)
5
+ *
6
+ * @description
7
+ * Provide/inject key for the per-app collector the server render fills with
8
+ * every `useStyle()` rule. Global symbol, like the other origam keys, so two
9
+ * bundles of the library interoperate. `createOrigam()` provides it ONLY when
10
+ * there is no `document`: on the client `inject()` returns `null` and
11
+ * `useStyle` takes the DOM path.
12
+ *
13
+ * @description
14
+ * Prefix of the `id` of the `<style>` tag a `useStyle()` instance owns,
15
+ * followed by the instance's `useId()` value, so the tag the server renders
16
+ * and the tag the client looks up at mount carry the SAME id.
17
+ ********************************************************/
18
+ export declare const ORIGAM_SSR_STYLES_KEY: InjectionKey<ISsrStyleCollector>;
19
+ export declare const SSR_STYLE_TAG_PREFIX = "origam-style-";
@@ -0,0 +1,2 @@
1
+ export const ORIGAM_SSR_STYLES_KEY = /* @__PURE__ */ Symbol.for("origam:ssr-styles");
2
+ export const SSR_STYLE_TAG_PREFIX = "origam-style-";
@@ -69,6 +69,17 @@ Object.keys(_defaults).forEach(function (key) {
69
69
  }
70
70
  });
71
71
  });
72
+ var _ssrStyle = require("./Commons/ssr-style.const.cjs");
73
+ Object.keys(_ssrStyle).forEach(function (key) {
74
+ if (key === "default" || key === "__esModule") return;
75
+ if (key in exports && exports[key] === _ssrStyle[key]) return;
76
+ Object.defineProperty(exports, key, {
77
+ enumerable: true,
78
+ get: function () {
79
+ return _ssrStyle[key];
80
+ }
81
+ });
82
+ });
72
83
  var _density = require("./Commons/density.const.cjs");
73
84
  Object.keys(_density).forEach(function (key) {
74
85
  if (key === "default" || key === "__esModule") return;
@@ -4,6 +4,7 @@ export * from './Commons/color.const';
4
4
  export * from './Commons/commons.const';
5
5
  export * from './Commons/date.const';
6
6
  export * from './Commons/defaults.const';
7
+ export * from './Commons/ssr-style.const';
7
8
  export * from './Commons/density.const';
8
9
  export * from './Commons/dimension.const';
9
10
  export * from './Commons/layout.const';
@@ -4,6 +4,7 @@ export * from "./Commons/color.const.js";
4
4
  export * from "./Commons/commons.const.js";
5
5
  export * from "./Commons/date.const.js";
6
6
  export * from "./Commons/defaults.const.js";
7
+ export * from "./Commons/ssr-style.const.js";
7
8
  export * from "./Commons/density.const.js";
8
9
  export * from "./Commons/dimension.const.js";
9
10
  export * from "./Commons/layout.const.js";
@@ -142,12 +142,67 @@ function enforceContrast(el, enabled) {
142
142
  const better = wcagRatio(WHITE, bg) >= wcagRatio(BLACK, bg) ? "#fff" : "#000";
143
143
  el.style.setProperty("color", better, "important");
144
144
  el.dataset.origamContrastFixed = "true";
145
- console.warn(`[origam] Low text contrast (${ratio.toFixed(2)}:1 < ${config.threshold}:1): text rgb(${fg.join(", ")}) on background rgb(${bg.join(", ")}). Overriding text colour to ${better} for legibility. Disable with createOrigam({ contrast: false }).`, el);
145
+ if (process.env?.DEV) {
146
+ console.warn(`[origam] Low text contrast (${ratio.toFixed(2)}:1 < ${config.threshold}:1): text rgb(${fg.join(", ")}) on background rgb(${bg.join(", ")}). Overriding text colour to ${better} for legibility. Disable with createOrigam({ contrast: false }).`, el);
147
+ }
148
+ }
149
+ const MAX_SETTLE_PASSES = 4;
150
+ const COLOR_TRANSITION_PROPERTIES = /* @__PURE__ */new Set(["color", "background-color"]);
151
+ function declaredTransitionMs(el) {
152
+ const toMs = value => {
153
+ const n = parseFloat(value);
154
+ if (Number.isNaN(n)) return 0;
155
+ return value.trim().endsWith("ms") ? n : n * 1e3;
156
+ };
157
+ let longest = 0;
158
+ for (let node = el; node; node = node.parentElement) {
159
+ const style = getComputedStyle(node);
160
+ const durations = style.transitionDuration.split(",");
161
+ const delays = style.transitionDelay.split(",");
162
+ durations.forEach((duration, i) => {
163
+ longest = Math.max(longest, toMs(duration) + toMs(delays[i % delays.length] ?? "0"));
164
+ });
165
+ }
166
+ return longest;
167
+ }
168
+ function runningColorTransitions(el) {
169
+ const found = [];
170
+ for (let node = el; node; node = node.parentElement) {
171
+ for (const animation of node.getAnimations()) {
172
+ const property = animation.transitionProperty;
173
+ if (property && COLOR_TRANSITION_PROPERTIES.has(property) && animation.playState !== "finished") {
174
+ found.push(animation);
175
+ }
176
+ }
177
+ }
178
+ return found;
179
+ }
180
+ function forceStyleRecalc(el) {
181
+ return getComputedStyle(el).color;
182
+ }
183
+ function settleThenEnforce(el, enabled, pending, pass = 0) {
184
+ if (pending.cancelled) return;
185
+ if (!forceStyleRecalc(el)) return;
186
+ if (typeof el.getAnimations === "function") {
187
+ const running = pass < MAX_SETTLE_PASSES ? runningColorTransitions(el) : [];
188
+ if (running.length) {
189
+ Promise.allSettled(running.map(animation => animation.finished)).then(() => settleThenEnforce(el, enabled, pending, pass + 1));
190
+ return;
191
+ }
192
+ enforceContrast(el, enabled);
193
+ return;
194
+ }
195
+ const wait = declaredTransitionMs(el);
196
+ if (wait > 0 && pass === 0 && typeof setTimeout !== "undefined") {
197
+ pending.timeout = setTimeout(() => settleThenEnforce(el, enabled, pending, 1), wait);
198
+ return;
199
+ }
200
+ enforceContrast(el, enabled);
146
201
  }
147
- const SETTLE_MS = 250;
148
202
  function cancelScheduled(el) {
149
203
  const pending = el._contrastTimers;
150
204
  if (!pending) return;
205
+ pending.cancelled = true;
151
206
  if (pending.raf !== void 0 && typeof cancelAnimationFrame !== "undefined") {
152
207
  cancelAnimationFrame(pending.raf);
153
208
  }
@@ -160,10 +215,7 @@ function schedule(el, enabled) {
160
215
  cancelScheduled(el);
161
216
  const pending = {};
162
217
  if (typeof requestAnimationFrame !== "undefined") {
163
- pending.raf = requestAnimationFrame(() => enforceContrast(el, enabled));
164
- }
165
- if (typeof setTimeout !== "undefined") {
166
- pending.timeout = setTimeout(() => enforceContrast(el, enabled), SETTLE_MS);
218
+ pending.raf = requestAnimationFrame(() => settleThenEnforce(el, enabled, pending));
167
219
  }
168
220
  el._contrastTimers = pending;
169
221
  }
@@ -1,5 +1,15 @@
1
1
  import type { Directive } from 'vue';
2
2
  import type { IContrastOptions } from '../../interfaces/Commons/commons.interface';
3
3
  export declare function setContrastConfig(value: boolean | IContrastOptions | undefined): void;
4
+ /**
5
+ * `v-contrast` — guards an element's text legibility at runtime.
6
+ *
7
+ * After the browser has resolved the design-token CSS variables, it measures
8
+ * the element's text colour against its painted background. When the WCAG
9
+ * ratio falls below the configured threshold it forces the better of
10
+ * black / white text (in every build) and, in dev only, logs a one-line warning explaining the swap. Toggle
11
+ * globally via `createOrigam({ contrast: false })`, or per-element with
12
+ * `v-contrast="false"`.
13
+ */
4
14
  declare const vContrast: Directive<HTMLElement, boolean | undefined>;
5
15
  export default vContrast;