spoko-design-system 1.3.7 → 1.4.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.
@@ -1,73 +1,182 @@
1
1
  <script lang="ts" setup>
2
- import { ref } from 'vue';
2
+ import { ref, onMounted } from 'vue';
3
3
 
4
4
  const props = defineProps({
5
+ // Primary state (visible by default)
6
+ message: {
7
+ type: String,
8
+ default: 'We stand with our friends and colleagues in Ukraine. To support Ukraine in their time of need visit',
9
+ },
10
+ linkText: {
11
+ type: String,
12
+ default: 'this page',
13
+ },
14
+ linkUrl: {
15
+ type: String,
16
+ default: 'https://polo.blue/support-ukraine/',
17
+ },
18
+ linkTitle: {
19
+ type: String,
20
+ default: '',
21
+ },
22
+
23
+ // Toggle state (shows when close button is clicked)
24
+ toggleMessage: {
25
+ type: String,
26
+ default: '',
27
+ },
28
+ toggleBgClass: {
29
+ type: String,
30
+ default: 'bg-black',
31
+ },
32
+ toggleTextClass: {
33
+ type: String,
34
+ default: 'text-white',
35
+ },
36
+
37
+ // Close button
5
38
  showCloseButton: {
6
39
  type: Boolean,
7
40
  default: true,
8
- required: false,
41
+ },
42
+ closeButtonAriaLabel: {
43
+ type: String,
44
+ default: 'Toggle',
45
+ },
46
+
47
+ // Show flag icon (Ukraine flag by default)
48
+ showIcon: {
49
+ type: Boolean,
50
+ default: false,
51
+ },
52
+ iconClass: {
53
+ type: String,
54
+ default: 'inline-block text-4xl w-6 h-3.5 min-w-[1.25rem] mr-3 bg-gradient-to-b stops-[#0057b7_50%,50%,#ffd700_100%]',
55
+ },
56
+
57
+ // LocalStorage persistence
58
+ persistClose: {
59
+ type: Boolean,
60
+ default: false,
61
+ },
62
+ storageKey: {
63
+ type: String,
64
+ default: 'slimbanner-closed',
65
+ },
66
+ // Expiration in days (0 = never expires)
67
+ expirationDays: {
68
+ type: Number,
69
+ default: 30,
9
70
  },
10
71
  });
72
+
11
73
  const isShow = ref(true);
74
+ const isHidden = ref(false);
75
+
76
+ // Check localStorage on mount
77
+ onMounted(() => {
78
+ if (props.persistClose && typeof window !== 'undefined') {
79
+ const stored = localStorage.getItem(props.storageKey);
80
+ if (stored) {
81
+ try {
82
+ const data = JSON.parse(stored);
83
+ const now = new Date().getTime();
84
+
85
+ // Check if expired
86
+ if (props.expirationDays > 0 && data.expires && now > data.expires) {
87
+ // Expired, remove from storage
88
+ localStorage.removeItem(props.storageKey);
89
+ } else if (data.closed) {
90
+ // Still valid, keep hidden
91
+ isHidden.value = true;
92
+ }
93
+ } catch (e) {
94
+ // Invalid data, remove it
95
+ localStorage.removeItem(props.storageKey);
96
+ }
97
+ }
98
+ }
99
+ });
100
+
12
101
  const toggleVisibility = () => {
13
102
  isShow.value = !isShow.value;
14
103
  };
104
+
105
+ const closePermanently = () => {
106
+ isHidden.value = true;
107
+
108
+ if (props.persistClose && typeof window !== 'undefined') {
109
+ const data = {
110
+ closed: true,
111
+ timestamp: new Date().getTime(),
112
+ expires: props.expirationDays > 0
113
+ ? new Date().getTime() + (props.expirationDays * 24 * 60 * 60 * 1000)
114
+ : null,
115
+ };
116
+ localStorage.setItem(props.storageKey, JSON.stringify(data));
117
+ }
118
+ };
15
119
  </script>
16
120
 
17
121
  <template>
18
- <div
19
- v-if="isShow"
20
- data-pagefind-ignore
21
- class="slimbanner"
22
- >
23
- <span
24
- class="inline-block text-4xl w-6 h-3.5 min-w-[1.25rem] mr-3 bg-gradient-to-b stops-[#0057b7_50%,50%,#ffd700_100%]"
25
- />
26
- <span class="leading-none"
27
- ><span
28
- data-text="We stand with our friends and colleagues in Ukraine. To support Ukraine in their time of need visit "
29
- />
30
- <a
31
- href="https://polo.blue/support-ukraine/"
32
- target="_blank"
33
- rel="noopener"
34
- title="Support Ukraine"
35
- class="underline underline-offset-2 hover:text-blue-wrc"
36
- >this page</a
37
- >.
38
- </span>
39
-
40
- <button
41
- v-if="props.showCloseButton"
42
- class="btn-close text-white"
43
- aria-label="Toggle"
44
- @click="toggleVisibility()"
122
+ <template v-if="!isHidden">
123
+ <div
124
+ v-if="isShow"
125
+ data-pagefind-ignore
126
+ class="slimbanner"
45
127
  >
46
- <span class="close close-dark" />
47
- </button>
48
- </div>
49
- <div
50
- v-else
51
- data-pagefind-ignore
52
- class="px-4 sm:px-8 py-3 flex items-center justify-center text-xs sm:text-base leading-none text-white relative bg-black drop-shadow-md z-2"
53
- >
54
- <div class="tracking-widest leading-none">
55
- <span data-text="RUSSIA IS A" />
56
- <span
57
- class="underline decoration-red-600 decoration-1 underline-offset-3"
58
- data-text="TERRORIST"
59
- />
60
- <span data-text="STATE" />
128
+ <slot name="icon">
129
+ <span
130
+ v-if="props.showIcon"
131
+ :class="props.iconClass"
132
+ />
133
+ </slot>
134
+
135
+ <span class="leading-none inline-flex">
136
+ <slot>
137
+ <span :data-text="props.message"></span>
138
+ <a
139
+ v-if="props.linkUrl"
140
+ :href="props.linkUrl"
141
+ target="_blank"
142
+ rel="noopener"
143
+ :title="props.linkTitle || props.linkText"
144
+ class="underline underline-offset-2 hover:text-blue-wrc ml-1.5"
145
+ :data-text="props.linkText"
146
+ ></a>
147
+ <span v-if="props.linkUrl" data-text="."></span>
148
+ </slot>
149
+ </span>
150
+
151
+ <button
152
+ v-if="props.showCloseButton && (props.toggleMessage || props.persistClose)"
153
+ class="btn-close text-white"
154
+ :aria-label="props.closeButtonAriaLabel"
155
+ @click="props.persistClose ? closePermanently() : toggleVisibility()"
156
+ >
157
+ <span class="close close-dark" />
158
+ </button>
61
159
  </div>
62
- <button
63
- v-if="props.showCloseButton"
64
- class="btn-close"
65
- aria-label="Toggle"
66
- @click="toggleVisibility()"
160
+ <div
161
+ v-else-if="props.toggleMessage"
162
+ data-pagefind-ignore
163
+ :class="`px-4 sm:px-8 py-3 flex items-center justify-center text-xs sm:text-base leading-none relative drop-shadow-md z-2 ${props.toggleBgClass} ${props.toggleTextClass}`"
67
164
  >
68
- <span class="close close-light" />
69
- </button>
70
- </div>
165
+ <div class="tracking-widest leading-none">
166
+ <slot name="toggle">
167
+ {{ props.toggleMessage }}
168
+ </slot>
169
+ </div>
170
+ <button
171
+ v-if="props.showCloseButton"
172
+ class="btn-close"
173
+ :aria-label="props.closeButtonAriaLabel"
174
+ @click="props.persistClose ? closePermanently() : toggleVisibility()"
175
+ >
176
+ <span :class="props.toggleTextClass === 'text-white' ? 'close close-light' : 'close close-dark'" />
177
+ </button>
178
+ </div>
179
+ </template>
71
180
  </template>
72
181
 
73
182
  <style>
@@ -9,56 +9,204 @@ import { Icon } from 'astro-icon/components';
9
9
 
10
10
  # Modal
11
11
 
12
- Native modal window based on `<dialog>` tag with `::backdrop` CSS pseudo-element (pure CSS3 modal window).
13
- #
14
- **Documentation:**
12
+ Native modal window based on `<dialog>` tag with `::backdrop` CSS pseudo-element. Features built-in X close button, action buttons, and flexible configuration.
15
13
 
14
+ **Documentation:**
16
15
  - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
17
16
  - https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop
18
17
 
19
- ## Modal window
18
+ ## Basic Modal with Title and X Button
19
+
20
+ <div class="component-preview">
21
+ <Modal id="dialog1" open="Open modal" title="Modal Title">
22
+ <p>This modal has a title and built-in X close button in the top-right corner.</p>
23
+ <p>You can also close by clicking the backdrop or pressing Escape.</p>
24
+ </Modal>
25
+ </div>
26
+
27
+ ```astro
28
+ <Modal id="dialog1" open="Open modal" title="Modal Title">
29
+ <p>This modal has a title and built-in X close button in the top-right corner.</p>
30
+ <p>You can also close by clicking the backdrop or pressing Escape.</p>
31
+ </Modal>
32
+ ```
20
33
 
34
+ ## Modal with Action Buttons
21
35
 
22
36
  <div class="component-preview">
23
- <Modal id="dialog" open="Open modal">
24
- <main slot="main">
25
- <p>Some extra content you would like here</p>
26
- <hr />
27
- </main>
28
- <Button primary slot="close" class="mt-4">Close</Button>
37
+ <Modal
38
+ id="dialog2"
39
+ open="Delete Item"
40
+ title="Confirm Delete"
41
+ showActions
42
+ cancelText="Cancel"
43
+ confirmText="Delete"
44
+ >
45
+ <p>Are you sure you want to delete this item? This action cannot be undone.</p>
29
46
  </Modal>
30
47
  </div>
31
48
 
32
- ```js
33
- <Modal id="dialog" open="Open modal">
34
- <main slot="main">
35
- <p>Some extra content you would like here</p>
36
- <hr />
37
- <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
38
- </main>
39
- <Button primary slot="close" class="mt-4">Close</Button>
49
+ ```astro
50
+ <Modal
51
+ id="dialog2"
52
+ open="Delete Item"
53
+ title="Confirm Delete"
54
+ showActions
55
+ cancelText="Cancel"
56
+ confirmText="Delete"
57
+ >
58
+ <p>Are you sure you want to delete this item? This action cannot be undone.</p>
40
59
  </Modal>
41
60
  ```
42
61
 
43
- ## Modal with different close button
62
+ ## Modal with Custom Actions
44
63
 
45
64
  <div class="component-preview">
46
- <Modal id="dialog2" open="Open modal">
47
- <main slot="main">
48
- <p>Some extra content you would like here</p>
49
- </main>
50
- <Button primary slot="close" class="mt-4"><Icon name="octicon:x-24" /></Button>
65
+ <Modal
66
+ id="dialog3"
67
+ open="Save Changes"
68
+ title="Save Document"
69
+ showActions
70
+ >
71
+ <p>Would you like to save your changes before closing?</p>
72
+
73
+ <div slot="actions" class="flex gap-3 justify-end">
74
+ <form method="dialog" class="contents">
75
+ <Button>Don't Save</Button>
76
+ </form>
77
+ <form method="dialog" class="contents">
78
+ <Button primary>Save</Button>
79
+ </form>
80
+ </div>
81
+ </Modal>
82
+ </div>
83
+
84
+ ```astro
85
+ <Modal
86
+ id="dialog3"
87
+ open="Save Changes"
88
+ title="Save Document"
89
+ showActions
90
+ >
91
+ <p>Would you like to save your changes before closing?</p>
92
+
93
+ <div slot="actions" class="flex gap-3 justify-end">
94
+ <form method="dialog" class="contents">
95
+ <Button>Don't Save</Button>
96
+ </form>
97
+ <form method="dialog" class="contents">
98
+ <Button primary>Save</Button>
99
+ </form>
100
+ </div>
101
+ </Modal>
102
+ ```
103
+
104
+ ## Modal with Custom Width
105
+
106
+ <div class="component-preview">
107
+ <Modal
108
+ id="dialog4"
109
+ open="Wide Modal"
110
+ title="Large Content"
111
+ maxWidth="900px"
112
+ >
113
+ <p>This modal is wider than the default. Useful for forms or detailed content.</p>
114
+ <div class="grid grid-cols-2 gap-4 mt-4">
115
+ <div class="p-4 bg-gray-100 rounded">Column 1</div>
116
+ <div class="p-4 bg-gray-100 rounded">Column 2</div>
117
+ </div>
118
+ </Modal>
119
+ </div>
120
+
121
+ ```astro
122
+ <Modal
123
+ id="dialog4"
124
+ open="Wide Modal"
125
+ title="Large Content"
126
+ maxWidth="900px"
127
+ >
128
+ <p>This modal is wider than the default. Useful for forms or detailed content.</p>
129
+ </Modal>
130
+ ```
131
+
132
+ ## Modal without X Button (Legacy Style)
133
+
134
+ <div class="component-preview">
135
+ <Modal id="dialog5" open="Open modal" title="Custom Close" showXButton={false}>
136
+ <p>This modal doesn't have the X button.</p>
137
+ <p>You need to provide your own close button.</p>
138
+ <Button primary slot="close" class="mt-4">Close Modal</Button>
51
139
  </Modal>
52
140
  </div>
53
141
 
54
- ```js
55
- <Modal id="dialog2" open="Open modal">
56
- <main slot="main">
57
- <p>Some extra content you would like here</p>
58
- <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
59
- </main>
142
+ ```astro
143
+ <Modal id="dialog5" open="Open modal" title="Custom Close" showXButton={false}>
144
+ <p>This modal doesn't have the X button.</p>
145
+ <Button primary slot="close" class="mt-4">Close Modal</Button>
146
+ </Modal>
147
+ ```
148
+
149
+ ## Modal with Custom Close Button (Icon)
150
+
151
+ <div class="component-preview">
152
+ <Modal id="dialog6" open="Open modal" title="Icon Close" showXButton={false}>
153
+ <p>Some extra content you would like here</p>
154
+ <Button primary slot="close" class="mt-4"><Icon name="octicon:x-24" /></Button>
155
+ </Modal>
156
+ </div>
157
+
158
+ ```astro
159
+ <Modal id="dialog6" open="Open modal" title="Icon Close" showXButton={false}>
160
+ <p>Some extra content you would like here</p>
60
161
  <Button primary slot="close" class="mt-4"><Icon name="octicon:x-24" /></Button>
61
162
  </Modal>
62
163
  ```
63
164
 
165
+ ## Handling Confirm Events
166
+
167
+ You can listen for the `confirm` event when using action buttons:
168
+
169
+ ```javascript
170
+ document.getElementById('dialog2').addEventListener('confirm', (e) => {
171
+ console.log('Confirmed!', e.detail);
172
+ // Perform your action here
173
+ document.getElementById('dialog2').close();
174
+ });
175
+ ```
176
+
177
+ ## Props
178
+
179
+ | Prop | Type | Default | Description |
180
+ |------|------|---------|-------------|
181
+ | `id` | string | required | Unique identifier for the modal |
182
+ | `open` | string | `'Open modal'` | Text for the trigger button |
183
+ | `title` | string | `undefined` | Modal title (shown in header) |
184
+ | `maxWidth` | string | `'600px'` | Maximum width of the modal |
185
+ | `showXButton` | boolean | `true` | Show X close button in top-right |
186
+ | `showTrigger` | boolean | `true` | Show the trigger button |
187
+ | `showActions` | boolean | `false` | Show action buttons footer |
188
+ | `cancelText` | string | `'Cancel'` | Text for cancel button |
189
+ | `confirmText` | string | `'Confirm'` | Text for confirm button |
190
+ | `confirmPrimary` | boolean | `true` | Make confirm button primary style |
191
+
192
+ ## Slots
193
+
194
+ | Slot | Description |
195
+ |------|-------------|
196
+ | `default` | Main modal content (alias for `main` slot) |
197
+ | `main` | Main modal content |
198
+ | `header` | Custom header content (replaces title) |
199
+ | `actions` | Custom action buttons (when `showActions` is true) |
200
+ | `close` | Custom close button (when `showActions` is false) |
201
+
202
+ ## Features
203
+
204
+ - ✅ Built-in X close button in top-right corner
205
+ - ✅ Click backdrop to close
206
+ - ✅ Press Escape to close
207
+ - ✅ Built-in action buttons (Cancel/Confirm)
208
+ - ✅ Customizable width
209
+ - ✅ TypeScript support
210
+ - ✅ Fully accessible with ARIA labels
211
+
64
212