@purpurds/popover 0.0.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 (72) hide show
  1. package/dist/LICENSE.txt +905 -0
  2. package/dist/metadata.js +8 -0
  3. package/dist/popover-back.d.ts +9 -0
  4. package/dist/popover-back.d.ts.map +1 -0
  5. package/dist/popover-button.d.ts +37 -0
  6. package/dist/popover-button.d.ts.map +1 -0
  7. package/dist/popover-content.d.ts +93 -0
  8. package/dist/popover-content.d.ts.map +1 -0
  9. package/dist/popover-flow.d.ts +65 -0
  10. package/dist/popover-flow.d.ts.map +1 -0
  11. package/dist/popover-footer.d.ts +16 -0
  12. package/dist/popover-footer.d.ts.map +1 -0
  13. package/dist/popover-header.d.ts +7 -0
  14. package/dist/popover-header.d.ts.map +1 -0
  15. package/dist/popover-internal-context.d.ts +15 -0
  16. package/dist/popover-internal-context.d.ts.map +1 -0
  17. package/dist/popover-next.d.ts +9 -0
  18. package/dist/popover-next.d.ts.map +1 -0
  19. package/dist/popover-standalone.d.ts +12 -0
  20. package/dist/popover-standalone.d.ts.map +1 -0
  21. package/dist/popover-steps.d.ts +6 -0
  22. package/dist/popover-steps.d.ts.map +1 -0
  23. package/dist/popover-trigger.d.ts +27 -0
  24. package/dist/popover-trigger.d.ts.map +1 -0
  25. package/dist/popover-walkthrough.d.ts +13 -0
  26. package/dist/popover-walkthrough.d.ts.map +1 -0
  27. package/dist/popover.cjs.js +42 -0
  28. package/dist/popover.cjs.js.map +1 -0
  29. package/dist/popover.d.ts +36 -0
  30. package/dist/popover.d.ts.map +1 -0
  31. package/dist/popover.es.js +3849 -0
  32. package/dist/popover.es.js.map +1 -0
  33. package/dist/styles.css +1 -0
  34. package/dist/use-screen-size.hook.d.ts +7 -0
  35. package/dist/use-screen-size.hook.d.ts.map +1 -0
  36. package/dist/use-smooth-scroll.d.ts +5 -0
  37. package/dist/use-smooth-scroll.d.ts.map +1 -0
  38. package/dist/usePopoverTrigger.d.ts +5 -0
  39. package/dist/usePopoverTrigger.d.ts.map +1 -0
  40. package/dist/usePopoverWalkthrough.d.ts +7 -0
  41. package/dist/usePopoverWalkthrough.d.ts.map +1 -0
  42. package/eslint.config.mjs +2 -0
  43. package/package.json +82 -0
  44. package/src/global.d.ts +4 -0
  45. package/src/popover-back.test.tsx +63 -0
  46. package/src/popover-back.tsx +40 -0
  47. package/src/popover-button.test.tsx +51 -0
  48. package/src/popover-button.tsx +84 -0
  49. package/src/popover-content.test.tsx +1122 -0
  50. package/src/popover-content.tsx +277 -0
  51. package/src/popover-flow.tsx +170 -0
  52. package/src/popover-footer.test.tsx +21 -0
  53. package/src/popover-footer.tsx +32 -0
  54. package/src/popover-header.test.tsx +22 -0
  55. package/src/popover-header.tsx +32 -0
  56. package/src/popover-internal-context.tsx +28 -0
  57. package/src/popover-next.test.tsx +61 -0
  58. package/src/popover-next.tsx +40 -0
  59. package/src/popover-standalone.tsx +48 -0
  60. package/src/popover-steps.tsx +32 -0
  61. package/src/popover-trigger.tsx +71 -0
  62. package/src/popover-walkthrough.test.tsx +346 -0
  63. package/src/popover-walkthrough.tsx +45 -0
  64. package/src/popover.module.scss +315 -0
  65. package/src/popover.stories.tsx +1157 -0
  66. package/src/popover.test.tsx +642 -0
  67. package/src/popover.tsx +76 -0
  68. package/src/use-screen-size.hook.ts +39 -0
  69. package/src/use-smooth-scroll.ts +62 -0
  70. package/src/usePopoverTrigger.ts +59 -0
  71. package/src/usePopoverWalkthrough.ts +85 -0
  72. package/vitest.setup.ts +30 -0
@@ -0,0 +1,315 @@
1
+ @use "@purpurds/tokens/_index" as *;
2
+ @use "@purpurds/tokens/breakpoint/variables" as *;
3
+
4
+ $animation-settings: var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)
5
+ forwards;
6
+
7
+ .purpur-popover {
8
+ $root: &;
9
+
10
+ // Trigger states
11
+ &__trigger {
12
+ position: relative;
13
+ width: fit-content;
14
+
15
+ &-highlight {
16
+ position: absolute;
17
+ top: -2px;
18
+ left: -2px;
19
+ right: -2px;
20
+ bottom: -2px;
21
+ border-radius: var(--purpur-border-radius-sm);
22
+ pointer-events: none;
23
+
24
+ // State 1: Outer glow (continuously fades in/out)
25
+ &--state1 {
26
+ border: var(--purpur-border-radius-xs) solid
27
+ var(--purpur-color-border-interactive-expressive);
28
+ box-shadow: 0 0 0 4px rgba(153, 10, 227, 0.5);
29
+
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ animation: highlightState1 var(--purpur-motion-duration-1500) ease-out 3;
32
+ animation-fill-mode: forwards;
33
+ }
34
+
35
+ @media (prefers-reduced-motion: reduce) {
36
+ opacity: 0;
37
+ }
38
+
39
+ // Negative variant (light purple)
40
+ &.purpur-popover__trigger-highlight--negative {
41
+ border-color: var(--purpur-color-purple-200);
42
+ box-shadow: 0 0 0 4px rgba(228, 182, 251, 0.5);
43
+ }
44
+ }
45
+
46
+ // State 2: Inner border (always visible)
47
+ &--state2 {
48
+ border: var(--purpur-border-radius-xs) solid
49
+ var(--purpur-color-border-interactive-expressive);
50
+ opacity: 1;
51
+
52
+ @media (prefers-reduced-motion: reduce) {
53
+ opacity: 1;
54
+ }
55
+
56
+ // Negative variant (light purple)
57
+ &.purpur-popover__trigger-highlight--negative {
58
+ border-color: var(--purpur-color-purple-200);
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ &__inner {
65
+ display: flex;
66
+ flex-direction: column;
67
+ align-items: flex-start;
68
+ gap: var(--purpur-spacing-100);
69
+ align-self: stretch;
70
+ }
71
+
72
+ // Content container
73
+ &__content {
74
+ display: flex;
75
+ flex-direction: column;
76
+ align-items: flex-start;
77
+ gap: var(--purpur-spacing-300);
78
+ width: 288px; // Hardcoded to 288px
79
+ padding: var(--purpur-spacing-200);
80
+ border-radius: var(--purpur-border-radius-md);
81
+ box-shadow: var(--purpur-shadow-lg);
82
+ z-index: var(--popover-z-index);
83
+
84
+ @media (prefers-reduced-motion: no-preference) {
85
+ // Animate based on position (data-side from Radix)
86
+ &[data-state="open"] {
87
+ animation: fadeIn $animation-settings;
88
+
89
+ &[data-side="top"] {
90
+ animation: fadeIn $animation-settings, slideFromTop $animation-settings;
91
+ }
92
+ &[data-side="bottom"] {
93
+ animation: fadeIn $animation-settings, slideFromBottom $animation-settings;
94
+ }
95
+ &[data-side="left"] {
96
+ animation: fadeIn $animation-settings, slideFromLeft $animation-settings;
97
+ }
98
+ &[data-side="right"] {
99
+ animation: fadeIn $animation-settings, slideFromRight $animation-settings;
100
+ }
101
+ }
102
+
103
+ &[data-state="closed"] {
104
+ animation: fadeOut $animation-settings;
105
+
106
+ &[data-side="top"] {
107
+ animation: fadeOut $animation-settings, slideToTop $animation-settings;
108
+ }
109
+ &[data-side="bottom"] {
110
+ animation: fadeOut $animation-settings, slideToBottom $animation-settings;
111
+ }
112
+ &[data-side="left"] {
113
+ animation: fadeOut $animation-settings, slideToLeft $animation-settings;
114
+ }
115
+ &[data-side="right"] {
116
+ animation: fadeOut $animation-settings, slideToRight $animation-settings;
117
+ }
118
+ }
119
+ }
120
+
121
+ @media (min-width: $purpur-breakpoint-md) {
122
+ width: 400px; // Hardcoded to 400px
123
+ }
124
+
125
+ // Default theme colors
126
+ background: var(--purpur-color-background-tone-on-tone-primary);
127
+
128
+ #{$root}__arrow {
129
+ fill: var(--purpur-color-background-tone-on-tone-primary);
130
+ }
131
+
132
+ #{$root}__body,
133
+ #{$root}__icon,
134
+ #{$root}__steps,
135
+ #{$root}__title {
136
+ color: var(--purpur-color-text-tone-on-tone-primary);
137
+ }
138
+
139
+ // Negative theme
140
+ &--negative {
141
+ background: var(--purpur-color-background-tone-on-tone-secondary);
142
+
143
+ #{$root}__arrow {
144
+ fill: var(--purpur-color-background-tone-on-tone-secondary);
145
+ }
146
+
147
+ #{$root}__body,
148
+ #{$root}__icon,
149
+ #{$root}__steps,
150
+ #{$root}__title {
151
+ color: var(--purpur-color-text-tone-on-tone-secondary);
152
+ }
153
+ }
154
+ }
155
+
156
+ // Header
157
+ &__header-content {
158
+ display: flex;
159
+ align-items: flex-start;
160
+ gap: var(--purpur-spacing-100);
161
+ align-self: stretch;
162
+ padding-right: var(--purpur-spacing-300); // was 28px, 300 is 24px but closest token
163
+
164
+ #{$root}__icon {
165
+ display: flex;
166
+ padding-top: var(--purpur-spacing-25);
167
+ align-items: center;
168
+ gap: 8px;
169
+ }
170
+ }
171
+
172
+ // Close button - increased specificity to ensure position absolute is applied
173
+ &__content &__close {
174
+ position: absolute;
175
+ top: 6.26px;
176
+ right: 6.26px;
177
+ }
178
+
179
+ // Footer
180
+ &__footer {
181
+ display: flex;
182
+ text-align: center;
183
+ gap: var(--purpur-spacing-150);
184
+ align-self: stretch;
185
+ align-items: center;
186
+
187
+ flex-direction: column-reverse;
188
+
189
+ @media (min-width: $purpur-breakpoint-md) {
190
+ flex-direction: row;
191
+ }
192
+
193
+ &-button-group {
194
+ display: flex;
195
+ justify-content: flex-end;
196
+ align-items: center;
197
+ gap: var(--purpur-spacing-200);
198
+ flex: 1 0 0;
199
+ }
200
+ }
201
+
202
+ &__steps {
203
+ flex-shrink: 0;
204
+ color: var(--purpur-color-text-tone-on-tone-primary);
205
+ overflow: hidden;
206
+ text-overflow: ellipsis;
207
+ }
208
+ }
209
+
210
+ // Animations
211
+ @keyframes fadeIn {
212
+ from {
213
+ opacity: 0;
214
+ }
215
+ to {
216
+ opacity: 1;
217
+ }
218
+ }
219
+
220
+ @keyframes fadeOut {
221
+ from {
222
+ opacity: 1;
223
+ }
224
+ to {
225
+ opacity: 0;
226
+ }
227
+ }
228
+
229
+ // Slide from top (popover is above trigger, beak pointing down)
230
+ @keyframes slideFromTop {
231
+ from {
232
+ transform: translateY(-8px);
233
+ }
234
+ to {
235
+ transform: translateY(0);
236
+ }
237
+ }
238
+
239
+ @keyframes slideToTop {
240
+ from {
241
+ transform: translateY(0);
242
+ }
243
+ to {
244
+ transform: translateY(-8px);
245
+ }
246
+ }
247
+
248
+ // Slide from bottom (popover is below trigger, beak pointing up)
249
+ @keyframes slideFromBottom {
250
+ from {
251
+ transform: translateY(8px);
252
+ }
253
+ to {
254
+ transform: translateY(0);
255
+ }
256
+ }
257
+
258
+ @keyframes slideToBottom {
259
+ from {
260
+ transform: translateY(0);
261
+ }
262
+ to {
263
+ transform: translateY(8px);
264
+ }
265
+ }
266
+
267
+ // Slide from left (popover is to the left of trigger, beak pointing right)
268
+ @keyframes slideFromLeft {
269
+ from {
270
+ transform: translateX(-8px);
271
+ }
272
+ to {
273
+ transform: translateX(0);
274
+ }
275
+ }
276
+
277
+ @keyframes slideToLeft {
278
+ from {
279
+ transform: translateX(0);
280
+ }
281
+ to {
282
+ transform: translateX(-8px);
283
+ }
284
+ }
285
+
286
+ // Slide from right (popover is to the right of trigger, beak pointing left)
287
+ @keyframes slideFromRight {
288
+ from {
289
+ transform: translateX(8px);
290
+ }
291
+ to {
292
+ transform: translateX(0);
293
+ }
294
+ }
295
+
296
+ @keyframes slideToRight {
297
+ from {
298
+ transform: translateX(0);
299
+ }
300
+ to {
301
+ transform: translateX(8px);
302
+ }
303
+ }
304
+
305
+ @keyframes highlightState1 {
306
+ 0% {
307
+ opacity: 0;
308
+ }
309
+ 50% {
310
+ opacity: 1;
311
+ }
312
+ 100% {
313
+ opacity: 0;
314
+ }
315
+ }