spoko-design-system 0.3.3 → 0.3.9

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/README.md CHANGED
@@ -66,7 +66,7 @@ export const SIDEBAR = [
66
66
  { text: "Introduction", link: "/core/introduction" },
67
67
  ...,
68
68
  { text: "Components", header: true },
69
- { text: "Buttons", link: "/components/jumbatron" },
69
+ { text: "Buttons", link: "/components/jumbotron" },
70
70
  ...,
71
71
  { text: "New section", header: true },
72
72
  { text: "New component", link: "/new-section/new-component.md" },
@@ -81,7 +81,7 @@ You're free to organize the pages however you want.
81
81
 
82
82
  ### Customizing Core section (colors, typography, shadows...)
83
83
 
84
- If you want to customize the default colors, typography or shadows you can find the configuration file at `src/config/design.config.ts`. I think I have prepared enough shades of blue ;-)
84
+ If you want to customize the default colors, typography or shadows you can find the configuration file at uno.config.ts`. I think I have prepared enough shades of blue ;-)
85
85
 
86
86
  Feel free to add new pages to the Core section
87
87
 
package/astro.config.mjs CHANGED
@@ -10,6 +10,9 @@ import pagefind from "astro-pagefind";
10
10
  import AstroPWA from '@vite-pwa/astro';
11
11
  import metaTags from "astro-meta-tags";
12
12
  import playformInline from "@playform/inline";
13
+ import { createSdsConfig } from './uno-config';
14
+
15
+ const unoConfig = createSdsConfig();
13
16
 
14
17
  // https://astro.build/config
15
18
  export default defineConfig({
@@ -72,8 +75,10 @@ export default defineConfig({
72
75
  experimental: {
73
76
  directoryAndTrailingSlashHandler: true
74
77
  }
75
- }), UnoCSS({
76
- injectReset: true
78
+ }),
79
+ UnoCSS({
80
+ injectReset: true,
81
+ ...unoConfig
77
82
  }),
78
83
  icon(iconConfig),
79
84
  metaTags(),
package/index.ts CHANGED
@@ -8,7 +8,7 @@ export { default as FuckRussia } from './src/components/FuckRussia.vue';
8
8
  export { default as FlagPL } from './src/components/flags/FlagPL.vue';
9
9
  export { default as Badges } from './src/components/Badges.vue';
10
10
  export { default as SlimBanner } from './src/components/SlimBanner.vue';
11
- export { default as Jumbatron } from './src/components/Jumbatron.vue';
11
+ export { default as Jumbotron } from './src/components/Jumbotron.astro';
12
12
  export { default as Button } from './src/components/Button.vue';
13
13
  export { default as Breadcrumbs } from './src/components/Breadcrumbs.vue';
14
14
  export { default as ProductDetailsList } from './src/components/ProductDetailsList.vue';
package/package.json CHANGED
@@ -1,119 +1,119 @@
1
- {
2
- "name": "spoko-design-system",
3
- "version": "0.3.03",
4
- "private": false,
5
- "main": "./index.ts",
6
- "module": "./index.ts",
7
- "types": "./index.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./index.ts",
11
- "require": "./index.ts"
12
- },
13
- "./styles/*": "./src/styles/*",
14
- "./icons": "./icon.config.ts",
15
- "./icon-collections": "./icon.collections.ts",
16
- "./uno-config": "./uno-config/index.ts"
17
- },
18
- "scripts": {
19
- "dev": "astro dev",
20
- "start": "astro dev",
21
- "build": "astro build",
22
- "preview": "astro preview"
23
- },
24
- "repository": {
25
- "type": "git",
26
- "url": "https://github.com/polo-blue/sds"
27
- },
28
- "author": {
29
- "name": "spokospace",
30
- "email": "szymon@spoko.space",
31
- "url": "https://spoko.space"
32
- },
33
- "homepage": "https://sds.spoko.space/",
34
- "license": "MIT",
35
- "keywords": [
36
- "astro-starter",
37
- "seo",
38
- "astro",
39
- "sds design system",
40
- "spoko design system"
41
- ],
42
- "dependencies": {
43
- "@algolia/client-search": "^5.20.2",
44
- "@astrojs/mdx": "^4.0.8",
45
- "@astrojs/node": "^9.0.2",
46
- "@astrojs/sitemap": "^3.2.1",
47
- "@astrojs/ts-plugin": "^1.10.4",
48
- "@astrojs/vue": "^5.0.6",
49
- "@docsearch/css": "^3.8.3",
50
- "@iconify-json/ant-design": "^1.2.5",
51
- "@iconify-json/bi": "^1.2.2",
52
- "@iconify-json/bx": "^1.2.2",
53
- "@iconify-json/carbon": "^1.2.6",
54
- "@iconify-json/circle-flags": "^1.2.6",
55
- "@iconify-json/ei": "^1.2.2",
56
- "@iconify-json/el": "^1.2.1",
57
- "@iconify-json/eos-icons": "^1.2.2",
58
- "@iconify-json/et": "^1.2.1",
59
- "@iconify-json/flowbite": "^1.2.4",
60
- "@iconify-json/fluent": "^1.2.13",
61
- "@iconify-json/fluent-emoji": "1.2.3",
62
- "@iconify-json/ic": "^1.2.2",
63
- "@iconify-json/icon-park-outline": "^1.2.2",
64
- "@iconify-json/la": "^1.2.1",
65
- "@iconify-json/material-symbols-light": "^1.2.14",
66
- "@iconify-json/mdi": "^1.2.3",
67
- "@iconify-json/noto-v1": "^1.2.1",
68
- "@iconify-json/octicon": "^1.2.4",
69
- "@iconify-json/ph": "^1.2.2",
70
- "@iconify-json/simple-icons": "^1.2.24",
71
- "@iconify-json/system-uicons": "^1.2.2",
72
- "@iconify-json/uil": "^1.2.3",
73
- "@iconify/json": "^2.2.305",
74
- "@iconify/vue": "^4.3.0",
75
- "@playform/compress": "^0.1.7",
76
- "@playform/inline": "^0.1.1",
77
- "@types/node": "^22.13.1",
78
- "@unocss/astro": "^65.4.3",
79
- "@unocss/preset-attributify": "^65.4.3",
80
- "@unocss/preset-typography": "^65.4.3",
81
- "@unocss/preset-uno": "^65.4.3",
82
- "@unocss/preset-web-fonts": "^65.4.3",
83
- "@unocss/preset-wind": "^65.4.3",
84
- "@unocss/reset": "^65.4.3",
85
- "@vite-pwa/astro": "^0.5.0",
86
- "@vueuse/core": "^12.5.0",
87
- "astro-i18next": "1.0.0-beta.21",
88
- "astro-icon": "^1.1.5",
89
- "astro-meta-tags": "^0.3.1",
90
- "astro-navbar": "^2.3.9",
91
- "astro-pagefind": "^1.8.0",
92
- "astro-remote": "^0.3.3",
93
- "dotenv": "^16.4.7",
94
- "i18next": "^24.2.2",
95
- "i18next-browser-languagedetector": "^8.0.2",
96
- "i18next-fs-backend": "^2.6.0",
97
- "i18next-http-backend": "^3.0.2",
98
- "i18next-vue": "^5.0.0",
99
- "swiper": "^11.2.2",
100
- "unocss": "^65.4.3",
101
- "vite": "^6.1.0",
102
- "vue": "^3.5.13"
103
- },
104
- "devDependencies": {
105
- "@unocss/transformer-variant-group": "^65.4.3",
106
- "@vitejs/plugin-vue": "^5.2.1",
107
- "@vue/compiler-sfc": "^3.5.13",
108
- "astro": "^5.2.5",
109
- "unocss": "^0.65.0"
110
- },
111
- "packageManager": "pnpm@9.15.3",
112
- "pnpm": {
113
- "default": "8.15.2",
114
- "overrides": {
115
- "file-type@>=17.0.0 <17.1.3": ">=17.1.3",
116
- "sharp@<0.30.5": ">=0.30.5"
117
- }
118
- }
119
- }
1
+ {
2
+ "name": "spoko-design-system",
3
+ "version": "0.3.9",
4
+ "private": false,
5
+ "main": "./index.ts",
6
+ "module": "./index.ts",
7
+ "types": "./index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./index.ts",
11
+ "require": "./index.ts"
12
+ },
13
+ "./styles/*": "./src/styles/*",
14
+ "./icons": "./icon.config.ts",
15
+ "./icon-collections": "./icon.collections.ts",
16
+ "./uno-config": "./uno-config/index.ts"
17
+ },
18
+ "scripts": {
19
+ "dev": "astro dev",
20
+ "start": "astro dev",
21
+ "build": "astro build",
22
+ "preview": "astro preview"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/polo-blue/sds"
27
+ },
28
+ "author": {
29
+ "name": "spokospace",
30
+ "email": "szymon@spoko.space",
31
+ "url": "https://spoko.space"
32
+ },
33
+ "homepage": "https://sds.spoko.space/",
34
+ "license": "MIT",
35
+ "keywords": [
36
+ "astro-starter",
37
+ "seo",
38
+ "astro",
39
+ "sds design system",
40
+ "spoko design system"
41
+ ],
42
+ "dependencies": {
43
+ "@algolia/client-search": "^5.20.2",
44
+ "@astrojs/mdx": "^4.0.8",
45
+ "@astrojs/node": "^9.1.0",
46
+ "@astrojs/sitemap": "^3.2.1",
47
+ "@astrojs/ts-plugin": "^1.10.4",
48
+ "@astrojs/vue": "^5.0.6",
49
+ "@docsearch/css": "^3.8.3",
50
+ "@iconify-json/ant-design": "^1.2.5",
51
+ "@iconify-json/bi": "^1.2.2",
52
+ "@iconify-json/bx": "^1.2.2",
53
+ "@iconify-json/carbon": "^1.2.7",
54
+ "@iconify-json/circle-flags": "^1.2.6",
55
+ "@iconify-json/ei": "^1.2.2",
56
+ "@iconify-json/el": "^1.2.1",
57
+ "@iconify-json/eos-icons": "^1.2.2",
58
+ "@iconify-json/et": "^1.2.1",
59
+ "@iconify-json/flowbite": "^1.2.4",
60
+ "@iconify-json/fluent": "^1.2.13",
61
+ "@iconify-json/fluent-emoji": "1.2.3",
62
+ "@iconify-json/ic": "^1.2.2",
63
+ "@iconify-json/icon-park-outline": "^1.2.2",
64
+ "@iconify-json/la": "^1.2.1",
65
+ "@iconify-json/material-symbols-light": "^1.2.14",
66
+ "@iconify-json/mdi": "^1.2.3",
67
+ "@iconify-json/noto-v1": "^1.2.1",
68
+ "@iconify-json/octicon": "^1.2.4",
69
+ "@iconify-json/ph": "^1.2.2",
70
+ "@iconify-json/simple-icons": "^1.2.24",
71
+ "@iconify-json/system-uicons": "^1.2.2",
72
+ "@iconify-json/uil": "^1.2.3",
73
+ "@iconify/json": "^2.2.306",
74
+ "@iconify/vue": "^4.3.0",
75
+ "@playform/compress": "^0.1.7",
76
+ "@playform/inline": "^0.1.1",
77
+ "@types/node": "^22.13.4",
78
+ "@unocss/astro": "^65.4.3",
79
+ "@unocss/preset-attributify": "^65.4.3",
80
+ "@unocss/preset-typography": "^65.4.3",
81
+ "@unocss/preset-uno": "^65.4.3",
82
+ "@unocss/preset-web-fonts": "^65.4.3",
83
+ "@unocss/preset-wind": "^65.4.3",
84
+ "@unocss/reset": "^65.4.3",
85
+ "@vite-pwa/astro": "^0.5.0",
86
+ "@vueuse/core": "^12.6.1",
87
+ "astro-i18next": "1.0.0-beta.21",
88
+ "astro-icon": "^1.1.5",
89
+ "astro-meta-tags": "^0.3.1",
90
+ "astro-navbar": "^2.3.9",
91
+ "astro-pagefind": "^1.8.0",
92
+ "astro-remote": "^0.3.3",
93
+ "dotenv": "^16.4.7",
94
+ "i18next": "^24.2.2",
95
+ "i18next-browser-languagedetector": "^8.0.3",
96
+ "i18next-fs-backend": "^2.6.0",
97
+ "i18next-http-backend": "^3.0.2",
98
+ "i18next-vue": "^5.1.0",
99
+ "swiper": "^11.2.3",
100
+ "unocss": "^65.4.3",
101
+ "vite": "^6.1.0",
102
+ "vue": "^3.5.13"
103
+ },
104
+ "devDependencies": {
105
+ "@unocss/transformer-variant-group": "^65.4.3",
106
+ "@vitejs/plugin-vue": "^5.2.1",
107
+ "@vue/compiler-sfc": "^3.5.13",
108
+ "astro": "^5.3.0",
109
+ "unocss": "^0.65.0"
110
+ },
111
+ "packageManager": "pnpm@9.15.3",
112
+ "pnpm": {
113
+ "default": "8.15.2",
114
+ "overrides": {
115
+ "file-type@>=17.0.0 <17.1.3": ">=17.1.3",
116
+ "sharp@<0.30.5": ">=0.30.5"
117
+ }
118
+ }
119
+ }
@@ -4,133 +4,123 @@ import CategoryViewToggler from './CategoryViewToggler.astro';
4
4
  import { Icon } from 'astro-icon/components';
5
5
  import { t } from "i18next";
6
6
 
7
- const { category, subcategory, subtitle, subsubtitle, titleSmall, locale, showViewToggler, viewerLabels } = Astro.props;
7
+ const {
8
+ category,
9
+ subcategory,
10
+ subtitle,
11
+ subsubtitle,
12
+ titleSmall,
13
+ locale,
14
+ showViewToggler,
15
+ viewerLabels
16
+ } = Astro.props;
8
17
 
9
- // Compute base URL for localization
10
18
  const baseURL = locale === 'en' ? '' : `/${locale}`;
11
-
12
19
  ---
13
20
 
14
21
  <div
15
- ref="el"
16
22
  class="flex flex-nowrap items-center pr-3 sm:pb-3 sm:pt-4 md:pl-4 relative z-10-off bg-neutral-lightest md:bg-white"
17
23
  transition:name="category-details"
18
24
  transition:animate="fade"
19
25
  >
20
- <CategorySidebarToggler onclick="toggleSidebar()" client:load>
21
- <!-- First icon - visible only on desktop when sidebar is expanded -->
22
- <Icon
23
- name="ant-design:menu-fold-outlined"
24
- class="toggler-btn hidden md:[&:not(.hidden)]:block"
25
- aria-hidden="true"
26
- />
27
- <!-- Second icon - visible only on desktop when sidebar is collapsed -->
28
- <Icon
29
- name="ant-design:menu-unfold-outlined"
30
- class="toggler-btn hidden md:[&:not(.hidden)]:block"
31
- aria-hidden="true"
32
- />
33
- <!-- Mobile icon -->
34
- <Icon
35
- name="ant-design:menu-outlined"
36
- class="toggler-btn block md:hidden"
37
- aria-hidden="true"
38
- />
39
- </CategorySidebarToggler>
26
+ <CategorySidebarToggler onclick="toggleSidebar()">
27
+ <!-- Desktop expanded -->
28
+ <Icon
29
+ name="ant-design:menu-fold-outlined"
30
+ class="toggler-btn md:[&:not(.hidden)]:block"
31
+ aria-hidden="true"
32
+ />
33
+ <!-- Desktop collapsed -->
34
+ <Icon
35
+ name="ant-design:menu-unfold-outlined"
36
+ class="toggler-btn hidden md:[&:not(.hidden)]:block"
37
+ aria-hidden="true"
38
+ />
39
+ <!-- Mobile icon -->
40
+ <Icon
41
+ name="ant-design:menu-outlined"
42
+ class="toggler-btn block md:hidden"
43
+ aria-hidden="true"
44
+ />
45
+ </CategorySidebarToggler>
40
46
 
41
- <div class="overflow-x-auto overflow-y-hidden flex max-w-full items-center">
42
- {subtitle ? (
43
- <>
44
- <a class="text-lg font-vw-headregular whitespace-nowrap block" href={`${baseURL}/${category.slug}/`}>
45
- {category.name}
46
- {titleSmall && <small>{titleSmall}</small>}
47
- </a>
48
- <span class="text-neutral-lighter text-lg inline-block px-1 font-headlight">/</span>
49
- {!subsubtitle ? (
50
- <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap underline underline-offset-6 decoration-blue-300 decoration-0.5">
51
- {subtitle} <span class="sr-only"> {t('catalog.extra-short')}</span>
52
- </h1>
53
- ) : (
54
- <>
55
- <div class="text-lg py-2.5 sm:py-0 whitespace-nowrap ">
56
- <a href={`${baseURL}/${category.slug}/${subcategory.slug}/`}>
57
- {subtitle}
58
- </a>
59
- </div>
60
- <span class="text-neutral-lighter text-lg inline-block px-1 font-headlight">/</span>
61
- <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap underline underline-offset-6 decoration-blue-300 decoration-0.5">
62
- {subsubtitle} <span class="sr-only"> {t('catalog.extra-short')}</span>
63
- </h1>
64
- </>
65
- )}
66
- </>
67
- ) : (
68
- <h1 class="text-lg py-2.5 sm:py-0 whitespace-nowrap">
69
- {category.name}
70
- {titleSmall && <small>{titleSmall}</small>}
71
- <span class="sr-only"> {t('catalog.extra-short')}</span>
72
- </h1>
73
- )}
74
- </div>
47
+ <!-- Rest of the existing template code -->
75
48
 
76
- <CategoryViewToggler {...viewerLabels} showViewToggler={showViewToggler} class="hidden lg:flex items-center gap-2 ml-auto" />
49
+ { showViewToggler && (
50
+ <CategoryViewToggler
51
+ {...viewerLabels}
52
+ class="hidden lg:flex items-center gap-2 ml-auto"
53
+ />
54
+ )}
77
55
  </div>
78
56
 
79
- <script is:inline>
57
+ <script>
58
+ function initializeSidebar() {
59
+ const sidebar = document.getElementById('sidebar');
60
+ const savedState = localStorage.getItem('sidebarState') || 'open';
61
+
62
+ if (sidebar) {
63
+ sidebar.classList.toggle('collapsed', savedState === 'closed');
64
+
65
+ const togglers = document.querySelectorAll('.toggler-btn');
66
+ if (togglers.length >= 2) {
67
+ const isCollapsed = savedState === 'closed';
68
+ togglers[0].classList.toggle('hidden', isCollapsed);
69
+ togglers[1].classList.toggle('hidden', !isCollapsed);
70
+ }
71
+ }
72
+ }
73
+
80
74
  function toggleSidebar() {
81
75
  const sidebar = document.getElementById('sidebar');
82
76
  const togglers = document.querySelectorAll('.toggler-btn');
83
77
  const isMobile = window.matchMedia("(max-width: 768px)").matches;
84
-
78
+
85
79
  if (sidebar) {
86
80
  if (isMobile) {
87
- // On mobile just toggle classes without localStorage
88
- document.body.classList.toggle('overflow-hidden');
89
- sidebar.classList.toggle('show');
81
+ const isShown = sidebar.classList.toggle('show');
82
+ document.body.classList.toggle('overflow-hidden', isShown);
90
83
  } else {
91
- // On desktop save the state
84
+ const isCollapsed = sidebar.classList.toggle('collapsed');
92
85
  document.body.classList.remove('overflow-hidden');
93
- sidebar.classList.toggle('collapsed');
94
- localStorage.setItem('sidebarState', sidebar.classList.contains('collapsed') ? 'closed' : 'open');
86
+ localStorage.setItem('sidebarState', isCollapsed ? 'closed' : 'open');
95
87
 
96
- // Toggle visibility of buttons on desktop
97
- togglers.forEach(btn => btn.classList.toggle('hidden'));
98
- }
99
- }
100
- }
101
-
102
- function handlePageLoad() {
103
- const sidebar = document.getElementById('sidebar');
104
- const togglers = document.querySelectorAll('.toggler-btn');
105
- const isMobile = window.matchMedia("(max-width: 768px)").matches;
106
-
107
- if (sidebar && !isMobile) {
108
- const savedState = localStorage.getItem('sidebarState');
109
-
110
- if (savedState === 'open') {
111
- sidebar.classList.remove('collapsed');
112
- // Show correct button on desktop
113
- if (togglers.length >= 2) {
114
- togglers[0].classList.remove('hidden'); // menu-fold
115
- togglers[1].classList.add('hidden'); // menu-unfold
116
- }
117
- } else if (savedState === 'closed') {
118
- sidebar.classList.add('collapsed');
119
- // Show correct button on desktop
120
88
  if (togglers.length >= 2) {
121
- togglers[0].classList.add('hidden'); // menu-fold
122
- togglers[1].classList.remove('hidden'); // menu-unfold
89
+ togglers[0].classList.toggle('hidden', isCollapsed);
90
+ togglers[1].classList.toggle('hidden', !isCollapsed);
123
91
  }
124
92
  }
125
93
  }
126
94
  }
127
95
 
128
- // Remove previous listener if exists
129
- // document.removeEventListener('astro:page-load', handlePageLoad);
130
- // Add new listener
131
- // document.addEventListener('astro:page-load', handlePageLoad);
132
- document.addEventListener('astro:page-load', handlePageLoad, { once: true });
96
+ // Initialize on every page load to maintain state during navigation
97
+ document.addEventListener('astro:page-load', initializeSidebar);
98
+
99
+ // Initialize on initial page load
100
+ document.addEventListener('DOMContentLoaded', initializeSidebar);
101
+
102
+ // Handle state during view transitions
103
+ document.addEventListener('astro:before-swap', () => {
104
+ const sidebarState = localStorage.getItem('sidebarState');
105
+ if (sidebarState) {
106
+ // Store state temporarily during transition
107
+ sessionStorage.setItem('tempSidebarState', sidebarState);
108
+ }
109
+ });
133
110
 
134
- // Initial call to set up the initial state
135
- handlePageLoad();
136
- </script>
111
+ document.addEventListener('astro:after-swap', () => {
112
+ const tempState = sessionStorage.getItem('tempSidebarState');
113
+ if (tempState) {
114
+ // Restore state after transition
115
+ localStorage.setItem('sidebarState', tempState);
116
+ sessionStorage.removeItem('tempSidebarState');
117
+ initializeSidebar();
118
+ }
119
+ });
120
+ </script>
121
+
122
+ <style>
123
+ .toggler-btn {
124
+ @apply md:-mt-0.5;
125
+ }
126
+ </style>
@@ -1,7 +1,7 @@
1
1
 
2
2
  <template>
3
3
  <button
4
- class="category-toggler"
4
+ class="category-toggler md:w-8"
5
5
  type="button"
6
6
  aria-label="toggle menu"
7
7
  >
@@ -7,7 +7,6 @@ interface Props {
7
7
  gridText: string; // "Grid"
8
8
  listAriaLabel: string; // "List view"
9
9
  gridAriaLabel: string; // "Grid view"
10
- showViewToggler: boolean;
11
10
  class?: string;
12
11
  }
13
12
 
@@ -17,13 +16,10 @@ const {
17
16
  gridText,
18
17
  listAriaLabel,
19
18
  gridAriaLabel,
20
- showViewToggler,
21
19
  class: className
22
20
  } = Astro.props;
23
21
  ---
24
22
 
25
- {
26
- showViewToggler &&
27
23
  <div class:list={[ className ]}>
28
24
  <span class="text-slate-default text-sm">{showText}</span>
29
25
  <div class="flex border rounded">
@@ -45,37 +41,42 @@ const {
45
41
  </button>
46
42
  </div>
47
43
  </div>
48
- }
49
44
 
50
- <script define:vars={{ showViewToggler }}>
51
- function handleViewToggle(e) {
52
- const button = e.currentTarget;
53
- if (!(button instanceof HTMLElement)) return;
45
+ <script>
46
+ function initializeView() {
47
+ const savedView = localStorage.getItem('categoryView') || 'list';
48
+ updateUI(savedView);
54
49
 
55
- const view = button.dataset.view;
50
+ // Clean up existing listeners to prevent duplicates
51
+ document.querySelectorAll('.view-toggle').forEach(btn => {
52
+ btn.removeEventListener('click', handleViewToggle);
53
+ btn.addEventListener('click', handleViewToggle);
54
+ });
55
+ }
56
+
57
+ function handleViewToggle(e) {
58
+ if (!(e.currentTarget instanceof HTMLElement)) return;
59
+ const view = e.currentTarget.dataset.view;
56
60
  if (!view) return;
57
-
61
+
58
62
  localStorage.setItem('categoryView', view);
59
63
  updateUI(view);
60
64
  }
61
-
65
+
62
66
  function updateUI(view) {
63
- document.querySelectorAll('.view-toggle')
64
- .forEach(btn => btn.classList.toggle('bg-neutral-lightest',
65
- btn.dataset.view === view));
66
-
67
+ // Update toggle buttons
68
+ document.querySelectorAll('.view-toggle').forEach(btn => {
69
+ btn.classList.toggle('bg-neutral-lightest', btn.dataset.view === view);
70
+ });
71
+
72
+ // Update products container
67
73
  const productsContainer = document.querySelector('.products-container');
68
74
  if (productsContainer) {
69
75
  productsContainer.classList.remove('view-grid', 'view-list');
70
76
  productsContainer.classList.add(`view-${view}`);
71
77
  }
72
78
  }
73
-
74
- document.addEventListener('astro:page-load', () => {
75
- if (!showViewToggler) return;
76
-
77
- updateUI(localStorage.getItem('categoryView') || 'list');
78
- document.querySelectorAll('.view-toggle')
79
- .forEach(btn => btn.addEventListener('click', handleViewToggle));
80
- }, { once: true });
81
- </script>
79
+
80
+ // Initialize on page load and view transitions
81
+ document.addEventListener('astro:page-load', initializeView);
82
+ </script>