@chassis-ui/docs 0.2.0 → 0.3.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.
- package/README.md +16 -16
- package/index.ts +6 -1
- package/package.json +20 -8
- package/src/components/DocsSidebar.astro +63 -23
- package/src/components/FeatureCard.astro +3 -3
- package/src/components/TableOfContents.astro +8 -7
- package/src/components/ThemeToggler.astro +15 -36
- package/src/components/shortcodes/Blockquote.astro +1 -1
- package/src/components/shortcodes/CSSOnly.astro +6 -2
- package/src/components/shortcodes/Callout.astro +6 -2
- package/src/components/shortcodes/Code.astro +289 -0
- package/src/components/shortcodes/DeprecatedIn.astro +5 -2
- package/src/components/shortcodes/Example.astro +132 -0
- package/src/components/shortcodes/Icon.astro +57 -0
- package/src/components/shortcodes/InFigma.astro +7 -2
- package/src/components/shortcodes/JsDocs.astro +64 -0
- package/src/components/shortcodes/Placeholder.astro +28 -0
- package/src/components/shortcodes/ResizableExample.astro +126 -0
- package/src/components/shortcodes/ScssDocs.astro +108 -0
- package/src/components/shortcodes/ScssDocsSimple.astro +66 -0
- package/src/js/color-modes.js +3 -3
- package/src/js/example-mode.js +75 -0
- package/src/js/search.js +501 -39
- package/src/js/theme-color.js +1 -1
- package/src/layouts/DocsLayout.astro +11 -14
- package/src/layouts/RedirectLayout.astro +1 -0
- package/src/layouts/footer/Scripts.astro +14 -9
- package/src/layouts/head/Analytics.astro +12 -6
- package/src/layouts/head/Favicons.astro +0 -2
- package/src/layouts/head/Head.astro +2 -7
- package/src/layouts/head/Stylesheet.astro +18 -11
- package/src/layouts/header/Header.astro +2 -5
- package/src/layouts/header/Navigation.astro +31 -33
- package/src/layouts/header/SearchDialog.astro +74 -0
- package/src/layouts/header/SearchTrigger.astro +41 -0
- package/src/libs/highlight.ts +91 -0
- package/src/libs/icon.ts +75 -0
- package/src/libs/image.ts +12 -1
- package/src/libs/layout.ts +18 -0
- package/src/libs/markdown.ts +128 -0
- package/src/libs/placeholder.ts +250 -0
- package/src/libs/rehype.ts +0 -17
- package/src/libs/shortcodes.ts +129 -0
- package/src/libs/site.ts +95 -0
- package/src/libs/toc.ts +40 -6
- package/src/libs/utils.ts +34 -82
- package/src/libs/vite.ts +45 -0
- package/src/scss/_anchor.scss +20 -16
- package/src/scss/_callouts.scss +29 -28
- package/src/scss/_content.scss +72 -103
- package/src/scss/_footer.scss +18 -19
- package/src/scss/_layout.scss +56 -63
- package/src/scss/_navbar.scss +19 -123
- package/src/scss/_placeholder-img.scss +11 -7
- package/src/scss/_scrolling.scss +14 -12
- package/src/scss/_search.scss +418 -0
- package/src/scss/_shiki.scss +120 -0
- package/src/scss/_sidebar.scss +137 -132
- package/src/scss/_skippy.scss +6 -4
- package/src/scss/_snippets.scss +181 -0
- package/src/scss/_toc.scss +97 -93
- package/src/scss/_vars.scss +89 -0
- package/src/scss/main.scss +75 -80
- package/src/js/icon-loader.js +0 -229
- package/src/libs/chassis.ts +0 -38
- package/src/scss/_brand.scss +0 -59
- package/src/scss/_buttons.scss +0 -33
- package/src/scss/_clipboard-js.scss +0 -63
- package/src/scss/_code.scss +0 -116
- package/src/scss/_colors.scss +0 -140
- package/src/scss/_docsearch.scss +0 -212
- package/src/scss/_settings.scss +0 -29
- package/src/scss/_syntax.scss +0 -160
- package/src/scss/_variables.scss +0 -73
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { replacePlaceholdersInHtml } from '../../libs/placeholder'
|
|
3
|
+
import { replaceIconsInHtml } from '../../libs/icon'
|
|
4
|
+
import Code from './Code.astro'
|
|
5
|
+
import Icon from './Icon.astro'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/**
|
|
9
|
+
* Defines if extra JS snippet should be added to StackBlitz or not.
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
12
|
+
addStackblitzJs?: boolean
|
|
13
|
+
/**
|
|
14
|
+
* The example code.
|
|
15
|
+
*/
|
|
16
|
+
code: string | string[]
|
|
17
|
+
/**
|
|
18
|
+
* Custom markup to display in the source code section, different from the preview code.
|
|
19
|
+
* If provided, this will be used instead of the `code` prop for the source display.
|
|
20
|
+
*/
|
|
21
|
+
customMarkup?: string | string[]
|
|
22
|
+
/**
|
|
23
|
+
* The CSS class(es) to be added to the resizable container.
|
|
24
|
+
*/
|
|
25
|
+
class?: string
|
|
26
|
+
/**
|
|
27
|
+
* Alias for class prop.
|
|
28
|
+
*/
|
|
29
|
+
containerClass?: string
|
|
30
|
+
/**
|
|
31
|
+
* The preview wrapping `div` element ID.
|
|
32
|
+
*/
|
|
33
|
+
id?: string
|
|
34
|
+
/**
|
|
35
|
+
* Initial width of the resizable container.
|
|
36
|
+
* @default '100%'
|
|
37
|
+
*/
|
|
38
|
+
initialWidth?: string
|
|
39
|
+
/**
|
|
40
|
+
* Minimum width of the resizable container.
|
|
41
|
+
* @default '200px'
|
|
42
|
+
*/
|
|
43
|
+
minWidth?: string
|
|
44
|
+
/**
|
|
45
|
+
* Whether to show the code markup below.
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
showMarkup?: boolean
|
|
49
|
+
/**
|
|
50
|
+
* If true, displays line numbers in the code block.
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
lineNumbers?: boolean
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const {
|
|
57
|
+
code,
|
|
58
|
+
class: className,
|
|
59
|
+
customMarkup,
|
|
60
|
+
containerClass,
|
|
61
|
+
id,
|
|
62
|
+
initialWidth = '100%',
|
|
63
|
+
minWidth = '200px',
|
|
64
|
+
showMarkup = true,
|
|
65
|
+
lineNumbers = false
|
|
66
|
+
} = Astro.props
|
|
67
|
+
|
|
68
|
+
let markup = Array.isArray(code) ? code.join('\n') : code
|
|
69
|
+
markup = replacePlaceholdersInHtml(markup)
|
|
70
|
+
markup = replaceIconsInHtml(markup)
|
|
71
|
+
|
|
72
|
+
// The resizable container needs `overflow: hidden` for the CSS `resize` handle
|
|
73
|
+
// to work, which clips popovers like menus/dropdowns when they open. Render them
|
|
74
|
+
// with a fixed positioning strategy in the live preview only so they can spill
|
|
75
|
+
// outside the container. The displayed markup below is left untouched.
|
|
76
|
+
const previewMarkup = markup.replace(
|
|
77
|
+
/data-cx-toggle="(menu|dropdown)"(?![^>]*\bdata-cx-strategy=)/g,
|
|
78
|
+
'data-cx-toggle="$1" data-cx-strategy="fixed"'
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
let sourceMarkup = customMarkup
|
|
82
|
+
? Array.isArray(customMarkup)
|
|
83
|
+
? customMarkup.join('\n')
|
|
84
|
+
: customMarkup
|
|
85
|
+
: markup
|
|
86
|
+
|
|
87
|
+
const simplifiedMarkup = sourceMarkup
|
|
88
|
+
.replace(
|
|
89
|
+
/<svg.*class="cxd-placeholder-img(?:-large)?(?: *?cxd-placeholder-img-large)? ?(.*?)".*?<\/svg>/g,
|
|
90
|
+
(_match, classes) => `<img src="..."${classes ? ` class="${classes}"` : ''} alt="...">`
|
|
91
|
+
)
|
|
92
|
+
.replace(
|
|
93
|
+
/<img.*class="cxd-placeholder-img(?:-large)?(?: *?cxd-placeholder-img-large)? ?(.*?)".*?>/g,
|
|
94
|
+
(_match, classes) => `<img src="..."${classes ? ` class="${classes}"` : ''} alt="...">`
|
|
95
|
+
)
|
|
96
|
+
.replace(/(<svg class="icon\b[^"]*")\s+width="\d+"\s+height="\d+"/g, '$1')
|
|
97
|
+
.replace(/href="\/static\/icons\/chassis-icons\.svg#/g, 'href="#')
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
<div class="cxd-example-snippet cxd-code-snippet" data-pagefind-ignore>
|
|
101
|
+
<div id={id} class:list={['cxd-example m-0 border-0 context', className]}>
|
|
102
|
+
<div class="cxd-mode">
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
class="button-mode"
|
|
106
|
+
title="Toggle color mode"
|
|
107
|
+
aria-label="Toggle color mode"
|
|
108
|
+
data-cx-toggle="tooltip"
|
|
109
|
+
onclick="toggleTheme(this)"
|
|
110
|
+
>
|
|
111
|
+
<Icon name="swatchbook-solid" />
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
<div
|
|
115
|
+
class:list={['cxd-resizable-container', containerClass]}
|
|
116
|
+
style={`width: ${initialWidth}; min-width: ${minWidth};`}
|
|
117
|
+
>
|
|
118
|
+
<Fragment set:html={previewMarkup} />
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
{
|
|
122
|
+
showMarkup && (
|
|
123
|
+
<Code code={simplifiedMarkup} lang="html" nestedInExample={true} lineNumbers={lineNumbers} />
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
</div>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import * as sass from 'sass'
|
|
5
|
+
import Code from './Code.astro'
|
|
6
|
+
|
|
7
|
+
// Prints everything between `// scss-docs-start "name"` and `// scss-docs-end "name"`
|
|
8
|
+
// comments in the docs. When compile={true}, the snippet is compiled via the Sass
|
|
9
|
+
// JS API so @include mixin output is shown as real CSS. Default is raw SCSS.
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
/**
|
|
13
|
+
* Reference name used to find the content to display within the content of the `file` prop.
|
|
14
|
+
*/
|
|
15
|
+
name: string
|
|
16
|
+
/**
|
|
17
|
+
* File path that contains the content to display relative to the root of the repository.
|
|
18
|
+
*/
|
|
19
|
+
file: string
|
|
20
|
+
/**
|
|
21
|
+
* When true, compiles the SCSS snippet via the Sass JS API and displays
|
|
22
|
+
* the resulting CSS output (useful when the block contains @include mixin calls).
|
|
23
|
+
* Defaults to false — raw SCSS is shown as-is.
|
|
24
|
+
*/
|
|
25
|
+
compile?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { name, file, compile = false } = Astro.props
|
|
29
|
+
|
|
30
|
+
if (!name || !file) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Missing required parameter(s) for the '<ScssDocs />' component, expected both 'name' and 'file' but got 'name: ${name}' and 'file: ${file}'.`
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let content: string
|
|
37
|
+
let lang: string = 'scss'
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const fileContent = fs.readFileSync(file, 'utf8')
|
|
41
|
+
|
|
42
|
+
const matches = fileContent.match(
|
|
43
|
+
new RegExp(`// scss-docs-start ${name}\n((?:.|\n)*)// scss-docs-end ${name}`, 'm')
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if (!matches || !matches[1]) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Failed to find the content named '${name}', make sure that '// scss-docs-start ${name}' and '// scss-docs-end ${name}' are defined.`
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const rawContent = matches[1]
|
|
53
|
+
|
|
54
|
+
if (compile) {
|
|
55
|
+
// Derive the SCSS load path from the file argument (e.g. "scss/_foo.scss" → "scss/")
|
|
56
|
+
const scssLoadPath = path.resolve(process.cwd(), file.split('/')[0])
|
|
57
|
+
|
|
58
|
+
const scssInput = [
|
|
59
|
+
'@use "config" as *;',
|
|
60
|
+
'@use "functions" as *;',
|
|
61
|
+
'@use "tokens" as *;',
|
|
62
|
+
'@use "maps" as *;',
|
|
63
|
+
'@use "mixins" as *;',
|
|
64
|
+
'',
|
|
65
|
+
'.__cxd_docs_sentinel__ {',
|
|
66
|
+
rawContent,
|
|
67
|
+
'}'
|
|
68
|
+
].join('\n')
|
|
69
|
+
|
|
70
|
+
const result = await sass.compileStringAsync(scssInput, {
|
|
71
|
+
loadPaths: [scssLoadPath, path.resolve(process.cwd(), 'scss/vendor')],
|
|
72
|
+
// Match the same deprecation silencing used in astro.config.ts / vite scss options
|
|
73
|
+
silenceDeprecations: ['import', 'global-builtin', 'color-functions', 'if-function'] as any
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Extract the body between the sentinel braces.
|
|
77
|
+
// Greedy [\s\S]* ensures we capture multi-property output including mixin expansions.
|
|
78
|
+
const sentinelMatch = result.css.match(/\.__cxd_docs_sentinel__\s*\{([\s\S]*)\}/)
|
|
79
|
+
content = sentinelMatch ? sentinelMatch[1] : result.css
|
|
80
|
+
// Apply the same cx- prefix to CSS custom properties as the build's css:prefix step does.
|
|
81
|
+
content = content.replace(/\.__cxd_docs_sentinel__\s*/g, '')
|
|
82
|
+
content = content.replace(/--(?!cx-|cxd-|pf-|shell-|shiki-)([a-zA-Z][a-zA-Z0-9-]*)/g, '--cx-$1')
|
|
83
|
+
} else {
|
|
84
|
+
// Raw SCSS mode — Sass variable declarations, shown as-is.
|
|
85
|
+
content = rawContent.replaceAll(' !default', '')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Normalise indentation by removing the common leading-space prefix.
|
|
89
|
+
const lines = content.split('\n')
|
|
90
|
+
const spaceCounts = lines
|
|
91
|
+
.filter((line) => line.trim().length > 0)
|
|
92
|
+
.map((line) => (line.match(/^ */) || [''])[0].length)
|
|
93
|
+
const minSpaces = spaceCounts.length ? Math.min(...spaceCounts) : 0
|
|
94
|
+
content = lines
|
|
95
|
+
.map((line) => line.slice(minSpaces))
|
|
96
|
+
.join('\n')
|
|
97
|
+
.trim()
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Failed to find the content to render in the '<ScssDocs />' component at '${file}'.`,
|
|
101
|
+
{
|
|
102
|
+
cause: error
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
<Code containerClass="cxd-example-snippet cxd-file-ref" code={content} lang={lang} file={file} />
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import Code from './Code.astro'
|
|
4
|
+
|
|
5
|
+
// Prints everything between `// scss-docs-start "name"` and `// scss-docs-end "name"`
|
|
6
|
+
// comments in the docs.
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
/**
|
|
10
|
+
* Reference name used to find the content to display within the content of the `file` prop.
|
|
11
|
+
*/
|
|
12
|
+
name: string
|
|
13
|
+
/**
|
|
14
|
+
* File path that contains the content to display relative to the root of the repository.
|
|
15
|
+
*/
|
|
16
|
+
file: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { name, file } = Astro.props
|
|
20
|
+
|
|
21
|
+
let lang: string = 'scss'
|
|
22
|
+
|
|
23
|
+
if (!name || !file) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Missing required parameter(s) for the '<ScssDocs />' component, expected both 'name' and 'file' but got 'name: ${name}' and 'file: ${file}'.`
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let content: string
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const fileContent = fs.readFileSync(file, 'utf8')
|
|
33
|
+
|
|
34
|
+
const matches = fileContent.match(
|
|
35
|
+
new RegExp(`// scss-docs-start ${name}\n((?:.|\n)*)// scss-docs-end ${name}`, 'm')
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if (!matches || !matches[1]) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Failed to find the content named '${name}', make sure that '// scss-docs-start ${name}' and '// scss-docs-end ${name}' are defined.`
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
content = matches[1].replaceAll(' !default', '')
|
|
45
|
+
|
|
46
|
+
// Fix the identation by removing extra spaces at the beginning of each line
|
|
47
|
+
const lines = content.split('\n')
|
|
48
|
+
const spaceCounts = lines
|
|
49
|
+
.filter((line) => line.trim().length > 0)
|
|
50
|
+
.map((line) => (line.match(/^ */) || [''])[0].length)
|
|
51
|
+
const minSpaces = spaceCounts.length ? Math.min(...spaceCounts) : 0
|
|
52
|
+
content = lines
|
|
53
|
+
.map((line) => line.slice(minSpaces))
|
|
54
|
+
.join('\n')
|
|
55
|
+
.trimEnd()
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Failed to find the content to render in the '<ScssDocs />' component at '${file}'.`,
|
|
59
|
+
{
|
|
60
|
+
cause: error
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
<Code containerClass="cxd-example-snippet cxd-file-ref" code={content} lang={lang} file={file} />
|
package/src/js/color-modes.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
;(() => {
|
|
8
8
|
'use strict'
|
|
9
9
|
|
|
10
|
-
const getStoredTheme = () => localStorage.getItem('theme')
|
|
10
|
+
const getStoredTheme = () => localStorage.getItem('theme') || 'auto'
|
|
11
11
|
const setStoredTheme = (theme) => localStorage.setItem('theme', theme)
|
|
12
12
|
|
|
13
13
|
const getPreferredTheme = () => {
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
const svgOfActiveButton = buttonToActive.querySelector('svg use').getAttribute('href')
|
|
46
46
|
|
|
47
47
|
document.querySelectorAll('[data-cx-theme-value]').forEach((element) => {
|
|
48
|
-
element.classList.remove('
|
|
48
|
+
element.classList.remove('selected')
|
|
49
49
|
element.setAttribute('aria-pressed', 'false')
|
|
50
50
|
})
|
|
51
51
|
|
|
52
|
-
buttonToActive.classList.add('
|
|
52
|
+
buttonToActive.classList.add('selected')
|
|
53
53
|
buttonToActive.setAttribute('aria-pressed', 'true')
|
|
54
54
|
activeThemeIcon.setAttribute('href', svgOfActiveButton)
|
|
55
55
|
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${buttonToActive.dataset.cxThemeValue})`
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Tooltip } from '@chassis-ui/css'
|
|
2
|
+
|
|
3
|
+
// Helper to detect the user's preferred theme
|
|
4
|
+
function getPreferredTheme() {
|
|
5
|
+
// First check localStorage
|
|
6
|
+
const storedTheme = localStorage.getItem('theme')
|
|
7
|
+
if (storedTheme && storedTheme !== 'auto') {
|
|
8
|
+
return storedTheme
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Then check system preference
|
|
12
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Direct theme toggle function that's globally accessible
|
|
16
|
+
function toggleTheme(button) {
|
|
17
|
+
const example = button.closest('.cxd-example-snippet').querySelector('.cxd-example')
|
|
18
|
+
if (!example) return
|
|
19
|
+
|
|
20
|
+
// Get current theme - try from element, then use detection logic if not set
|
|
21
|
+
let currentTheme = example.getAttribute('data-cx-theme')
|
|
22
|
+
if (!currentTheme) {
|
|
23
|
+
// If element doesn't have theme set, use preferred theme
|
|
24
|
+
currentTheme = getPreferredTheme()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Toggle to the opposite
|
|
28
|
+
const newTheme = currentTheme === 'dark' ? 'light' : 'dark'
|
|
29
|
+
|
|
30
|
+
// Get system preferred theme
|
|
31
|
+
const preferredTheme = getPreferredTheme()
|
|
32
|
+
|
|
33
|
+
// If the new theme matches the preferred system theme, remove the attribute
|
|
34
|
+
// to let it naturally follow system preference
|
|
35
|
+
if (newTheme === preferredTheme) {
|
|
36
|
+
example.removeAttribute('data-cx-theme')
|
|
37
|
+
} else {
|
|
38
|
+
// Otherwise, explicitly set the theme
|
|
39
|
+
example.setAttribute('data-cx-theme', newTheme)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Update the icon to match the theme
|
|
43
|
+
const iconUse = button.querySelector('use')
|
|
44
|
+
if (iconUse) {
|
|
45
|
+
const iconHref = newTheme === 'dark' ? 'sun-solid' : 'moon-solid'
|
|
46
|
+
iconUse.setAttribute('href', `/static/icons/chassis-icons.svg#${iconHref}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Expose globally for inline onclick="toggleTheme(this)" handlers in Example.astro
|
|
51
|
+
window.toggleTheme = toggleTheme
|
|
52
|
+
|
|
53
|
+
// Initialize theme icons for all buttons based on current theme
|
|
54
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
55
|
+
const preferredTheme = getPreferredTheme()
|
|
56
|
+
|
|
57
|
+
document.querySelectorAll('.button-mode').forEach((button) => {
|
|
58
|
+
// Initialize tooltip
|
|
59
|
+
Tooltip.getOrCreateInstance(button)
|
|
60
|
+
|
|
61
|
+
// Set initial icon based on theme
|
|
62
|
+
const iconUse = button.querySelector('use')
|
|
63
|
+
if (iconUse) {
|
|
64
|
+
const example = button.closest('.cxd-example-snippet')?.querySelector('.cxd-example')
|
|
65
|
+
const currentTheme = example?.getAttribute('data-cx-theme') || preferredTheme
|
|
66
|
+
const iconHref = currentTheme === 'dark' ? 'sun-solid' : 'moon-solid'
|
|
67
|
+
iconUse.setAttribute('href', `/static/icons/chassis-icons.svg#${iconHref}`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Add event listener if not already set through onclick
|
|
71
|
+
if (!button.hasAttribute('onclick')) {
|
|
72
|
+
button.addEventListener('click', () => toggleTheme(button))
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
})
|