spoko-design-system 1.36.0 → 1.37.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [1.37.0](https://github.com/polo-blue/sds/compare/v1.36.1...v1.37.0) (2026-03-26)
2
+
3
+ ### Features
4
+
5
+ * add updateTooltipContent export and migrate ButtonCopy to SDS tooltip ([#414](https://github.com/polo-blue/sds/issues/414)) ([d6d7f28](https://github.com/polo-blue/sds/commit/d6d7f289c4d8a1ce28b7b00df9dad8d45612f20a))
6
+
7
+ ## [1.36.1](https://github.com/polo-blue/sds/compare/v1.36.0...v1.36.1) (2026-03-24)
8
+
1
9
  ## [1.36.0](https://github.com/polo-blue/sds/compare/v1.35.1...v1.36.0) (2026-03-24)
2
10
 
3
11
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoko-design-system",
3
- "version": "1.36.0",
3
+ "version": "1.37.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./index.ts",
@@ -1,14 +1,13 @@
1
1
  ---
2
2
  interface Props {
3
3
  productNumber: string;
4
- tooltipClasses?: string;
5
4
  texts: {
6
5
  copy: string;
7
6
  copied: string;
8
7
  };
9
8
  }
10
9
 
11
- const { productNumber, tooltipClasses = '', texts } = Astro.props;
10
+ const { productNumber, texts } = Astro.props;
12
11
 
13
12
  // SVG icon as data URL for mask
14
13
  const COPY_ICON = `url("data:image/svg+xml;utf8,${encodeURIComponent(
@@ -18,33 +17,32 @@ const COPY_ICON = `url("data:image/svg+xml;utf8,${encodeURIComponent(
18
17
 
19
18
  <button
20
19
  aria-label={texts.copy}
21
- class="btn-copy has-tooltip"
20
+ class="btn-copy"
22
21
  data-copy-text={productNumber}
23
22
  data-category={Astro.url.pathname.split('/')[1] || ''}
23
+ data-sds-tooltip={texts.copy}
24
+ data-copy-original-tooltip={texts.copy}
25
+ data-copy-copied-tooltip={texts.copied}
24
26
  >
25
- <span
26
- class:list={['tooltip rounded-full btn-copy-text', tooltipClasses]}
27
- data-text={texts.copy}
28
- data-copied-text={texts.copied}></span>
29
27
  <span class="copy-icon" role="img" aria-hidden="true"></span>
30
28
  </button>
31
29
 
32
30
  <script>
31
+ import { updateTooltipContent } from '../scripts/tooltips';
32
+
33
33
  class ClipboardButton {
34
34
  private static readonly COPY_TIMEOUT = 2000;
35
35
  private static readonly ANALYTICS_EVENT = 'product_code_copy';
36
36
  private readonly originalText: string;
37
+ private readonly copiedText: string;
37
38
  private resetTimer: ReturnType<typeof setTimeout> | null = null;
38
39
 
39
40
  constructor(private button: HTMLButtonElement) {
40
- this.originalText = this.tooltip?.dataset.text || 'Copy';
41
+ this.originalText = button.dataset.copyOriginalTooltip || 'Copy';
42
+ this.copiedText = button.dataset.copyCopiedTooltip || 'Copied';
41
43
  this.button.addEventListener('click', () => this.handleCopy());
42
44
  }
43
45
 
44
- private get tooltip(): HTMLSpanElement {
45
- return this.button.querySelector('.tooltip') as HTMLSpanElement;
46
- }
47
-
48
46
  private get copyText(): string {
49
47
  return this.button.dataset.copyText || '';
50
48
  }
@@ -53,10 +51,6 @@ const COPY_ICON = `url("data:image/svg+xml;utf8,${encodeURIComponent(
53
51
  return this.button.dataset.category || '';
54
52
  }
55
53
 
56
- private get copiedText(): string {
57
- return this.tooltip.dataset.copiedText || 'Copied';
58
- }
59
-
60
54
  private async handleCopy(): Promise<void> {
61
55
  try {
62
56
  await this.copyToClipboard();
@@ -89,9 +83,11 @@ const COPY_ICON = `url("data:image/svg+xml;utf8,${encodeURIComponent(
89
83
 
90
84
  private updateTooltip(): void {
91
85
  if (this.resetTimer) clearTimeout(this.resetTimer);
92
- this.tooltip.dataset.text = this.copiedText;
86
+ updateTooltipContent(this.button, this.copiedText);
87
+ this.button.setAttribute('aria-label', this.copiedText);
93
88
  this.resetTimer = setTimeout(() => {
94
- this.tooltip.dataset.text = this.originalText;
89
+ updateTooltipContent(this.button, this.originalText);
90
+ this.button.setAttribute('aria-label', this.originalText);
95
91
  this.resetTimer = null;
96
92
  }, ClipboardButton.COPY_TIMEOUT);
97
93
  }
@@ -151,18 +147,6 @@ const COPY_ICON = `url("data:image/svg+xml;utf8,${encodeURIComponent(
151
147
  }
152
148
  }
153
149
 
154
- .tooltip {
155
- @apply invisible absolute -top-8 left-1/2 -translate-x-1/2 bg-neutral px-2 py-1 text-xs text-white opacity-0 transition-opacity;
156
- }
157
-
158
- .tooltip::before {
159
- content: attr(data-text);
160
- }
161
-
162
- .has-tooltip:hover .tooltip {
163
- @apply visible opacity-100;
164
- }
165
-
166
150
  .copy-icon {
167
151
  width: 1.2em;
168
152
  height: 1.2em;
@@ -16,7 +16,7 @@ import '../styles/sds-tooltip.css';
16
16
 
17
17
  const SELECTOR = '[data-sds-tooltip]';
18
18
  const OFFSET = 8;
19
- const ARROW_SIZE = 8;
19
+ const ARROW_SIZE = 13;
20
20
  const SHIFT_PADDING = 5;
21
21
  const SHOW_DELAY = 80;
22
22
  const HIDE_DELAY = 60;
@@ -144,6 +144,17 @@ export function showTooltip(target: HTMLElement) {
144
144
  });
145
145
  }
146
146
 
147
+ /**
148
+ * Update tooltip content for a target element.
149
+ * If the tooltip is currently visible for this target, updates the displayed content immediately.
150
+ */
151
+ export function updateTooltipContent(target: HTMLElement, html: string) {
152
+ target.setAttribute('data-sds-tooltip', html);
153
+ if (currentTarget === target && contentEl) {
154
+ contentEl.innerHTML = html;
155
+ }
156
+ }
157
+
147
158
  export function hideTooltip() {
148
159
  if (!tooltipEl || !currentTarget) return;
149
160
 
@@ -15,12 +15,12 @@
15
15
  color: #1e293b; /* slate-darkest */
16
16
  font-size: 0.75rem;
17
17
  line-height: 1.5;
18
- border-radius: 0.5rem;
18
+ border-radius: 2px;
19
19
  box-shadow:
20
- 0 10px 15px -3px rgb(0 0 0 / 0.1),
21
- 0 4px 6px -4px rgb(0 0 0 / 0.1);
20
+ 0 30px 90px -20px rgba(0, 0, 0, 0.3),
21
+ 0 0 0 1px #eaecf0;
22
22
  max-width: 280px;
23
- border: 1px solid #e5e7eb; /* neutral-lighter */
23
+ margin: 3px 0;
24
24
  pointer-events: none;
25
25
  opacity: 0;
26
26
  transition: opacity 0.15s ease-out;
@@ -42,10 +42,10 @@
42
42
  /* Arrow */
43
43
  .sds-tooltip-arrow {
44
44
  position: absolute;
45
- width: 8px;
46
- height: 8px;
47
- background-color: #f3f4f6; /* neutral-lightest */
48
- border: 1px solid #e5e7eb; /* neutral-lighter */
45
+ width: 13px;
46
+ height: 13px;
47
+ background-color: inherit;
48
+ border: 1px solid #eaecf0;
49
49
  transform: rotate(45deg);
50
50
  }
51
51
 
@@ -74,7 +74,7 @@
74
74
  padding: 0.5rem 0.75rem;
75
75
  color: #1e293b; /* slate-darkest */
76
76
  overflow: hidden;
77
- border-radius: 0.5rem;
77
+ border-radius: 2px;
78
78
  }
79
79
 
80
80
  /* Remove padding for structured tooltips (with tooltip-header) */
@@ -94,7 +94,7 @@
94
94
  gap: 0.5rem;
95
95
  padding: 0.375rem 0.5rem;
96
96
  background-color: #001e50;
97
- border-radius: 0.5rem 0.5rem 0 0;
97
+ border-radius: 2px 2px 0 0;
98
98
  color: white;
99
99
  }
100
100