origam 2.26.2 → 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 (44) hide show
  1. package/CHANGELOG.md +68 -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/components/Skeleton/OrigamSkeleton.vue +1 -122
  13. package/dist/src/composables/Commons/style.composable.cjs +14 -1
  14. package/dist/src/composables/Commons/style.composable.d.ts +0 -14
  15. package/dist/src/composables/Commons/style.composable.js +13 -2
  16. package/dist/src/composables/Commons/styleTag.composable.cjs +10 -4
  17. package/dist/src/composables/Commons/styleTag.composable.js +11 -5
  18. package/dist/src/consts/Commons/ssr-style.const.cjs +8 -0
  19. package/dist/src/consts/Commons/ssr-style.const.d.ts +19 -0
  20. package/dist/src/consts/Commons/ssr-style.const.js +2 -0
  21. package/dist/src/consts/index.cjs +11 -0
  22. package/dist/src/consts/index.d.ts +1 -0
  23. package/dist/src/consts/index.js +1 -0
  24. package/dist/src/directives/Contrast/contrast.directive.cjs +58 -6
  25. package/dist/src/directives/Contrast/contrast.directive.d.ts +10 -0
  26. package/dist/src/directives/Contrast/contrast.directive.js +61 -9
  27. package/dist/src/enums/Skeleton/skeleton.enum.cjs +0 -2
  28. package/dist/src/enums/Skeleton/skeleton.enum.d.ts +2 -5
  29. package/dist/src/enums/Skeleton/skeleton.enum.js +0 -2
  30. package/dist/src/interfaces/Btn/btn.interface.d.ts +1 -0
  31. package/dist/src/interfaces/Commons/commons.interface.d.ts +14 -0
  32. package/dist/src/interfaces/Commons/style.interface.d.ts +21 -0
  33. package/dist/src/nuxt/plugin.server.cjs +18 -0
  34. package/dist/src/nuxt/plugin.server.js +9 -0
  35. package/dist/src/origam.cjs +3 -0
  36. package/dist/src/origam.js +5 -1
  37. package/dist/src/types/tokens.type.d.ts +1 -1
  38. package/dist/src/utils/Commons/ssr-styles.util.cjs +41 -0
  39. package/dist/src/utils/Commons/ssr-styles.util.d.ts +6 -0
  40. package/dist/src/utils/Commons/ssr-styles.util.js +26 -0
  41. package/dist/src/utils/index.cjs +11 -0
  42. package/dist/src/utils/index.d.ts +1 -0
  43. package/dist/src/utils/index.js +1 -0
  44. package/package.json +1 -1
@@ -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;
@@ -120,15 +120,70 @@ function enforceContrast(el, enabled) {
120
120
  const better = wcagRatio(WHITE, bg) >= wcagRatio(BLACK, bg) ? "#fff" : "#000";
121
121
  el.style.setProperty("color", better, "important");
122
122
  el.dataset.origamContrastFixed = "true";
123
- console.warn(
124
- `[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 }).`,
125
- el
126
- );
123
+ if (import.meta.env?.DEV) {
124
+ console.warn(
125
+ `[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 }).`,
126
+ el
127
+ );
128
+ }
129
+ }
130
+ const MAX_SETTLE_PASSES = 4;
131
+ const COLOR_TRANSITION_PROPERTIES = /* @__PURE__ */ new Set(["color", "background-color"]);
132
+ function declaredTransitionMs(el) {
133
+ const toMs = (value) => {
134
+ const n = parseFloat(value);
135
+ if (Number.isNaN(n)) return 0;
136
+ return value.trim().endsWith("ms") ? n : n * 1e3;
137
+ };
138
+ let longest = 0;
139
+ for (let node = el; node; node = node.parentElement) {
140
+ const style = getComputedStyle(node);
141
+ const durations = style.transitionDuration.split(",");
142
+ const delays = style.transitionDelay.split(",");
143
+ durations.forEach((duration, i) => {
144
+ longest = Math.max(longest, toMs(duration) + toMs(delays[i % delays.length] ?? "0"));
145
+ });
146
+ }
147
+ return longest;
148
+ }
149
+ function runningColorTransitions(el) {
150
+ const found = [];
151
+ for (let node = el; node; node = node.parentElement) {
152
+ for (const animation of node.getAnimations()) {
153
+ const property = animation.transitionProperty;
154
+ if (property && COLOR_TRANSITION_PROPERTIES.has(property) && animation.playState !== "finished") {
155
+ found.push(animation);
156
+ }
157
+ }
158
+ }
159
+ return found;
160
+ }
161
+ function forceStyleRecalc(el) {
162
+ return getComputedStyle(el).color;
163
+ }
164
+ function settleThenEnforce(el, enabled, pending, pass = 0) {
165
+ if (pending.cancelled) return;
166
+ if (!forceStyleRecalc(el)) return;
167
+ if (typeof el.getAnimations === "function") {
168
+ const running = pass < MAX_SETTLE_PASSES ? runningColorTransitions(el) : [];
169
+ if (running.length) {
170
+ Promise.allSettled(running.map((animation) => animation.finished)).then(() => settleThenEnforce(el, enabled, pending, pass + 1));
171
+ return;
172
+ }
173
+ enforceContrast(el, enabled);
174
+ return;
175
+ }
176
+ const wait = declaredTransitionMs(el);
177
+ if (wait > 0 && pass === 0 && typeof setTimeout !== "undefined") {
178
+ pending.timeout = setTimeout(() => settleThenEnforce(el, enabled, pending, 1), wait);
179
+ return;
180
+ }
181
+ enforceContrast(el, enabled);
127
182
  }
128
- const SETTLE_MS = 250;
129
183
  function cancelScheduled(el) {
130
184
  const pending = el._contrastTimers;
131
185
  if (!pending) return;
186
+ pending.cancelled = true;
132
187
  if (pending.raf !== void 0 && typeof cancelAnimationFrame !== "undefined") {
133
188
  cancelAnimationFrame(pending.raf);
134
189
  }
@@ -141,10 +196,7 @@ function schedule(el, enabled) {
141
196
  cancelScheduled(el);
142
197
  const pending = {};
143
198
  if (typeof requestAnimationFrame !== "undefined") {
144
- pending.raf = requestAnimationFrame(() => enforceContrast(el, enabled));
145
- }
146
- if (typeof setTimeout !== "undefined") {
147
- pending.timeout = setTimeout(() => enforceContrast(el, enabled), SETTLE_MS);
199
+ pending.raf = requestAnimationFrame(() => settleThenEnforce(el, enabled, pending));
148
200
  }
149
201
  el._contrastTimers = pending;
150
202
  }
@@ -9,8 +9,6 @@ var SKELETON_VARIANT = exports.SKELETON_VARIANT = /* @__PURE__ */(SKELETON_VARIA
9
9
  SKELETON_VARIANT2["RECTANGULAR"] = "rectangular";
10
10
  SKELETON_VARIANT2["CIRCULAR"] = "circular";
11
11
  SKELETON_VARIANT2["PILL"] = "pill";
12
- SKELETON_VARIANT2["CARD"] = "card";
13
- SKELETON_VARIANT2["LIST_ITEM"] = "list-item";
14
12
  return SKELETON_VARIANT2;
15
13
  })(SKELETON_VARIANT || {});
16
14
  var SKELETON_ANIMATION = exports.SKELETON_ANIMATION = /* @__PURE__ */(SKELETON_ANIMATION2 => {
@@ -4,16 +4,13 @@
4
4
  * @description
5
5
  * Shape presets for `<OrigamSkeleton>`. `text`, `rectangular`, `circular`
6
6
  * and `pill` are the PRIMITIVES — the bricks a component assembles to mimic
7
- * its own anatomy. `card` and `list-item` are fixed composite templates kept
8
- * until the components that consume them have moved to primitives.
7
+ * its own anatomy, inside an `<OrigamSkeletonGroup>`.
9
8
  ********************************************************/
10
9
  export declare enum SKELETON_VARIANT {
11
10
  TEXT = "text",
12
11
  RECTANGULAR = "rectangular",
13
12
  CIRCULAR = "circular",
14
- PILL = "pill",
15
- CARD = "card",
16
- LIST_ITEM = "list-item"
13
+ PILL = "pill"
17
14
  }
18
15
  /*********************************************************
19
16
  * SKELETON_ANIMATION
@@ -3,8 +3,6 @@ export var SKELETON_VARIANT = /* @__PURE__ */ ((SKELETON_VARIANT2) => {
3
3
  SKELETON_VARIANT2["RECTANGULAR"] = "rectangular";
4
4
  SKELETON_VARIANT2["CIRCULAR"] = "circular";
5
5
  SKELETON_VARIANT2["PILL"] = "pill";
6
- SKELETON_VARIANT2["CARD"] = "card";
7
- SKELETON_VARIANT2["LIST_ITEM"] = "list-item";
8
6
  return SKELETON_VARIANT2;
9
7
  })(SKELETON_VARIANT || {});
10
8
  export var SKELETON_ANIMATION = /* @__PURE__ */ ((SKELETON_ANIMATION2) => {
@@ -33,6 +33,7 @@ export interface IBtnProps extends ICommonsComponentProps, IColorProps, IBgColor
33
33
  block?: boolean;
34
34
  slim?: boolean;
35
35
  stacked?: boolean;
36
+ wrap?: boolean;
36
37
  text?: string;
37
38
  status?: TStatus;
38
39
  statusIconPosition?: TStatusPosition;
@@ -15,6 +15,20 @@ import { TSSROptions } from '../../types/Commons/display.type';
15
15
  * `enabled` toggles the whole feature; `threshold` is the minimum acceptable
16
16
  * WCAG contrast ratio (default 4.5:1 — AA for normal text).
17
17
  */
18
+ /*********************************************************
19
+ * IContrastScheduled
20
+ *
21
+ * @description
22
+ * What `v-contrast` has queued for one element: the animation frame that
23
+ * starts the measure, the fallback timer (browsers without `getAnimations`,
24
+ * derived from the declared transition) and a flag that cancels a measure
25
+ * already waiting on a transition. Stored on the element as `_contrastTimers`.
26
+ ********************************************************/
27
+ export interface IContrastScheduled {
28
+ raf?: number;
29
+ timeout?: number;
30
+ cancelled?: boolean;
31
+ }
18
32
  export interface IContrastOptions {
19
33
  enabled?: boolean;
20
34
  threshold?: number;
@@ -23,3 +23,24 @@ export interface IStyleTagOptions extends IConfigurableDocument {
23
23
  */
24
24
  id?: string;
25
25
  }
26
+ /*********************************************************
27
+ * ISsrStyleEntry / ISsrStyleCollector (#1092)
28
+ *
29
+ * @description
30
+ * `ISsrStyleEntry` is one `useStyle()` rule resolved for the server render:
31
+ * `id` is the DOM id of the `<style>` tag it belongs to, `css` the
32
+ * `#element-id { … }` rule, already safe to embed in a `<style>`.
33
+ *
34
+ * @description
35
+ * `ISsrStyleCollector` is the per-app registry of those rules. Rules are
36
+ * registered as GETTERS and read at collection time, so a value a later pass
37
+ * of the render settled on is what ships.
38
+ ********************************************************/
39
+ export interface ISsrStyleEntry {
40
+ id: string;
41
+ css: string;
42
+ }
43
+ export interface ISsrStyleCollector {
44
+ add(id: string, css: () => string): void;
45
+ entries(): ISsrStyleEntry[];
46
+ }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
 
7
7
  var _origam = require("../origam.cjs");
8
8
  var _applyTheme = require("../utils/Commons/apply-theme.util.cjs");
9
+ var _ssrStyles = require("../utils/Commons/ssr-styles.util.cjs");
9
10
  var _theme = require("../consts/Commons/theme.const.cjs");
10
11
  var _app = require("#app");
11
12
  function resolveServerTheme(cookieValue, config) {
@@ -79,6 +80,23 @@ module.exports = (0, _app.defineNuxtPlugin)({
79
80
  });
80
81
  origam._defaultsRef.value = origam._activeDefaultsFor(resolvedTheme === _theme.ORIGAM_THEME_AUTO ? "" : resolvedTheme, resolvedMode);
81
82
  nuxtApp.vueApp.use(origam);
83
+ nuxtApp.hook("app:rendered", ({
84
+ ssrContext
85
+ }) => {
86
+ const styles = (0, _ssrStyles.getSsrStyles)(nuxtApp.vueApp);
87
+ if (styles.length && ssrContext) {
88
+ ssrContext.head.push({
89
+ style: styles.map(({
90
+ id,
91
+ css
92
+ }) => ({
93
+ id,
94
+ key: id,
95
+ innerHTML: css
96
+ }))
97
+ });
98
+ }
99
+ });
82
100
  return {
83
101
  provide: {
84
102
  origam: {
@@ -1,5 +1,6 @@
1
1
  import { createOrigam } from "../origam.js";
2
2
  import { themeToCss } from "../utils/Commons/apply-theme.util.js";
3
+ import { getSsrStyles } from "../utils/Commons/ssr-styles.util.js";
3
4
  import { ORIGAM_MODE_ATTR, ORIGAM_MODE_DARK, ORIGAM_MODE_LIGHT, ORIGAM_THEME_ATTR, ORIGAM_THEME_AUTO } from "../consts/Commons/theme.const.js";
4
5
  import { defineNuxtPlugin, useCookie, useHead, useRequestHeaders, useRuntimeConfig } from "#app";
5
6
  function resolveServerTheme(cookieValue, config) {
@@ -69,6 +70,14 @@ export default defineNuxtPlugin({
69
70
  resolvedMode
70
71
  );
71
72
  nuxtApp.vueApp.use(origam);
73
+ nuxtApp.hook("app:rendered", ({ ssrContext }) => {
74
+ const styles = getSsrStyles(nuxtApp.vueApp);
75
+ if (styles.length && ssrContext) {
76
+ ssrContext.head.push({
77
+ style: styles.map(({ id, css }) => ({ id, key: id, innerHTML: css }))
78
+ });
79
+ }
80
+ });
72
81
  return {
73
82
  provide: {
74
83
  origam: {
@@ -69,6 +69,9 @@ function createOrigam(origam = {}) {
69
69
  app.provide(_consts.ORIGAM_GO_TO_KEY, goTo);
70
70
  app.provide(_consts.ORIGAM_THEMES_KEY, installedThemes);
71
71
  app.provide(_consts.ORIGAM_DEFAULTS_KEY, defaultsRef);
72
+ if (!_consts.IN_BROWSER) {
73
+ app.provide(_consts.ORIGAM_SSR_STYLES_KEY, (0, _utils.registerSsrStyleCollector)(app));
74
+ }
72
75
  (0, _composables.installThemePropsResolver)(app, themedKeysUnion, variantPresets);
73
76
  app.provide(_consts.ORIGAM_THEME_DEFAULTS_KEY, (brand, mode) => activeDefaultsFor(allThemes, brand, mode));
74
77
  if (_consts.IN_BROWSER && options.ssr) {
@@ -20,13 +20,14 @@ import {
20
20
  ORIGAM_GO_TO_KEY,
21
21
  ORIGAM_ICONS_KEY,
22
22
  ORIGAM_LOCALE_KEY,
23
+ ORIGAM_SSR_STYLES_KEY,
23
24
  ORIGAM_THEME_DEFAULTS_KEY,
24
25
  ORIGAM_THEMES_KEY,
25
26
  VARIANT_PRESETS
26
27
  } from "./consts/index.js";
27
28
  import * as origamDirectives from "./directives/index.js";
28
29
  import { setContrastConfig } from "./directives/Contrast/contrast.directive.js";
29
- import { applyThemes, installedThemesFromList, mergeDeep } from "./utils/index.js";
30
+ import { applyThemes, installedThemesFromList, mergeDeep, registerSsrStyleCollector } from "./utils/index.js";
30
31
  import "@mdi/font/css/materialdesignicons.css";
31
32
  import { defineComponent, effectScope, nextTick, reactive } from "vue";
32
33
  export function createOrigam(origam = {}) {
@@ -88,6 +89,9 @@ export function createOrigam(origam = {}) {
88
89
  app.provide(ORIGAM_GO_TO_KEY, goTo);
89
90
  app.provide(ORIGAM_THEMES_KEY, installedThemes);
90
91
  app.provide(ORIGAM_DEFAULTS_KEY, defaultsRef);
92
+ if (!IN_BROWSER) {
93
+ app.provide(ORIGAM_SSR_STYLES_KEY, registerSsrStyleCollector(app));
94
+ }
91
95
  installThemePropsResolver(app, themedKeysUnion, variantPresets);
92
96
  app.provide(ORIGAM_THEME_DEFAULTS_KEY, (brand, mode) => activeDefaultsFor(allThemes, brand, mode));
93
97
  if (IN_BROWSER && options.ssr) {