spoko-design-system 0.2.84 → 0.2.85

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/astro.config.mjs CHANGED
@@ -93,9 +93,7 @@ export default defineConfig({
93
93
  "icon-park-outline": ["shopping-bag", "comment", "comments", "tag-one"],
94
94
  flowbite: ["arrow-left-outline", "arrow-right-outline", "angle-left-outline", "angle-right-outline", "chevron-left-outline", "chevron-right-outline", "map-location-outline", "map-pin-alt-solid", "x-outline", "messages-outline", "arrow-down-to-bracket-outline", "check-outline"]
95
95
  }
96
- }),
97
- // Compress({
98
- // CSS: true,
96
+ }), // CSS: true,
99
97
  // HTML: {
100
98
  // caseSensitive: true,
101
99
  // collapseBooleanAttributes: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoko-design-system",
3
- "version": "0.2.84",
3
+ "version": "0.2.85",
4
4
  "private": false,
5
5
  "main": "./index.ts",
6
6
  "module": "./index.ts",
@@ -58,11 +58,11 @@
58
58
  "@iconify-json/la": "^1.2.1",
59
59
  "@iconify-json/mdi": "^1.2.3",
60
60
  "@iconify-json/noto-v1": "^1.2.1",
61
- "@iconify-json/octicon": "^1.2.3",
61
+ "@iconify-json/octicon": "^1.2.4",
62
62
  "@iconify-json/ph": "^1.2.2",
63
63
  "@iconify-json/simple-icons": "^1.2.23",
64
64
  "@iconify-json/uil": "^1.2.3",
65
- "@iconify/json": "^2.2.303",
65
+ "@iconify/json": "^2.2.304",
66
66
  "@iconify/vue": "^4.3.0",
67
67
  "@playform/compress": "^0.1.7",
68
68
  "@playform/inline": "github:playform/inline",
@@ -1,63 +1,77 @@
1
1
  ---
2
2
  import CategorySidebarToggler from './CategorySidebarToggler.vue';
3
+ import CategoryViewToggler from './CategoryViewToggler.astro';
3
4
  import { Icon } from 'astro-icon/components';
4
5
  const { category, subcategory, subtitle, subsubtitle, titleSmall, locale } = Astro.props;
5
6
  import { t } from "i18next";
6
7
 
7
8
  // Compute base URL for localization
8
9
  const baseURL = locale === 'en' ? '' : `/${locale}`;
10
+
11
+ // View toggler translations
12
+ const viewerLabels = {
13
+ showText: t('category.view.show'),
14
+ listText: t('category.view.list'),
15
+ gridText: t('category.view.grid'),
16
+ listAriaLabel: t('category.view.listAriaLabel'),
17
+ gridAriaLabel: t('category.view.gridAriaLabel')
18
+ };
9
19
  ---
10
20
 
11
- <div ref="el"
12
- class="flex flex-nowrap pr-3 sm:pb-3 sm:pt-4 md:pl-4 relative z-10-off bg-gray-100 md:bg-white"
13
- transition:name="category-details"
14
- transition:animate="fade"
21
+ <div
22
+ ref="el"
23
+ class="flex flex-nowrap items-center pr-3 sm:pb-3 sm:pt-4 md:pl-4 relative z-10-off bg-gray-100 md:bg-white"
24
+ transition:name="category-details"
25
+ transition:animate="fade"
15
26
  >
16
- <CategorySidebarToggler onclick="toggleSidebar()">
27
+ <CategorySidebarToggler client:load onclick="toggleSidebar()">
17
28
  <Icon name="ant-design:menu-fold-outlined" class="toggler-btn hidden md:block" />
18
29
  <Icon name="ant-design:menu-unfold-outlined" class="toggler-btn hidden" />
19
30
  <Icon name="ant-design:menu-outlined" class="toggler-btn md:hidden" />
20
31
  </CategorySidebarToggler>
21
32
 
22
33
  <div class="overflow-x-auto overflow-y-hidden flex max-w-full items-center">
23
- {subtitle ? (
24
- <>
25
- <a class="text-lg font-vw-headregular whitespace-nowrap block" href={`${baseURL}/${category.slug}/`}>
26
- {category.name}
27
- {titleSmall && <small>{titleSmall}</small>}
28
- </a>
29
- <span class="text-gray-200 text-lg inline-block px-1 font-headlight">/</span>
30
- {!subsubtitle ? (
31
- <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap underline underline-offset-6 decoration-blue-300 decoration-0.5">
32
- {subtitle} <span class="sr-only"> {t('catalog.extra-short')}</span>
33
- </h1>
34
- ) : (
35
- <>
36
- <div class="text-lg py-2.5 sm:py-0 whitespace-nowrap ">
37
- <a href={`${baseURL}/${category.slug}/${subcategory.slug}/`}>
38
- {subtitle}
39
- </a>
40
- </div>
41
- <span class="text-gray-200 text-lg inline-block px-1 font-headlight">/</span>
42
- <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap underline underline-offset-6 decoration-blue-300 decoration-0.5">
43
- {subsubtitle} <span class="sr-only"> {t('catalog.extra-short')}</span>
44
- </h1>
45
- </>
46
- )}
47
- </>
48
- ) : (
49
- <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap">
34
+ {subtitle ? (
35
+ <>
36
+ <a class="text-lg font-vw-headregular whitespace-nowrap block" href={`${baseURL}/${category.slug}/`}>
50
37
  {category.name}
51
38
  {titleSmall && <small>{titleSmall}</small>}
52
- <span class="sr-only"> {t('catalog.extra-short')}</span>
53
- </h1>
54
- )}
39
+ </a>
40
+ <span class="text-gray-200 text-lg inline-block px-1 font-headlight">/</span>
41
+ {!subsubtitle ? (
42
+ <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap underline underline-offset-6 decoration-blue-300 decoration-0.5">
43
+ {subtitle} <span class="sr-only"> {t('catalog.extra-short')}</span>
44
+ </h1>
45
+ ) : (
46
+ <>
47
+ <div class="text-lg py-2.5 sm:py-0 whitespace-nowrap ">
48
+ <a href={`${baseURL}/${category.slug}/${subcategory.slug}/`}>
49
+ {subtitle}
50
+ </a>
51
+ </div>
52
+ <span class="text-gray-200 text-lg inline-block px-1 font-headlight">/</span>
53
+ <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap underline underline-offset-6 decoration-blue-300 decoration-0.5">
54
+ {subsubtitle} <span class="sr-only"> {t('catalog.extra-short')}</span>
55
+ </h1>
56
+ </>
57
+ )}
58
+ </>
59
+ ) : (
60
+ <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap">
61
+ {category.name}
62
+ {titleSmall && <small>{titleSmall}</small>}
63
+ <span class="sr-only"> {t('catalog.extra-short')}</span>
64
+ </h1>
65
+ )}
55
66
  </div>
67
+
68
+ <CategoryViewToggler {...viewerLabels} />
56
69
  </div>
57
70
 
58
71
  <script is:inline>
59
72
  function toggleSidebar() {
60
73
  const sidebar = document.getElementById('sidebar');
74
+ const togglers = document.querySelectorAll('.toggler-btn');
61
75
  const isMobile = window.matchMedia("(max-width: 768px)").matches;
62
76
 
63
77
  if (sidebar) {
@@ -69,13 +83,17 @@ const baseURL = locale === 'en' ? '' : `/${locale}`;
69
83
  document.body.classList.remove('overflow-hidden');
70
84
  sidebar.classList.toggle('collapsed');
71
85
  localStorage.setItem('sidebarState', sidebar.classList.contains('collapsed') ? 'closed' : 'open');
86
+
87
+ // Toggle visibility of buttons on desktop
88
+ togglers.forEach(btn => btn.classList.toggle('hidden'));
72
89
  }
73
90
  }
74
91
  }
75
92
 
76
93
  // Read status from localStorage on page load
77
- document.addEventListener('DOMContentLoaded', () => {
94
+ document.addEventListener('astro:page-load', () => {
78
95
  const sidebar = document.getElementById('sidebar');
96
+ const togglers = document.querySelectorAll('.toggler-btn');
79
97
  const isMobile = window.matchMedia("(max-width: 768px)").matches;
80
98
  const savedState = localStorage.getItem('sidebarState');
81
99
 
@@ -85,22 +103,35 @@ const baseURL = locale === 'en' ? '' : `/${locale}`;
85
103
  sidebar.classList.add('show');
86
104
  } else {
87
105
  sidebar.classList.remove('collapsed');
106
+ // Show correct button on desktop
107
+ if (togglers.length >= 2) {
108
+ togglers[0].classList.remove('hidden'); // menu-fold
109
+ togglers[1].classList.add('hidden'); // menu-unfold
110
+ }
88
111
  }
89
112
  } else if (savedState === 'closed') {
90
113
  if (isMobile) {
91
114
  sidebar.classList.remove('show');
92
115
  } else {
93
116
  sidebar.classList.add('collapsed');
117
+ // Show correct button on desktop
118
+ if (togglers.length >= 2) {
119
+ togglers[0].classList.add('hidden'); // menu-fold
120
+ togglers[1].classList.remove('hidden'); // menu-unfold
121
+ }
94
122
  }
95
123
  }
96
124
  }
97
125
  });
98
126
  </script>
99
127
 
100
-
101
128
  <style>
102
- .category-toggler {
103
- right: -1px;
104
- bottom: -1px;
129
+ /* View toggle styles */
130
+ .view-grid {
131
+ @apply grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4;
132
+ }
133
+
134
+ .view-list {
135
+ @apply flex flex-col gap-4;
105
136
  }
106
- </style>
137
+ </style>
@@ -0,0 +1,99 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+
4
+ interface Props {
5
+ showText: string; // "Show:"
6
+ listText: string; // "List"
7
+ gridText: string; // "Grid"
8
+ listAriaLabel: string; // "List view"
9
+ gridAriaLabel: string; // "Grid view"
10
+ }
11
+
12
+ const {
13
+ showText,
14
+ listText,
15
+ gridText,
16
+ listAriaLabel,
17
+ gridAriaLabel
18
+ } = Astro.props;
19
+ ---
20
+
21
+ <div class="flex items-center gap-2 ml-auto">
22
+ <span class="text-gray-600 text-sm">{showText}</span>
23
+ <div class="flex border rounded">
24
+ <button
25
+ data-view="list"
26
+ class="view-toggle flex items-center gap-1 px-3 py-1.5 transition-colors"
27
+ aria-label={listAriaLabel}
28
+ >
29
+ <Icon name="ant-design:bars-outlined" class="w-4 h-4" />
30
+ <span class="text-sm">{listText}</span>
31
+ </button>
32
+ <button
33
+ data-view="grid"
34
+ class="view-toggle flex items-center gap-1 px-3 py-1.5 transition-colors"
35
+ aria-label={gridAriaLabel}
36
+ >
37
+ <Icon name="ant-design:appstore-outlined" class="w-4 h-4" />
38
+ <span class="text-sm">{gridText}</span>
39
+ </button>
40
+ </div>
41
+ </div>
42
+
43
+ <script>
44
+ // Initialize view state
45
+ function initializeView() {
46
+ const savedView = localStorage.getItem('categoryView') || 'list';
47
+ const buttons = document.querySelectorAll('.view-toggle');
48
+ const productsContainer = document.querySelector('.products-container');
49
+
50
+ // Set initial active state
51
+ buttons.forEach(button => {
52
+ if (button instanceof HTMLElement) {
53
+ const isActive = button.dataset.view === savedView;
54
+ button.classList.toggle('bg-gray-100', isActive);
55
+ }
56
+ });
57
+
58
+ // Set initial view on products container
59
+ if (productsContainer) {
60
+ productsContainer.classList.remove('view-grid', 'view-list');
61
+ productsContainer.classList.add(`view-${savedView}`);
62
+ }
63
+ }
64
+
65
+ // Handle view changes
66
+ function setupViewToggle() {
67
+ const buttons = document.querySelectorAll('.view-toggle');
68
+
69
+ buttons.forEach(button => {
70
+ button.addEventListener('click', (e) => {
71
+ const clickedButton = e.currentTarget;
72
+ if (!(clickedButton instanceof HTMLElement)) return;
73
+
74
+ const view = clickedButton.dataset.view;
75
+ if (!view) return;
76
+
77
+ // Update localStorage
78
+ localStorage.setItem('categoryView', view);
79
+
80
+ // Update button states
81
+ buttons.forEach(btn => btn.classList.remove('bg-gray-100'));
82
+ clickedButton.classList.add('bg-gray-100');
83
+
84
+ // Update products container
85
+ const productsContainer = document.querySelector('.products-container');
86
+ if (productsContainer) {
87
+ productsContainer.classList.remove('view-grid', 'view-list');
88
+ productsContainer.classList.add(`view-${view}`);
89
+ }
90
+ });
91
+ });
92
+ }
93
+
94
+ // Setup on page load
95
+ document.addEventListener('astro:page-load', () => {
96
+ initializeView();
97
+ setupViewToggle();
98
+ });
99
+ </script>