origam 2.35.1 → 2.36.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 (37) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/src/assets/locales/en.json +5 -0
  3. package/dist/src/assets/locales/fr.json +5 -0
  4. package/dist/src/components/Code/OrigamCode.vue +21 -9
  5. package/dist/src/components/CodeGroup/OrigamCodeGroup.vue +215 -0
  6. package/dist/src/components/CodeGroup/index.cjs +13 -0
  7. package/dist/src/components/CodeGroup/index.d.ts +1 -0
  8. package/dist/src/components/CodeGroup/index.js +1 -0
  9. package/dist/src/components/index.cjs +11 -0
  10. package/dist/src/components/index.d.ts +1 -0
  11. package/dist/src/components/index.js +1 -0
  12. package/dist/src/consts/CodeGroup/code-group.const.cjs +17 -0
  13. package/dist/src/consts/CodeGroup/code-group.const.d.ts +16 -0
  14. package/dist/src/consts/CodeGroup/code-group.const.js +11 -0
  15. package/dist/src/consts/index.cjs +11 -0
  16. package/dist/src/consts/index.d.ts +1 -0
  17. package/dist/src/consts/index.js +1 -0
  18. package/dist/src/interfaces/Code/code.interface.d.ts +20 -0
  19. package/dist/src/interfaces/CodeGroup/code-group.interface.cjs +1 -0
  20. package/dist/src/interfaces/CodeGroup/code-group.interface.d.ts +102 -0
  21. package/dist/src/interfaces/CodeGroup/code-group.interface.js +0 -0
  22. package/dist/src/interfaces/index.cjs +11 -0
  23. package/dist/src/interfaces/index.d.ts +1 -0
  24. package/dist/src/interfaces/index.js +1 -0
  25. package/dist/src/types/CodeGroup/code-group.type.cjs +1 -0
  26. package/dist/src/types/CodeGroup/code-group.type.d.ts +2 -0
  27. package/dist/src/types/CodeGroup/code-group.type.js +0 -0
  28. package/dist/src/types/index.cjs +11 -0
  29. package/dist/src/types/index.d.ts +1 -0
  30. package/dist/src/types/index.js +1 -0
  31. package/dist/src/utils/CodeGroup/code-group.util.cjs +99 -0
  32. package/dist/src/utils/CodeGroup/code-group.util.d.ts +51 -0
  33. package/dist/src/utils/CodeGroup/code-group.util.js +66 -0
  34. package/dist/src/utils/index.cjs +11 -0
  35. package/dist/src/utils/index.d.ts +1 -0
  36. package/dist/src/utils/index.js +1 -0
  37. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -18,6 +18,16 @@ This project follows [Semantic Versioning](https://semver.org).
18
18
 
19
19
  ## [Unreleased]
20
20
 
21
+ ## [2.36.0] - 2026-10-19
22
+
23
+ ### Added — `OrigamCodeGroup` : groupe de code à onglets (npm / pnpm / yarn) en une seule surface (#1179)
24
+
25
+ - Nouveau composant `OrigamCodeGroup`, qui réunit `OrigamTabs` et `OrigamCode` : une seule surface (bordure, rayon et élévation portés par le groupe, blocs internes aplatis et sans en-tête), un seul bouton copier qui copie le code de l'onglet actif (slot `copy` pour le remplacer), `v-model` sur l'onglet actif.
26
+ - Deux formes : prop `items` (`{ label, code, lang?, filename?, value? }`) ou slot avec des `<origam-code>` enfants ; le libellé de l'onglet vient de `label`, sinon de `filename`, sinon de la position.
27
+ - Chaque zone défilante porte un nom accessible distinct (`{groupe}, {onglet}, code block {n} of {total}, scrollable region`, clés `origam.code_group.*` en `en` et `fr`) : plus de `landmark-unique` axe entre panneaux. Donner un `label` à chaque groupe quand plusieurs se trouvent sur une même page.
28
+ - `OrigamCode` gagne `hideHeader`, `flat` et `scrollerAriaLabel`, utilisables seuls.
29
+ - Aucun nouveau token : le groupe lit `--origam-code---*`, donc un thème qui restyle `OrigamCode` restyle aussi le groupe.
30
+
21
31
  ## [2.35.1] - 2026-10-19
22
32
 
23
33
  ### Fixed — `OrigamCode` : un 2e bloc monté pendant un import de `shiki` en échec restait vide (#1189)
@@ -175,6 +175,11 @@
175
175
  "scroller_aria_label_filename": "{filename}, code block, scrollable region",
176
176
  "scroller_aria_label_lang": "{lang} code block, scrollable region"
177
177
  },
178
+ "code_group": {
179
+ "tabs_aria_label": "Code variants",
180
+ "scroller_aria_label": "{label}, code block {position} of {total}, scrollable region",
181
+ "scroller_aria_label_group": "{group}, {label}, code block {position} of {total}, scrollable region"
182
+ },
178
183
  "qr_code": {
179
184
  "aria_label": "QR code for {value}"
180
185
  },
@@ -175,6 +175,11 @@
175
175
  "scroller_aria_label_filename": "{filename}, bloc de code, zone défilante",
176
176
  "scroller_aria_label_lang": "Bloc de code {lang}, zone défilante"
177
177
  },
178
+ "code_group": {
179
+ "tabs_aria_label": "Variantes du code",
180
+ "scroller_aria_label": "{label}, bloc de code {position} sur {total}, zone défilante",
181
+ "scroller_aria_label_group": "{group}, {label}, bloc de code {position} sur {total}, zone défilante"
182
+ },
178
183
  "qr_code": {
179
184
  "aria_label": "QR code pour {value}"
180
185
  },
@@ -124,6 +124,9 @@ const props = defineProps({
124
124
  filename: { type: String, required: false, default: void 0 },
125
125
  compact: { type: Boolean, required: false, default: false },
126
126
  prompt: { type: String, required: false, default: void 0 },
127
+ hideHeader: { type: Boolean, required: false, default: false },
128
+ flat: { type: Boolean, required: false, default: false },
129
+ scrollerAriaLabel: { type: String, required: false, default: void 0 },
127
130
  id: { type: String, required: false },
128
131
  class: { type: [String, Array, Object], required: false },
129
132
  style: { type: [String, Array, Object, Boolean, null], required: false, skipCheck: true },
@@ -207,6 +210,7 @@ const { typographyStyles } = useTypography(props, "code");
207
210
  const { highlight } = useCode();
208
211
  const { t } = useLocale();
209
212
  const scrollerLabel = computed(() => {
213
+ if (props.scrollerAriaLabel) return props.scrollerAriaLabel;
210
214
  if (props.filename) return t("origam.code.scroller_aria_label_filename", { filename: props.filename });
211
215
  if (props.lang && props.lang !== CODE_LANG.PLAINTEXT) return t("origam.code.scroller_aria_label_lang", { lang: props.lang });
212
216
  return t("origam.code.scroller_aria_label");
@@ -323,7 +327,7 @@ const copyAriaLabel = computed(
323
327
  const copyBtnStyle = {
324
328
  padding: "var(--origam-code__copy---padding-block) var(--origam-code__copy---padding-inline)"
325
329
  };
326
- const showHeader = computed(() => !props.compact && (!!props.filename || !!slots.header || props.copyable));
330
+ const showHeader = computed(() => !props.compact && !props.hideHeader && (!!props.filename || !!slots.header || props.copyable));
327
331
  const compactCopyIcon = computed(() => copied.value ? "mdi-check" : "mdi-content-copy");
328
332
  const headerTag = computed(() => props.tag === "figure" ? "figcaption" : "div");
329
333
  const codeClasses = computed(() => [
@@ -335,21 +339,22 @@ const codeClasses = computed(() => [
335
339
  "origam-code--wrap": props.wrap,
336
340
  "origam-code--has-header": showHeader.value,
337
341
  "origam-code--copyable": props.copyable,
338
- "origam-code--max-height": props.maxHeight != null
342
+ "origam-code--max-height": props.maxHeight != null,
343
+ "origam-code--flat": props.flat
339
344
  },
340
- borderClasses.value,
341
- roundedClasses.value,
342
- elevationClasses.value,
345
+ props.flat ? [] : borderClasses.value,
346
+ props.flat ? [] : roundedClasses.value,
347
+ props.flat ? [] : elevationClasses.value,
343
348
  paddingClasses.value,
344
- marginClasses.value,
349
+ props.flat ? [] : marginClasses.value,
345
350
  colorClasses.value,
346
351
  props.class
347
352
  ]);
348
353
  const codeStyles = computed(() => [
349
- borderStyles.value,
350
- roundedStyles.value,
354
+ props.flat ? {} : borderStyles.value,
355
+ props.flat ? {} : roundedStyles.value,
351
356
  paddingStyles.value,
352
- marginStyles.value,
357
+ props.flat ? {} : marginStyles.value,
353
358
  colorStyles.value,
354
359
  dimensionStyles.value,
355
360
  typographyStyles.value,
@@ -420,6 +425,13 @@ defineExpose({ rebuild, copied, codeRef });
420
425
  white-space: nowrap;
421
426
  }
422
427
 
428
+ .origam-code--flat {
429
+ border: 0;
430
+ border-radius: 0;
431
+ box-shadow: none;
432
+ margin: 0;
433
+ }
434
+
423
435
  .origam-code__prompt {
424
436
  flex: none;
425
437
  color: var(--origam-code__prompt---color);
@@ -0,0 +1,215 @@
1
+ <template>
2
+ <component
3
+ :is="tag"
4
+ :id="id"
5
+ :class="groupClasses"
6
+ :style="groupStyles"
7
+ >
8
+ <origam-tabs
9
+ v-model="model"
10
+ class="origam-code-group__tabs"
11
+ variant="underline"
12
+ :aria-label="tabsLabel"
13
+ data-cy="origam-code-group-tabs"
14
+ >
15
+ <origam-tab
16
+ v-for="item in getItems()"
17
+ :key="item.value"
18
+ :value="item.value"
19
+ :text="item.label"
20
+ />
21
+ </origam-tabs>
22
+
23
+ <slot
24
+ v-if="copyable"
25
+ name="copy"
26
+ :copy="handleCopy"
27
+ :copied="copied"
28
+ >
29
+ <origam-btn
30
+ variant="text"
31
+ class="origam-code__copy origam-code-group__copy"
32
+ :text="copyButtonLabel"
33
+ :aria-label="copyAriaLabel"
34
+ :style="copyBtnStyle"
35
+ aria-live="polite"
36
+ data-cy="origam-code-group-copy"
37
+ @click="handleCopy"
38
+ />
39
+ </slot>
40
+
41
+ <origam-tab-panels
42
+ v-model="model"
43
+ class="origam-code-group__panels"
44
+ :transition="false"
45
+ >
46
+ <origam-tab-panel
47
+ v-for="item in getItems()"
48
+ :key="item.value"
49
+ :value="item.value"
50
+ >
51
+ <component :is="item.vnode"/>
52
+ </origam-tab-panel>
53
+ </origam-tab-panels>
54
+ </component>
55
+ </template>
56
+
57
+ <script lang="ts" setup>
58
+ import { computed, useSlots } from "vue";
59
+ import OrigamBtn from "../Btn/OrigamBtn.vue";
60
+ import OrigamTab from "../Tabs/OrigamTab.vue";
61
+ import OrigamTabPanel from "../Tabs/OrigamTabPanel.vue";
62
+ import OrigamTabPanels from "../Tabs/OrigamTabPanels.vue";
63
+ import OrigamTabs from "../Tabs/OrigamTabs.vue";
64
+ import { useBorder } from "../../composables/Commons/border.composable";
65
+ import { useClipboard } from "../../composables/Clipboard/clipboard.composable";
66
+ import { useDimension } from "../../composables/Commons/dimension.composable";
67
+ import { useElevation } from "../../composables/Commons/elevation.composable";
68
+ import { useLocale } from "../../composables/Commons/locale.composable";
69
+ import { useMargin } from "../../composables/Commons/margin.composable";
70
+ import { useRounded } from "../../composables/Commons/rounded.composable";
71
+ import { useVModel } from "../../composables/Commons/vModel.composable";
72
+ import { CODE_DEFAULTS } from "../../consts/Code/code.const";
73
+ import { findCodeGroupItem, resolveCodeGroupItems } from "../../utils/CodeGroup/code-group.util";
74
+ const props = defineProps({
75
+ items: { type: Array, required: false, default: () => [] },
76
+ modelValue: { type: String, required: false, default: void 0 },
77
+ label: { type: String, required: false, default: void 0 },
78
+ copyable: { type: Boolean, required: false, default: true },
79
+ lineNumbers: { type: Boolean, required: false, default: false },
80
+ wrap: { type: Boolean, required: false, default: false },
81
+ id: { type: String, required: false },
82
+ class: { type: [String, Array, Object], required: false },
83
+ style: { type: [String, Array, Object, Boolean, null], required: false, skipCheck: true },
84
+ tag: { type: String, required: false, default: "figure" },
85
+ border: { type: [Boolean, Number, String, Array], required: false, skipCheck: true },
86
+ borderTop: { type: [Boolean, Number, String], required: false },
87
+ borderLeft: { type: [Boolean, Number, String], required: false },
88
+ borderBottom: { type: [Boolean, Number, String], required: false },
89
+ borderRight: { type: [Boolean, Number, String], required: false },
90
+ borderBlock: { type: [Boolean, Number, String], required: false },
91
+ borderInline: { type: [Boolean, Number, String], required: false },
92
+ borderInlineStart: { type: [Boolean, Number, String], required: false },
93
+ borderInlineEnd: { type: [Boolean, Number, String], required: false },
94
+ borderBlockStart: { type: [Boolean, Number, String], required: false },
95
+ borderBlockEnd: { type: [Boolean, Number, String], required: false },
96
+ borderColor: { type: String, required: false },
97
+ borderStyle: { type: String, required: false },
98
+ borderTopColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
99
+ borderRightColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
100
+ borderBottomColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
101
+ borderLeftColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
102
+ borderInlineStartColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
103
+ borderInlineEndColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
104
+ borderBlockStartColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
105
+ borderBlockEndColor: { type: [String, Object, Boolean, null], required: false, skipCheck: true },
106
+ rounded: { type: [Boolean, Number, String, null], required: false, skipCheck: true },
107
+ roundedTopRight: { type: [Boolean, Number, String], required: false },
108
+ roundedTopLeft: { type: [Boolean, Number, String], required: false },
109
+ roundedBottomLeft: { type: [Boolean, Number, String], required: false },
110
+ roundedBottomRight: { type: [Boolean, Number, String], required: false },
111
+ roundedStartStart: { type: [Boolean, Number, String], required: false },
112
+ roundedStartEnd: { type: [Boolean, Number, String], required: false },
113
+ roundedEndStart: { type: [Boolean, Number, String], required: false },
114
+ roundedEndEnd: { type: [Boolean, Number, String], required: false },
115
+ elevation: { type: [Number, String], required: false },
116
+ margin: { type: [Boolean, Number, String], required: false },
117
+ marginTop: { type: [Boolean, Number, String], required: false },
118
+ marginLeft: { type: [Boolean, Number, String], required: false },
119
+ marginBottom: { type: [Boolean, Number, String], required: false },
120
+ marginRight: { type: [Boolean, Number, String], required: false },
121
+ marginBlock: { type: [Boolean, Number, String], required: false },
122
+ marginInline: { type: [Boolean, Number, String], required: false },
123
+ marginInlineStart: { type: [Boolean, Number, String], required: false },
124
+ marginInlineEnd: { type: [Boolean, Number, String], required: false },
125
+ marginBlockStart: { type: [Boolean, Number, String], required: false },
126
+ marginBlockEnd: { type: [Boolean, Number, String], required: false },
127
+ height: { type: [Number, String], required: false },
128
+ maxHeight: { type: [Number, String], required: false },
129
+ maxWidth: { type: [Number, String], required: false },
130
+ minHeight: { type: [Number, String], required: false },
131
+ minWidth: { type: [Number, String], required: false },
132
+ width: { type: [Number, String], required: false }
133
+ });
134
+ const emit = defineEmits(["update:modelValue", "copy"]);
135
+ defineSlots();
136
+ const slots = useSlots();
137
+ const { t } = useLocale();
138
+ function getItems() {
139
+ return resolveCodeGroupItems(props.items, slots.default?.(), {
140
+ label: props.label,
141
+ lineNumbers: props.lineNumbers,
142
+ wrap: props.wrap,
143
+ t
144
+ });
145
+ }
146
+ const model = useVModel(props, "modelValue", () => getItems()[0]?.value);
147
+ const tabsLabel = computed(() => props.label ?? t("origam.code_group.tabs_aria_label"));
148
+ const { copy, copied } = useClipboard({ feedbackDuration: CODE_DEFAULTS.copyFeedbackDurationMs });
149
+ async function handleCopy() {
150
+ const code = findCodeGroupItem(getItems(), model.value)?.code ?? "";
151
+ if (await copy(code)) emit("copy", code);
152
+ }
153
+ const copyButtonLabel = computed(() => copied.value ? t("origam.code.copied") : t("origam.code.copy"));
154
+ const copyAriaLabel = computed(() => copied.value ? t("origam.code.copied_aria_label") : t("origam.code.copy_aria_label"));
155
+ const copyBtnStyle = {
156
+ padding: "var(--origam-code__copy---padding-block) var(--origam-code__copy---padding-inline)"
157
+ };
158
+ const { borderClasses, borderStyles } = useBorder(props);
159
+ const { roundedClasses, roundedStyles } = useRounded(props);
160
+ const { elevationClasses } = useElevation(props);
161
+ const { marginClasses, marginStyles } = useMargin(props);
162
+ const { dimensionStyles } = useDimension(props);
163
+ const groupClasses = computed(() => [
164
+ "origam-code-group",
165
+ { "origam-code-group--copyable": props.copyable },
166
+ borderClasses.value,
167
+ roundedClasses.value,
168
+ elevationClasses.value,
169
+ marginClasses.value,
170
+ props.class
171
+ ]);
172
+ const groupStyles = computed(() => [
173
+ borderStyles.value,
174
+ roundedStyles.value,
175
+ marginStyles.value,
176
+ dimensionStyles.value,
177
+ props.style
178
+ ]);
179
+ defineExpose({ copied, model });
180
+ </script>
181
+
182
+ <style lang="scss">
183
+ .origam-code-group {
184
+ display: grid;
185
+ grid-template-columns: minmax(0, 1fr) auto;
186
+ grid-template-areas:
187
+ "tabs copy"
188
+ "panels panels";
189
+ align-items: center;
190
+ margin: 0;
191
+ overflow: hidden;
192
+ background-color: var(--origam-code__header---background-color);
193
+ color: var(--origam-code---color);
194
+ border: var(--origam-code---border-width) solid var(--origam-code---border-color);
195
+ border-radius: var(--origam-code---border-radius);
196
+ }
197
+
198
+ .origam-code-group__tabs {
199
+ grid-area: tabs;
200
+ min-inline-size: 0;
201
+ overflow-x: auto;
202
+ }
203
+
204
+ .origam-code-group__copy {
205
+ grid-area: copy;
206
+ margin-inline-end: var(--origam-code__header---padding-inline);
207
+ }
208
+
209
+ .origam-code-group__panels {
210
+ grid-area: panels;
211
+ min-inline-size: 0;
212
+ background-color: var(--origam-code---background-color);
213
+ border-top: var(--origam-code---border-width) solid var(--origam-code---border-color);
214
+ }
215
+ </style>
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "OrigamCodeGroup", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _OrigamCodeGroup.default;
10
+ }
11
+ });
12
+ var _OrigamCodeGroup = _interopRequireDefault(require("./OrigamCodeGroup.vue"));
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -0,0 +1 @@
1
+ export { default as OrigamCodeGroup } from './OrigamCodeGroup.vue';
@@ -0,0 +1 @@
1
+ export { default as OrigamCodeGroup } from "./OrigamCodeGroup.vue";
@@ -201,6 +201,17 @@ Object.keys(_Code).forEach(function (key) {
201
201
  }
202
202
  });
203
203
  });
204
+ var _CodeGroup = require("./CodeGroup/index.cjs");
205
+ Object.keys(_CodeGroup).forEach(function (key) {
206
+ if (key === "default" || key === "__esModule") return;
207
+ if (key in exports && exports[key] === _CodeGroup[key]) return;
208
+ Object.defineProperty(exports, key, {
209
+ enumerable: true,
210
+ get: function () {
211
+ return _CodeGroup[key];
212
+ }
213
+ });
214
+ });
204
215
  var _ColorPicker = require("./ColorPicker/index.cjs");
205
216
  Object.keys(_ColorPicker).forEach(function (key) {
206
217
  if (key === "default" || key === "__esModule") return;
@@ -16,6 +16,7 @@ export * from './Chip';
16
16
  export * from './ClientOnly';
17
17
  export * from './Clipboard';
18
18
  export * from './Code';
19
+ export * from './CodeGroup';
19
20
  export * from './ColorPicker';
20
21
  export * from './ColorPickerField';
21
22
  export * from './CommandPalette';
@@ -16,6 +16,7 @@ export * from "./Chip/index.js";
16
16
  export * from "./ClientOnly/index.js";
17
17
  export * from "./Clipboard/index.js";
18
18
  export * from "./Code/index.js";
19
+ export * from "./CodeGroup/index.js";
19
20
  export * from "./ColorPicker/index.js";
20
21
  export * from "./ColorPickerField/index.js";
21
22
  export * from "./CommandPalette/index.js";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.CODE_GROUP_EMBED_PROPS = exports.CODE_GROUP_DEFAULTS = void 0;
7
+ const CODE_GROUP_EMBED_PROPS = exports.CODE_GROUP_EMBED_PROPS = {
8
+ hideHeader: true,
9
+ flat: true,
10
+ copyable: false
11
+ };
12
+ const CODE_GROUP_DEFAULTS = exports.CODE_GROUP_DEFAULTS = {
13
+ tag: "figure",
14
+ copyable: true,
15
+ lineNumbers: false,
16
+ wrap: false
17
+ };
@@ -0,0 +1,16 @@
1
+ import type { ICodeProps } from '../../interfaces/Code/code.interface';
2
+ /*********************************************************
3
+ * CODE_GROUP_EMBED_PROPS
4
+ *
5
+ * @description
6
+ * What `<OrigamCodeGroup>` forces on every `<OrigamCode>` it hosts: the tab
7
+ * already shows the label, the group already owns the one surface and the
8
+ * one copy button.
9
+ ********************************************************/
10
+ export declare const CODE_GROUP_EMBED_PROPS: Pick<ICodeProps, 'hideHeader' | 'flat' | 'copyable'>;
11
+ export declare const CODE_GROUP_DEFAULTS: {
12
+ readonly tag: "figure";
13
+ readonly copyable: true;
14
+ readonly lineNumbers: false;
15
+ readonly wrap: false;
16
+ };
@@ -0,0 +1,11 @@
1
+ export const CODE_GROUP_EMBED_PROPS = {
2
+ hideHeader: true,
3
+ flat: true,
4
+ copyable: false
5
+ };
6
+ export const CODE_GROUP_DEFAULTS = {
7
+ tag: "figure",
8
+ copyable: true,
9
+ lineNumbers: false,
10
+ wrap: false
11
+ };
@@ -366,6 +366,17 @@ Object.keys(_code).forEach(function (key) {
366
366
  }
367
367
  });
368
368
  });
369
+ var _codeGroup = require("./CodeGroup/code-group.const.cjs");
370
+ Object.keys(_codeGroup).forEach(function (key) {
371
+ if (key === "default" || key === "__esModule") return;
372
+ if (key in exports && exports[key] === _codeGroup[key]) return;
373
+ Object.defineProperty(exports, key, {
374
+ enumerable: true,
375
+ get: function () {
376
+ return _codeGroup[key];
377
+ }
378
+ });
379
+ });
369
380
  var _selectionControl = require("./SelectionControl/selection-control.const.cjs");
370
381
  Object.keys(_selectionControl).forEach(function (key) {
371
382
  if (key === "default" || key === "__esModule") return;
@@ -31,6 +31,7 @@ export * from './Commons/vmodel.const';
31
31
  export * from './Commons/forwardRefs.const';
32
32
  export * from './ColorPicker/color-picker.const';
33
33
  export * from './Code/code.const';
34
+ export * from './CodeGroup/code-group.const';
34
35
  export * from './SelectionControl/selection-control.const';
35
36
  export * from './Snackbar/snackbar-group.const';
36
37
  export * from './Snackbar/snackbar.const';
@@ -31,6 +31,7 @@ export * from "./Commons/vmodel.const.js";
31
31
  export * from "./Commons/forwardRefs.const.js";
32
32
  export * from "./ColorPicker/color-picker.const.js";
33
33
  export * from "./Code/code.const.js";
34
+ export * from "./CodeGroup/code-group.const.js";
34
35
  export * from "./SelectionControl/selection-control.const.js";
35
36
  export * from "./Snackbar/snackbar-group.const.js";
36
37
  export * from "./Snackbar/snackbar.const.js";
@@ -88,7 +88,27 @@ export interface ICodeProps extends ICommonsComponentProps, ITagProps, IBorderPr
88
88
  * Most useful paired with `compact` for an install-command pill.
89
89
  */
90
90
  prompt?: string;
91
+ hideHeader?: boolean;
92
+ flat?: boolean;
93
+ scrollerAriaLabel?: string;
91
94
  }
95
+ /*********************************************************
96
+ * Embedding props (`hideHeader`, `flat`, `scrollerAriaLabel`)
97
+ *
98
+ * @description
99
+ * `hideHeader` removes the header bar (filename / language badge / copy
100
+ * button) whatever `filename`, `copyable` or the `header` slot say.
101
+ * @description
102
+ * `flat` flattens the block into its host: no border, no radius, no
103
+ * elevation, no outer margin, so the host owns the one visible surface.
104
+ * @description
105
+ * `scrollerAriaLabel` is the accessible name of the scrollable region. Left
106
+ * out, it is derived from `filename`, then `lang`; set it when several blocks
107
+ * of the same language share a page.
108
+ * @description
109
+ * The three exist so a block can sit inside `<OrigamCodeGroup>`, a tab already
110
+ * showing the label and the group owning the surface and the copy button.
111
+ ********************************************************/
92
112
  /*********************************************************
93
113
  * ICodeOptions
94
114
  *
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,102 @@
1
+ import type { VNode } from 'vue';
2
+ import type { IBorderProps } from '../Commons/border.interface';
3
+ import type { ICommonsComponentEmits, ICommonsComponentProps, ITagProps } from '../Commons/commons.interface';
4
+ import type { IDimensionProps } from '../Commons/dimension.interface';
5
+ import type { IElevationProps } from '../Commons/elevation.interface';
6
+ import type { IMarginProps } from '../Commons/margin.interface';
7
+ import type { IRoundedProps } from '../Commons/rounded.interface';
8
+ import type { TCodeLangOpen } from '../../types/Code/code.type';
9
+ /*********************************************************
10
+ * ICodeGroupItem
11
+ *
12
+ * @description
13
+ * One tab of an `<OrigamCodeGroup>` in its `items` form: the label shown on
14
+ * the tab and the code shown in its panel.
15
+ ********************************************************/
16
+ export interface ICodeGroupItem {
17
+ label: string;
18
+ code: string;
19
+ lang?: TCodeLangOpen;
20
+ filename?: string;
21
+ value?: string;
22
+ }
23
+ /*********************************************************
24
+ * ICodeGroupResolvedItem
25
+ *
26
+ * @description
27
+ * An `ICodeGroupItem` once the group has settled its `value` and, in the
28
+ * slot form, kept the child `<OrigamCode>` vnode to render in the panel.
29
+ ********************************************************/
30
+ export interface ICodeGroupResolvedItem extends Required<Pick<ICodeGroupItem, 'label' | 'code' | 'value'>>, Pick<ICodeGroupItem, 'lang' | 'filename'> {
31
+ vnode?: VNode;
32
+ }
33
+ /*********************************************************
34
+ * ICodeGroupRenderedItem
35
+ *
36
+ * @description
37
+ * A resolved item whose `vnode` is the `<OrigamCode>` to render in the panel,
38
+ * already flattened, header-less and named. Built by `resolveCodeGroupItems`.
39
+ ********************************************************/
40
+ export interface ICodeGroupRenderedItem extends Omit<ICodeGroupResolvedItem, 'vnode'> {
41
+ vnode: VNode;
42
+ }
43
+ /*********************************************************
44
+ * ICodeGroupResolveOptions
45
+ *
46
+ * @description
47
+ * What `resolveCodeGroupItems` needs from the group: its `label` and `t` to
48
+ * name each scroller, and `lineNumbers` / `wrap` to forward to every block.
49
+ ********************************************************/
50
+ export interface ICodeGroupResolveOptions {
51
+ label?: string;
52
+ lineNumbers: boolean;
53
+ wrap: boolean;
54
+ t: (key: string, ...params: unknown[]) => string;
55
+ }
56
+ /*********************************************************
57
+ * ICodeGroupProps
58
+ *
59
+ * @description
60
+ * Props for `<OrigamCodeGroup>` — tabbed code blocks (npm / pnpm / yarn)
61
+ * drawn as ONE surface with one shared copy button. Fed either by `items`
62
+ * or by `<origam-code>` children in the default slot (the tab label comes
63
+ * from the child's `label`, else its `filename`).
64
+ * @description
65
+ * `modelValue` is the `value` of the active tab (default: the first tab).
66
+ * `label` is the accessible name of the tab list (default: a localised
67
+ * generic name). `copyable`, `lineNumbers` and `wrap` apply to the whole
68
+ * group. `items` is ignored when the default slot holds code blocks.
69
+ ********************************************************/
70
+ export interface ICodeGroupProps extends ICommonsComponentProps, ITagProps, IBorderProps, IRoundedProps, IElevationProps, IMarginProps, IDimensionProps {
71
+ items?: ICodeGroupItem[];
72
+ modelValue?: string;
73
+ label?: string;
74
+ copyable?: boolean;
75
+ lineNumbers?: boolean;
76
+ wrap?: boolean;
77
+ }
78
+ /*********************************************************
79
+ * ICodeGroupEmits
80
+ *
81
+ * @description
82
+ * `update:modelValue` carries the new active tab `value`; `copy` carries the
83
+ * code of the tab that was copied.
84
+ ********************************************************/
85
+ export interface ICodeGroupEmits extends ICommonsComponentEmits {
86
+ (e: 'update:modelValue', value: string): void;
87
+ (e: 'copy', code: string): void;
88
+ }
89
+ /*********************************************************
90
+ * ICodeGroupSlots
91
+ *
92
+ * @description
93
+ * `default` takes the `<origam-code>` children, one per tab, when `items` is
94
+ * not used. `copy` replaces the shared copy button.
95
+ ********************************************************/
96
+ export interface ICodeGroupSlots {
97
+ default?: () => any;
98
+ copy?: (data: {
99
+ copy: () => Promise<void>;
100
+ copied: boolean;
101
+ }) => any;
102
+ }
@@ -399,6 +399,17 @@ Object.keys(_code).forEach(function (key) {
399
399
  }
400
400
  });
401
401
  });
402
+ var _codeGroup = require("./CodeGroup/code-group.interface.cjs");
403
+ Object.keys(_codeGroup).forEach(function (key) {
404
+ if (key === "default" || key === "__esModule") return;
405
+ if (key in exports && exports[key] === _codeGroup[key]) return;
406
+ Object.defineProperty(exports, key, {
407
+ enumerable: true,
408
+ get: function () {
409
+ return _codeGroup[key];
410
+ }
411
+ });
412
+ });
402
413
  var _colorPicker = require("./ColorPicker/color-picker.interface.cjs");
403
414
  Object.keys(_colorPicker).forEach(function (key) {
404
415
  if (key === "default" || key === "__esModule") return;
@@ -34,6 +34,7 @@ export * from './Chip/chip-group.interface';
34
34
  export * from './ClientOnly/client-only.interface';
35
35
  export * from './Clipboard/clipboard.interface';
36
36
  export * from './Code/code.interface';
37
+ export * from './CodeGroup/code-group.interface';
37
38
  export * from './ColorPicker/color-picker.interface';
38
39
  export * from './ColorPicker/color-picker-canvas.interface';
39
40
  export * from './ConfirmWrapper/confirm-wrapper.interface';
@@ -34,6 +34,7 @@ export * from "./Chip/chip-group.interface.js";
34
34
  export * from "./ClientOnly/client-only.interface.js";
35
35
  export * from "./Clipboard/clipboard.interface.js";
36
36
  export * from "./Code/code.interface.js";
37
+ export * from "./CodeGroup/code-group.interface.js";
37
38
  export * from "./ColorPicker/color-picker.interface.js";
38
39
  export * from "./ColorPicker/color-picker-canvas.interface.js";
39
40
  export * from "./ConfirmWrapper/confirm-wrapper.interface.js";
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,2 @@
1
+ import OrigamCodeGroup from '../../components/CodeGroup/OrigamCodeGroup.vue';
2
+ export type TOrigamCodeGroup = InstanceType<typeof OrigamCodeGroup>;
File without changes
@@ -333,6 +333,17 @@ Object.keys(_code).forEach(function (key) {
333
333
  }
334
334
  });
335
335
  });
336
+ var _codeGroup = require("./CodeGroup/code-group.type.cjs");
337
+ Object.keys(_codeGroup).forEach(function (key) {
338
+ if (key === "default" || key === "__esModule") return;
339
+ if (key in exports && exports[key] === _codeGroup[key]) return;
340
+ Object.defineProperty(exports, key, {
341
+ enumerable: true,
342
+ get: function () {
343
+ return _codeGroup[key];
344
+ }
345
+ });
346
+ });
336
347
  var _colorPicker = require("./ColorPicker/color-picker.type.cjs");
337
348
  Object.keys(_colorPicker).forEach(function (key) {
338
349
  if (key === "default" || key === "__esModule") return;
@@ -28,6 +28,7 @@ export * from './Chip/chip-group.type';
28
28
  export * from './ClientOnly/client-only.type';
29
29
  export * from './Clipboard/clipboard.type';
30
30
  export * from './Code/code.type';
31
+ export * from './CodeGroup/code-group.type';
31
32
  export * from './ColorPicker/color-picker.type';
32
33
  export * from './ColorPicker/color-picker-canvas.type';
33
34
  export * from './ColorPicker/color-picker-preview.type';
@@ -28,6 +28,7 @@ export * from "./Chip/chip-group.type.js";
28
28
  export * from "./ClientOnly/client-only.type.js";
29
29
  export * from "./Clipboard/clipboard.type.js";
30
30
  export * from "./Code/code.type.js";
31
+ export * from "./CodeGroup/code-group.type.js";
31
32
  export * from "./ColorPicker/color-picker.type.js";
32
33
  export * from "./ColorPicker/color-picker-canvas.type.js";
33
34
  export * from "./ColorPicker/color-picker-preview.type.js";
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.extractCodeGroupText = extractCodeGroupText;
7
+ exports.findCodeGroupItem = findCodeGroupItem;
8
+ exports.flattenCodeGroupVNodes = flattenCodeGroupVNodes;
9
+ exports.resolveCodeGroupItems = resolveCodeGroupItems;
10
+ exports.resolveCodeGroupSlotItems = resolveCodeGroupSlotItems;
11
+ var _vue = require("vue");
12
+ var _OrigamCode = _interopRequireDefault(require("../../components/Code/OrigamCode.vue"));
13
+ var _codeGroup = require("../../consts/CodeGroup/code-group.const.cjs");
14
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
+ function flattenCodeGroupVNodes(nodes) {
16
+ const out = [];
17
+ for (const node of nodes ?? []) {
18
+ if (node.type === _vue.Fragment && Array.isArray(node.children)) {
19
+ out.push(...flattenCodeGroupVNodes(node.children));
20
+ } else if (node.type !== _vue.Comment && node.type !== _vue.Text) {
21
+ out.push(node);
22
+ }
23
+ }
24
+ return out;
25
+ }
26
+ function extractCodeGroupText(node) {
27
+ const children = node.children;
28
+ const raw = typeof children === "object" && children !== null && !Array.isArray(children) ? children.default?.() : children;
29
+ const parts = [];
30
+ const visit = value => {
31
+ if (value == null || typeof value === "boolean") return;
32
+ if (typeof value === "string" || typeof value === "number") {
33
+ parts.push(String(value));
34
+ return;
35
+ }
36
+ if (Array.isArray(value)) {
37
+ value.forEach(visit);
38
+ return;
39
+ }
40
+ visit(value.children);
41
+ };
42
+ visit(raw);
43
+ return parts.join("").replace(/^\n+/, "").replace(/\n+$/, "");
44
+ }
45
+ function resolveCodeGroupSlotItems(nodes) {
46
+ return flattenCodeGroupVNodes(nodes).map((vnode, index) => {
47
+ const attrs = vnode.props ?? {};
48
+ const label = String(attrs.label ?? attrs.filename ?? index + 1);
49
+ return {
50
+ label,
51
+ value: String(attrs.value ?? label),
52
+ code: typeof attrs.code === "string" ? attrs.code : extractCodeGroupText(vnode),
53
+ lang: attrs.lang,
54
+ filename: attrs.filename,
55
+ vnode
56
+ };
57
+ });
58
+ }
59
+ function resolveCodeGroupItems(items, nodes, options) {
60
+ const fromSlot = resolveCodeGroupSlotItems(nodes);
61
+ const source = fromSlot.length > 0 ? fromSlot : items.map(item => ({
62
+ ...item,
63
+ value: item.value ?? item.label
64
+ }));
65
+ return source.map((item, index) => {
66
+ const params = {
67
+ label: item.label,
68
+ position: index + 1,
69
+ total: source.length
70
+ };
71
+ const scrollerAriaLabel = options.label ? options.t("origam.code_group.scroller_aria_label_group", {
72
+ ...params,
73
+ group: options.label
74
+ }) : options.t("origam.code_group.scroller_aria_label", params);
75
+ const embed = {
76
+ ..._codeGroup.CODE_GROUP_EMBED_PROPS,
77
+ lineNumbers: options.lineNumbers || void 0,
78
+ wrap: options.wrap || void 0,
79
+ scrollerAriaLabel
80
+ };
81
+ const vnode = item.vnode ? (0, _vue.cloneVNode)(item.vnode, {
82
+ ...embed,
83
+ label: void 0,
84
+ value: void 0
85
+ }) : (0, _vue.h)(_OrigamCode.default, {
86
+ code: item.code,
87
+ lang: item.lang,
88
+ filename: item.filename,
89
+ ...embed
90
+ });
91
+ return {
92
+ ...item,
93
+ vnode
94
+ };
95
+ });
96
+ }
97
+ function findCodeGroupItem(items, value) {
98
+ return items.find(item => item.value === value) ?? items[0];
99
+ }
@@ -0,0 +1,51 @@
1
+ import type { VNode } from 'vue';
2
+ import type { ICodeGroupRenderedItem, ICodeGroupResolvedItem, ICodeGroupResolveOptions } from '../../interfaces/CodeGroup/code-group.interface';
3
+ /*********************************************************
4
+ * flattenCodeGroupVNodes
5
+ *
6
+ * @description
7
+ * Walks a default-slot vnode list, going through fragments (`v-for`), and
8
+ * returns the component vnodes only: comments and whitespace text are the
9
+ * template's noise, not tabs.
10
+ ********************************************************/
11
+ export declare function flattenCodeGroupVNodes(nodes: VNode[] | undefined): VNode[];
12
+ /*********************************************************
13
+ * extractCodeGroupText
14
+ *
15
+ * @description
16
+ * Text content of a child's default slot, used when the block gives its code
17
+ * in the slot rather than in the `code` prop (same rule as `<OrigamCode>`:
18
+ * leading and trailing blank lines dropped).
19
+ ********************************************************/
20
+ export declare function extractCodeGroupText(node: VNode): string;
21
+ /*********************************************************
22
+ * resolveCodeGroupSlotItems
23
+ *
24
+ * @description
25
+ * Builds the tabs of the slot form. The label is the child's `label`, else its
26
+ * `filename`, else its position, so a block is never left without a tab.
27
+ ********************************************************/
28
+ export declare function resolveCodeGroupSlotItems(nodes: VNode[] | undefined): ICodeGroupResolvedItem[];
29
+ /*********************************************************
30
+ * resolveCodeGroupItems
31
+ *
32
+ * @description
33
+ * One list for both forms of `<OrigamCodeGroup>`: the code blocks of the
34
+ * default slot win, `items` is the fallback. Every item comes back with the
35
+ * `<OrigamCode>` vnode to render, already flattened, header-less and named
36
+ * after the group `label`, its tab and its position — so the component does
37
+ * no per-item work in its template.
38
+ * @description
39
+ * `items` is typed `readonly unknown[]` on purpose: the compiled `.vue` that
40
+ * `nuxt typecheck` reads (`packages/ds/dist`) has its types stripped, so the
41
+ * prop reaches this call as `unknown[]` and an `ICodeGroupItem[]` parameter
42
+ * would add one error to the marketing type-check ratchet.
43
+ ********************************************************/
44
+ export declare function resolveCodeGroupItems(items: readonly unknown[], nodes: VNode[] | undefined, options: ICodeGroupResolveOptions): ICodeGroupRenderedItem[];
45
+ /*********************************************************
46
+ * findCodeGroupItem
47
+ *
48
+ * @description
49
+ * The item whose `value` is the active one, the first item when none matches.
50
+ ********************************************************/
51
+ export declare function findCodeGroupItem(items: ICodeGroupRenderedItem[], value: unknown): ICodeGroupRenderedItem | undefined;
@@ -0,0 +1,66 @@
1
+ import { cloneVNode, Comment, Fragment, h, Text } from "vue";
2
+ import OrigamCode from "../../components/Code/OrigamCode.vue";
3
+ import { CODE_GROUP_EMBED_PROPS } from "../../consts/CodeGroup/code-group.const.js";
4
+ export function flattenCodeGroupVNodes(nodes) {
5
+ const out = [];
6
+ for (const node of nodes ?? []) {
7
+ if (node.type === Fragment && Array.isArray(node.children)) {
8
+ out.push(...flattenCodeGroupVNodes(node.children));
9
+ } else if (node.type !== Comment && node.type !== Text) {
10
+ out.push(node);
11
+ }
12
+ }
13
+ return out;
14
+ }
15
+ export function extractCodeGroupText(node) {
16
+ const children = node.children;
17
+ const raw = typeof children === "object" && children !== null && !Array.isArray(children) ? children.default?.() : children;
18
+ const parts = [];
19
+ const visit = (value) => {
20
+ if (value == null || typeof value === "boolean") return;
21
+ if (typeof value === "string" || typeof value === "number") {
22
+ parts.push(String(value));
23
+ return;
24
+ }
25
+ if (Array.isArray(value)) {
26
+ value.forEach(visit);
27
+ return;
28
+ }
29
+ visit(value.children);
30
+ };
31
+ visit(raw);
32
+ return parts.join("").replace(/^\n+/, "").replace(/\n+$/, "");
33
+ }
34
+ export function resolveCodeGroupSlotItems(nodes) {
35
+ return flattenCodeGroupVNodes(nodes).map((vnode, index) => {
36
+ const attrs = vnode.props ?? {};
37
+ const label = String(attrs.label ?? attrs.filename ?? index + 1);
38
+ return {
39
+ label,
40
+ value: String(attrs.value ?? label),
41
+ code: typeof attrs.code === "string" ? attrs.code : extractCodeGroupText(vnode),
42
+ lang: attrs.lang,
43
+ filename: attrs.filename,
44
+ vnode
45
+ };
46
+ });
47
+ }
48
+ export function resolveCodeGroupItems(items, nodes, options) {
49
+ const fromSlot = resolveCodeGroupSlotItems(nodes);
50
+ const source = fromSlot.length > 0 ? fromSlot : items.map((item) => ({ ...item, value: item.value ?? item.label }));
51
+ return source.map((item, index) => {
52
+ const params = { label: item.label, position: index + 1, total: source.length };
53
+ const scrollerAriaLabel = options.label ? options.t("origam.code_group.scroller_aria_label_group", { ...params, group: options.label }) : options.t("origam.code_group.scroller_aria_label", params);
54
+ const embed = {
55
+ ...CODE_GROUP_EMBED_PROPS,
56
+ lineNumbers: options.lineNumbers || void 0,
57
+ wrap: options.wrap || void 0,
58
+ scrollerAriaLabel
59
+ };
60
+ const vnode = item.vnode ? cloneVNode(item.vnode, { ...embed, label: void 0, value: void 0 }) : h(OrigamCode, { code: item.code, lang: item.lang, filename: item.filename, ...embed });
61
+ return { ...item, vnode };
62
+ });
63
+ }
64
+ export function findCodeGroupItem(items, value) {
65
+ return items.find((item) => item.value === value) ?? items[0];
66
+ }
@@ -696,6 +696,17 @@ Object.keys(_parseHighlightLines).forEach(function (key) {
696
696
  }
697
697
  });
698
698
  });
699
+ var _codeGroup = require("./CodeGroup/code-group.util.cjs");
700
+ Object.keys(_codeGroup).forEach(function (key) {
701
+ if (key === "default" || key === "__esModule") return;
702
+ if (key in exports && exports[key] === _codeGroup[key]) return;
703
+ Object.defineProperty(exports, key, {
704
+ enumerable: true,
705
+ get: function () {
706
+ return _codeGroup[key];
707
+ }
708
+ });
709
+ });
699
710
  var _sanitizeHtml = require("./TextareaField/sanitize-html.util.cjs");
700
711
  Object.keys(_sanitizeHtml).forEach(function (key) {
701
712
  if (key === "default" || key === "__esModule") return;
@@ -61,6 +61,7 @@ export * from './DataTable/slot-name.util';
61
61
  export * from './DataTable/skeleton-cell.util';
62
62
  export * from './CommandPalette/fuzzy-match.util';
63
63
  export * from './Code/parse-highlight-lines.util';
64
+ export * from './CodeGroup/code-group.util';
64
65
  export * from './TextareaField/sanitize-html.util';
65
66
  export * from './TextareaField/html-to-markdown.util';
66
67
  export * from './Commons/apply-mask.util';
@@ -61,6 +61,7 @@ export * from "./DataTable/slot-name.util.js";
61
61
  export * from "./DataTable/skeleton-cell.util.js";
62
62
  export * from "./CommandPalette/fuzzy-match.util.js";
63
63
  export * from "./Code/parse-highlight-lines.util.js";
64
+ export * from "./CodeGroup/code-group.util.js";
64
65
  export * from "./TextareaField/sanitize-html.util.js";
65
66
  export * from "./TextareaField/html-to-markdown.util.js";
66
67
  export * from "./Commons/apply-mask.util.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "origam",
3
- "version": "2.35.1",
3
+ "version": "2.36.0",
4
4
  "type": "module",
5
5
  "description": "origam — a Vue 3 design system with multi-theme design tokens, CSS-first components and TypeScript types.",
6
6
  "keywords": [