@citizenplane/pimp 16.0.3 → 16.2.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 (71) hide show
  1. package/dist/pimp.es.js +781 -757
  2. package/dist/pimp.umd.js +21 -21
  3. package/dist/style.css +1 -1
  4. package/package.json +10 -8
  5. package/src/components/CpDate.vue +3 -1
  6. package/src/components/CpHeading.vue +4 -5
  7. package/src/components/CpMultiselect.vue +2 -5
  8. package/src/components/CpTable.vue +4 -2
  9. package/src/components/CpTelInput.vue +18 -12
  10. package/src/components/CpText.vue +141 -0
  11. package/src/components/CpToast.vue +1 -1
  12. package/src/components/CpTransitionExpand.vue +23 -20
  13. package/src/components/index.ts +2 -0
  14. package/src/libs/CoreDatepicker.vue +1 -0
  15. package/src/stories/BaseInputLabel.stories.ts +36 -9
  16. package/src/stories/Colors.mdx +9 -0
  17. package/src/stories/Colors.stories.ts +177 -0
  18. package/src/stories/CpAccordion.stories.ts +188 -159
  19. package/src/stories/CpAccordionGroup.stories.ts +51 -95
  20. package/src/stories/CpAirlineLogo.stories.ts +52 -28
  21. package/src/stories/CpAlert.stories.ts +196 -159
  22. package/src/stories/CpBadge.stories.ts +260 -194
  23. package/src/stories/CpButton.stories.ts +257 -426
  24. package/src/stories/CpCheckbox.stories.ts +102 -30
  25. package/src/stories/CpContextualMenu.stories.ts +14 -9
  26. package/src/stories/CpDate.stories.ts +53 -26
  27. package/src/stories/CpDatepicker.stories.ts +53 -80
  28. package/src/stories/CpDialog.stories.ts +23 -2
  29. package/src/stories/CpHeading.stories.ts +60 -20
  30. package/src/stories/CpIcon.stories.ts +98 -31
  31. package/src/stories/CpInput.stories.ts +164 -73
  32. package/src/stories/CpItemActions.stories.ts +23 -12
  33. package/src/stories/CpLoader.stories.ts +55 -7
  34. package/src/stories/CpMenuItem.stories.ts +53 -27
  35. package/src/stories/CpMultiselect.stories.ts +53 -72
  36. package/src/stories/CpPartnerBadge.stories.ts +58 -76
  37. package/src/stories/CpRadio.stories.ts +45 -49
  38. package/src/stories/CpRadioGroup.stories.ts +47 -40
  39. package/src/stories/CpSelect.stories.ts +108 -34
  40. package/src/stories/CpSelectMenu.stories.ts +66 -55
  41. package/src/stories/CpSelectableButton.stories.ts +170 -81
  42. package/src/stories/CpSwitch.stories.ts +136 -134
  43. package/src/stories/CpTable.stories.ts +69 -13
  44. package/src/stories/CpTableEmptyState.stories.ts +11 -7
  45. package/src/stories/CpTabs.stories.ts +23 -5
  46. package/src/stories/CpTelInput.stories.ts +28 -19
  47. package/src/stories/CpText.stories.ts +131 -0
  48. package/src/stories/CpTextarea.stories.ts +74 -27
  49. package/src/stories/CpToast.stories.ts +75 -109
  50. package/src/stories/CpTooltip.stories.ts +82 -77
  51. package/src/stories/CpTransitionCounter.stories.ts +5 -1
  52. package/src/stories/CpTransitionExpand.stories.ts +12 -7
  53. package/src/stories/CpTransitionListItems.stories.ts +6 -1
  54. package/src/stories/CpTransitionSize.stories.ts +9 -1
  55. package/src/stories/CpTransitionSlide.stories.ts +5 -1
  56. package/src/stories/CpTransitionTabContent.stories.ts +5 -1
  57. package/src/stories/Dimensions.mdx +9 -0
  58. package/src/stories/Dimensions.stories.ts +119 -0
  59. package/src/stories/Easings.mdx +9 -0
  60. package/src/stories/Easings.stories.ts +101 -0
  61. package/src/stories/FocusRings.mdx +9 -0
  62. package/src/stories/FocusRings.stories.ts +74 -0
  63. package/src/stories/Shadows.mdx +9 -0
  64. package/src/stories/Shadows.stories.ts +100 -0
  65. package/src/stories/Typography.mdx +9 -0
  66. package/src/stories/Typography.stories.ts +181 -0
  67. package/src/stories/documentationStyles.ts +2 -10
  68. package/src/stories/tokenUtils.ts +259 -0
  69. package/src/types/primevue-toasteventbus.d.ts +14 -0
  70. package/tsconfig.json +1 -0
  71. package/.lintstagedrc.json +0 -4
@@ -0,0 +1,259 @@
1
+ import { ref } from 'vue'
2
+
3
+ export type Token = { name: string; value: string }
4
+
5
+ export async function copyText(text: string): Promise<boolean> {
6
+ if (typeof navigator === 'undefined' || typeof window === 'undefined') return false
7
+ try {
8
+ if (navigator.clipboard?.writeText) {
9
+ await navigator.clipboard.writeText(text)
10
+ return true
11
+ }
12
+ } catch {
13
+ // fall through to fallback
14
+ }
15
+ try {
16
+ const textarea = document.createElement('textarea')
17
+ textarea.value = text
18
+ textarea.setAttribute('readonly', '')
19
+ textarea.style.position = 'fixed'
20
+ textarea.style.opacity = '0'
21
+ document.body.appendChild(textarea)
22
+ textarea.select()
23
+ const ok = document.execCommand('copy')
24
+ document.body.removeChild(textarea)
25
+ return ok
26
+ } catch {
27
+ return false
28
+ }
29
+ }
30
+
31
+ export function useCopier() {
32
+ const copiedKey = ref<null | string>(null)
33
+ let timer: null | ReturnType<typeof setTimeout> = null
34
+
35
+ const copy = async (key: string, text: string = key) => {
36
+ const ok = await copyText(text)
37
+ if (!ok) return
38
+ if (timer) clearTimeout(timer)
39
+ copiedKey.value = key
40
+ timer = setTimeout(() => {
41
+ copiedKey.value = null
42
+ }, 1200)
43
+ }
44
+
45
+ return { copiedKey, copy }
46
+ }
47
+
48
+ type MatcherFn = (name: string) => boolean
49
+ type Matcher = string | RegExp | MatcherFn
50
+
51
+ const toMatcherFn = (m: Matcher): MatcherFn => {
52
+ if (typeof m === 'function') return m
53
+ if (m instanceof RegExp) return (name: string) => m.test(name)
54
+ return (name: string) => name.startsWith(m)
55
+ }
56
+
57
+ export function readTokens(include: Matcher[], exclude: Matcher[] = []): Token[] {
58
+ if (typeof window === 'undefined' || typeof document === 'undefined') return []
59
+ const styles = getComputedStyle(document.documentElement)
60
+ const includeFns = include.map(toMatcherFn)
61
+ const excludeFns = exclude.map(toMatcherFn)
62
+ const out: Token[] = []
63
+ for (let i = 0; i < styles.length; i++) {
64
+ const name = styles[i]
65
+ if (!name.startsWith('--cp-')) continue
66
+ if (!includeFns.some((fn) => fn(name))) continue
67
+ if (excludeFns.some((fn) => fn(name))) continue
68
+ out.push({ name, value: styles.getPropertyValue(name).trim() })
69
+ }
70
+ return out.sort((a, b) => a.name.localeCompare(b.name))
71
+ }
72
+
73
+ export type ProbeProperty = 'letter-spacing' | 'line-height' | 'width'
74
+
75
+ /**
76
+ * Apply a token as the given CSS property on a hidden probe element and read
77
+ * back the computed value. Defaults to `width`, which is the right choice for
78
+ * length tokens (rem/px). Use `line-height` or `letter-spacing` to resolve
79
+ * values that depend on the current font size.
80
+ */
81
+ export function getTokenPixels(tokenName: string, property: ProbeProperty = 'width'): null | string {
82
+ if (typeof window === 'undefined' || typeof document === 'undefined') return null
83
+ const probe = document.createElement('div')
84
+ probe.style.position = 'absolute'
85
+ probe.style.visibility = 'hidden'
86
+ probe.style.pointerEvents = 'none'
87
+ probe.style.fontSize = '16px'
88
+ if (property === 'width') {
89
+ probe.style.width = `var(${tokenName})`
90
+ } else if (property === 'line-height') {
91
+ probe.style.lineHeight = `var(${tokenName})`
92
+ probe.textContent = 'M'
93
+ } else {
94
+ probe.style.letterSpacing = `var(${tokenName})`
95
+ }
96
+ document.body.appendChild(probe)
97
+ const style = getComputedStyle(probe)
98
+ let computed: string
99
+ if (property === 'width') computed = style.width
100
+ else if (property === 'line-height') computed = style.lineHeight
101
+ else computed = style.letterSpacing
102
+ document.body.removeChild(probe)
103
+ if (!computed || computed === 'auto' || computed === 'normal') return null
104
+ return computed
105
+ }
106
+
107
+ const tshirtRank: Record<string, number> = {
108
+ none: -1,
109
+ '3xs': 0,
110
+ '2xs': 1,
111
+ xs: 2,
112
+ sm: 3,
113
+ 'sm-md': 3.5,
114
+ md: 4,
115
+ 'md-lg': 4.5,
116
+ lg: 5,
117
+ xl: 6,
118
+ '2xl': 7,
119
+ '3xl': 8,
120
+ '4xl': 9,
121
+ '5xl': 10,
122
+ '6xl': 11,
123
+ '7xl': 12,
124
+ '8xl': 13,
125
+ '9xl': 14,
126
+ full: 9999,
127
+ }
128
+
129
+ function extractSizeRank(name: string): null | number {
130
+ const parts = name.replace(/^--cp-/, '').split('-')
131
+ for (let i = parts.length - 1; i >= 0; i--) {
132
+ const one = parts[i]
133
+ const two = i > 0 ? `${parts[i - 1]}-${one}` : null
134
+ if (two && two in tshirtRank) return tshirtRank[two]
135
+ if (one in tshirtRank) return tshirtRank[one]
136
+ }
137
+ return null
138
+ }
139
+
140
+ function parsePxNumber(px: null | string): null | number {
141
+ if (!px) return null
142
+ const m = px.match(/^(-?\d+(?:\.\d+)?)px$/)
143
+ return m ? parseFloat(m[1]) : null
144
+ }
145
+
146
+ export function sortTokensBySize(tokens: Token[], direction: 'asc' | 'desc' = 'desc'): Token[] {
147
+ const factor = direction === 'desc' ? -1 : 1
148
+ return [...tokens].sort((a, b) => {
149
+ const rankA = extractSizeRank(a.name)
150
+ const rankB = extractSizeRank(b.name)
151
+ if (rankA !== null && rankB !== null && rankA !== rankB) {
152
+ return factor * (rankA - rankB)
153
+ }
154
+ if (rankA !== null && rankB === null) return -1
155
+ if (rankA === null && rankB !== null) return 1
156
+ const pxA = parsePxNumber(getTokenPixels(a.name))
157
+ const pxB = parsePxNumber(getTokenPixels(b.name))
158
+ if (pxA !== null && pxB !== null && pxA !== pxB) {
159
+ return factor * (pxA - pxB)
160
+ }
161
+ return a.name.localeCompare(b.name)
162
+ })
163
+ }
164
+
165
+ export const copyableClass = 'cp-copyable'
166
+
167
+ export const copyableCopiedClass = 'cp-copyable--copied'
168
+
169
+ export const tokenTableClass = 'cp-token-table'
170
+
171
+ /**
172
+ * Split a token value into a rem part and a px part. When the raw value is
173
+ * expressed in rem, both columns are filled (value in rem + computed px).
174
+ * Otherwise the value goes into whichever column matches its unit.
175
+ */
176
+ export function splitRemPx(token: Token): { px: string; rem: string } {
177
+ const value = token.value.trim()
178
+ const resolvedPx = getTokenPixels(token.name) ?? ''
179
+ const dash = '—'
180
+ const isRem = /^-?\d*\.?\d+rem$/.test(value)
181
+ if (isRem) {
182
+ return { rem: value, px: resolvedPx || dash }
183
+ }
184
+ const isPx = /^-?\d*\.?\d+px$/.test(value)
185
+ if (isPx) {
186
+ return { rem: dash, px: value }
187
+ }
188
+ return { rem: dash, px: resolvedPx || dash }
189
+ }
190
+
191
+ function ensureFoundationStyles(): void {
192
+ if (typeof document === 'undefined') return
193
+ const id = 'cp-foundation-styles'
194
+ if (document.getElementById(id)) return
195
+ const style = document.createElement('style')
196
+ style.id = id
197
+ style.textContent = `
198
+ .${copyableClass} {
199
+ cursor: pointer;
200
+ border-radius: 4px;
201
+ padding: 2px 6px;
202
+ margin: -2px -6px;
203
+ transition: background-color 0.15s ease, color 0.15s ease;
204
+ position: relative;
205
+ }
206
+ .${copyableClass}:hover {
207
+ background-color: #eef0ff;
208
+ }
209
+ .${copyableClass}:focus-visible {
210
+ outline: 2px solid #6366f1;
211
+ outline-offset: 2px;
212
+ }
213
+ .${copyableClass}.${copyableCopiedClass} {
214
+ background-color: #dcfce7;
215
+ color: #065f46;
216
+ }
217
+ .${tokenTableClass} {
218
+ width: 100%;
219
+ border-collapse: collapse;
220
+ text-align: left;
221
+ }
222
+ .${tokenTableClass} thead th {
223
+ font-family: ui-sans-serif, system-ui, sans-serif;
224
+ font-size: 11px;
225
+ font-weight: 600;
226
+ text-transform: uppercase;
227
+ letter-spacing: 0.04em;
228
+ color: #6b7280;
229
+ padding: 10px 12px;
230
+ border-bottom: 1px solid #e5e7eb;
231
+ background: #f9fafb;
232
+ }
233
+ .${tokenTableClass} tbody td {
234
+ padding: 10px 12px;
235
+ border-bottom: 1px solid #f1f2f6;
236
+ vertical-align: middle;
237
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
238
+ font-size: 12px;
239
+ color: #6b7280;
240
+ word-break: break-all;
241
+ white-space: nowrap;
242
+ }
243
+ .${tokenTableClass} tbody td:first-child {
244
+ color: #111;
245
+ }
246
+ .${tokenTableClass} tbody tr:last-child td {
247
+ border-bottom: none;
248
+ }
249
+ .${tokenTableClass} tbody td.cp-token-table__preview {
250
+ width: 1%;
251
+ white-space: nowrap;
252
+ }
253
+ `
254
+ document.head.appendChild(style)
255
+ }
256
+
257
+ if (typeof document !== 'undefined') {
258
+ ensureFoundationStyles()
259
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * PrimeVue ships `primevue/toasteventbus` as ESM without bundled `.d.ts`.
3
+ * This minimal declaration satisfies `vue-tsc` for consumers (e.g. `CpToast.vue`).
4
+ */
5
+ declare module 'primevue/toasteventbus' {
6
+ type ToastEventHandler = (payload: unknown) => void
7
+
8
+ interface ToastEventBus {
9
+ on(event: string, handler: ToastEventHandler): void
10
+ }
11
+
12
+ const ToastEventBus: ToastEventBus
13
+ export default ToastEventBus
14
+ }
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "skipLibCheck": true,
3
4
  "isolatedModules": true,
4
5
  "moduleResolution": "bundler",
5
6
  "module": "esnext",
@@ -1,4 +0,0 @@
1
- {
2
- "*.{ts,js,vue}": "npm run lint",
3
- "*": "npm run format"
4
- }