srcdev-nuxt-components 9.2.8 → 9.2.10

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.
@@ -0,0 +1,34 @@
1
+ ---
2
+ description: Produce release notes as a fenced markdown block, scoped to commits since the last release
3
+ ---
4
+
5
+ Produce release notes for the commits since the last `chore(release):` commit. Always output them as a single fenced ` ```markdown ` code block so the content can be copied directly into a git tag, GitHub release, or changelog — never render it as plain markdown.
6
+
7
+ Steps:
8
+
9
+ 1. **Determine the range.** Run `git log --oneline -20` and find the most recent `chore(release): release vX.Y.Z` commit — that's the boundary. Also check `package.json` for the current version if you need to confirm what's already shipped vs pending.
10
+ 2. **Review each commit since that boundary.** Run `git show <commit> --stat` (and the message) for each one to understand what changed. Skip other `chore(release):` commits themselves, and skip purely internal/tooling commits (e.g. adding slash commands, promoting doc gotchas) unless they're user-facing.
11
+ 3. **Draft the notes** using the format below. Only include sections that have content — omit empty headings. Keep each bullet to one line where possible. Lead with the most user-facing changes (New, Fixed) before internal ones (Changed, Documentation). Fold minor follow-up commits (e.g. a test/docs completion commit for a feature shipped a commit earlier) into the same bullet as the feature rather than giving them their own line — unless the follow-up is substantial enough to warrant one.
12
+ 4. **Output** the result wrapped in a ` ```markdown ` fence. Do not commit, tag, or push anything — this command only produces text for review.
13
+
14
+ ## Format
15
+
16
+ ```markdown
17
+ ## vX.Y.Z
18
+
19
+ ### New
20
+
21
+ - **`ComponentOrComposableName`** — one-line description of what it does and why it exists
22
+
23
+ ### Fixed
24
+
25
+ - Short description of what was wrong and what was corrected
26
+
27
+ ### Changed
28
+
29
+ - Short description of intentional behaviour or API changes
30
+
31
+ ### Documentation
32
+
33
+ - **`skill-name` skill** — new/updated: what it covers
34
+ ```
@@ -96,7 +96,9 @@
96
96
  "Bash(git reset *)",
97
97
  "Read(//Users/simoncornforth/.claude/**)",
98
98
  "Bash(git push *)",
99
- "Bash(node -e \"JSON.parse\\(require\\('fs'\\).readFileSync\\('.vscode/srcdev-component-pricing-card.code-snippets','utf8'\\)\\); console.log\\('valid json'\\)\")"
99
+ "Bash(node -e \"JSON.parse\\(require\\('fs'\\).readFileSync\\('.vscode/srcdev-component-pricing-card.code-snippets','utf8'\\)\\); console.log\\('valid json'\\)\")",
100
+ "Bash(rm /Users/simoncornforth/websites/nuxt-components/.claude/skills/create-release-notes.md)",
101
+ "Bash(node -e \"console.log\\('sanity: file exists and readable'\\)\")"
100
102
  ],
101
103
  "additionalDirectories": [
102
104
  "/Users/simoncornforth/websites/instepreflexology",
@@ -48,7 +48,6 @@ Each skill is a single markdown file named `<area>-<task>.md`.
48
48
  ├── robots-env-aware.md — @nuxtjs/robots: allow crawling on prod domain only, block on preview/staging via env var
49
49
  ├── new-app-scaffold.md — scaffold a new Nuxt consumer app extending this layer (package.json, nuxt.config, app structure, CLAUDE.md)
50
50
  ├── qa-panel.md — collapsible panel for toggling component props live on a page (always on for /pages/ui/ demo pages; gate with isDev in consuming apps)
51
- ├── create-release-notes.md — produce release notes as a fenced markdown block from git log (named to avoid colliding with the built-in /release-notes command)
52
51
  ├── pull-request-description.md — produce a PR description as a fenced markdown block from git diff vs main
53
52
  ├── using-component-skills.md — discover and use component skills in consumer apps: where skills land, browsing patterns, workflow for deciding build vs. compose
54
53
  ├── composable-canonical-url.md — useCanonicalUrl: set <link rel="canonical"> from runtimeConfig.public.canonicalHost; layout setup, node types
@@ -24,7 +24,6 @@ html {
24
24
  color: var(--colour-text-default);
25
25
  font-family: var(--font-family);
26
26
  font-size: var(--step-4);
27
- /* min-height: 100dvh; */
28
27
  transition:
29
28
  background-color 0.4s ease,
30
29
  color 0.4s ease;
@@ -33,12 +32,11 @@ html {
33
32
 
34
33
  #__nuxt {
35
34
  height: 100%;
36
- div {
37
- .page-layout {
38
- min-block-size: 100svh;
39
- display: grid;
40
- grid-template-rows: auto 1fr auto;
41
- }
35
+ .page-layout {
36
+ min-block-size: 100svh;
37
+ display: grid;
38
+ grid-template-rows: auto 1fr auto;
39
+ align-items: start;
42
40
  }
43
41
  }
44
42
  }
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="expanding-panel" :class="[elementClasses, { 'content-is-on-top': contentIsOnTop }]">
2
+ <div ref="rootEl" class="expanding-panel" :class="[elementClasses, { 'content-is-on-top': contentIsOnTop }]">
3
3
  <details class="expanding-panel-details" :name :open @toggle="onDetailsToggle">
4
4
  <summary
5
5
  :id="`id-${name}-trigger`"
@@ -32,6 +32,8 @@
32
32
  </template>
33
33
 
34
34
  <script setup lang="ts">
35
+ import { onClickOutside } from "@vueuse/core";
36
+
35
37
  interface Props {
36
38
  name?: string;
37
39
  animationDuration?: number;
@@ -56,6 +58,17 @@ const open = computed(() => props.forceOpened || isPanelOpen.value);
56
58
 
57
59
  const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
58
60
 
61
+ // contentIsOnTop panels behave like a dropdown/overlay (e.g. ContentDocs' mobile nav) rather
62
+ // than an inline accordion — clicking outside is the expected way to dismiss those, matching
63
+ // native <select>/menu behaviour. Not wanted for ordinary accordions (forceOpened is always
64
+ // false here too — a forceOpened panel isn't meant to be dismissible by any interaction).
65
+ const rootEl = ref<HTMLElement | null>(null);
66
+ onClickOutside(rootEl, () => {
67
+ if (props.contentIsOnTop && !props.forceOpened && isPanelOpen.value) {
68
+ isPanelOpen.value = false;
69
+ }
70
+ });
71
+
59
72
  if (import.meta.dev) {
60
73
  watch(
61
74
  () => props.contentIsOnTop,
@@ -97,10 +110,9 @@ const onDetailsToggle = (event: Event) => {
97
110
  cursor: pointer;
98
111
  }
99
112
  .expanding-panel-summary {
100
- display: flex;
113
+ display: grid;
101
114
  align-items: center;
102
- justify-content: space-between;
103
- flex-direction: row;
115
+ grid-template-columns: 1fr auto;
104
116
  gap: var(--expanding-panel-summary-gap, 1rem);
105
117
  list-style: none;
106
118
  user-select: none;
@@ -427,6 +427,53 @@ describe("ExpandingPanel", () => {
427
427
  expect(vm.isPanelOpen).toBe(true);
428
428
  });
429
429
 
430
+ it("closes an open contentIsOnTop panel on outside click", async () => {
431
+ const wrapper = await mountSuspended(ExpandingPanel, {
432
+ props: { name: "content-on-top-outside-click", contentIsOnTop: true },
433
+ attachTo: document.body,
434
+ });
435
+ const vm = wrapper.vm as unknown as ExpandingPanelInstance;
436
+
437
+ await wrapper.find("summary").trigger("click");
438
+ expect(vm.isPanelOpen).toBe(true);
439
+
440
+ document.body.dispatchEvent(new MouseEvent("click", { bubbles: true }));
441
+ await nextTick();
442
+
443
+ expect(vm.isPanelOpen).toBe(false);
444
+ wrapper.unmount();
445
+ });
446
+
447
+ it("does not close on outside click when contentIsOnTop is false (ordinary accordion)", async () => {
448
+ const wrapper = await mountSuspended(ExpandingPanel, {
449
+ props: { name: "content-on-top-outside-click-inline" },
450
+ attachTo: document.body,
451
+ });
452
+ const vm = wrapper.vm as unknown as ExpandingPanelInstance;
453
+
454
+ await wrapper.find("summary").trigger("click");
455
+ expect(vm.isPanelOpen).toBe(true);
456
+
457
+ document.body.dispatchEvent(new MouseEvent("click", { bubbles: true }));
458
+ await nextTick();
459
+
460
+ expect(vm.isPanelOpen).toBe(true);
461
+ wrapper.unmount();
462
+ });
463
+
464
+ it("does not close a forceOpened contentIsOnTop panel on outside click", async () => {
465
+ const wrapper = await mountSuspended(ExpandingPanel, {
466
+ props: { name: "content-on-top-outside-click-forced", contentIsOnTop: true, forceOpened: true },
467
+ attachTo: document.body,
468
+ });
469
+
470
+ document.body.dispatchEvent(new MouseEvent("click", { bubbles: true }));
471
+ await nextTick();
472
+
473
+ expect(wrapper.find("details").attributes("open")).toBeDefined();
474
+ wrapper.unmount();
475
+ });
476
+
430
477
  // ─── name prop / useId fallback ───────────────────────────────────────────
431
478
 
432
479
  it("uses the provided name in ARIA attributes", async () => {
@@ -141,6 +141,9 @@ watch(
141
141
  top: -0.6rem;
142
142
  left: 50%;
143
143
  transform: translateX(-50%);
144
+ /* Above .pricing-card__ribbon-clip (z-index: 1) — without this the badge has no stacking
145
+ context of its own and the ribbon, which does, paints over it regardless of DOM order. */
146
+ z-index: 2;
144
147
 
145
148
  display: inline-block;
146
149
  padding: 0.4rem 1rem;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.2.8",
4
+ "version": "9.2.10",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",
@@ -1,48 +0,0 @@
1
- # Release Notes
2
-
3
- ## Overview
4
-
5
- When asked to create release notes, always produce a fenced markdown code block (` ```markdown `) so the content can be copied and pasted directly into a git tag, GitHub release, or changelog.
6
-
7
- ## Steps
8
-
9
- ### 1. Determine the version
10
-
11
- Run `git log --oneline -8` to find the most recent release commit and the commits since the previous release.
12
-
13
- ### 2. Review the diff
14
-
15
- Run `git show <commit> --stat` for each commit since the last release to understand what changed.
16
-
17
- ### 3. Produce a fenced markdown block
18
-
19
- Always wrap the output in a ` ```markdown ` code fence — never render it as plain markdown. This ensures the user can copy the raw text without formatting being stripped.
20
-
21
- ## Format
22
-
23
- ```markdown
24
- ## vX.Y.Z
25
-
26
- ### New
27
-
28
- - **`ComponentOrComposableName`** — one-line description of what it does and why it exists
29
-
30
- ### Fixed
31
-
32
- - Short description of what was wrong and what was corrected
33
-
34
- ### Changed
35
-
36
- - Short description of intentional behaviour or API changes
37
-
38
- ### Documentation
39
-
40
- - **`skill-name` skill** — new/updated: what it covers
41
- ```
42
-
43
- ## Notes
44
-
45
- - Only include sections that have content — omit empty headings
46
- - Keep each bullet to one line where possible
47
- - Lead with the most user-facing changes (New, Fixed) before internal ones (Documentation, Tests)
48
- - Test additions warrant their own section only when substantial; otherwise fold into the relevant New/Fixed bullet