@chassis-ui/docs 0.1.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 +98 -0
- package/index.ts +7 -0
- package/package.json +66 -0
- package/src/components/DocsSidebar.astro +89 -0
- package/src/components/NavDocsMenu.astro +26 -0
- package/src/components/NavLink.astro +24 -0
- package/src/components/ResponsiveImage.astro +31 -0
- package/src/components/TableOfContents.astro +31 -0
- package/src/components/ThemeToggler.astro +70 -0
- package/src/components/shortcodes/AddedIn.astro +13 -0
- package/src/components/shortcodes/Callout.astro +37 -0
- package/src/components/shortcodes/CxTable.astro +16 -0
- package/src/js/color-modes.js +82 -0
- package/src/js/icon-loader.js +220 -0
- package/src/js/search.js +58 -0
- package/src/js/sidebar.js +30 -0
- package/src/js/theme-color.js +126 -0
- package/src/layouts/BaseLayout.astro +94 -0
- package/src/layouts/DocsLayout.astro +147 -0
- package/src/layouts/IconsLayout.astro +19 -0
- package/src/layouts/RedirectLayout.astro +23 -0
- package/src/layouts/SingleLayout.astro +33 -0
- package/src/layouts/footer/Footer.astro +56 -0
- package/src/layouts/footer/Scripts.astro +22 -0
- package/src/layouts/head/Analytics.astro +22 -0
- package/src/layouts/head/Favicons.astro +11 -0
- package/src/layouts/head/Head.astro +54 -0
- package/src/layouts/head/Scss.astro +9 -0
- package/src/layouts/head/Social.astro +38 -0
- package/src/layouts/head/Stylesheet.astro +15 -0
- package/src/layouts/header/Header.astro +19 -0
- package/src/layouts/header/Navigation.astro +121 -0
- package/src/layouts/header/Skippy.astro +22 -0
- package/src/libs/image.ts +13 -0
- package/src/libs/layout.ts +7 -0
- package/src/libs/rehype.ts +38 -0
- package/src/libs/remark.ts +205 -0
- package/src/libs/toc.ts +44 -0
- package/src/libs/utils.ts +122 -0
- package/src/scss/_anchor.scss +21 -0
- package/src/scss/_brand.scss +59 -0
- package/src/scss/_buttons.scss +36 -0
- package/src/scss/_callouts.scss +40 -0
- package/src/scss/_clipboard-js.scss +63 -0
- package/src/scss/_code.scss +116 -0
- package/src/scss/_colors.scss +140 -0
- package/src/scss/_content.scss +141 -0
- package/src/scss/_docsearch.scss +174 -0
- package/src/scss/_footer.scss +29 -0
- package/src/scss/_layout.scss +72 -0
- package/src/scss/_masthead.scss +124 -0
- package/src/scss/_navbar.scss +138 -0
- package/src/scss/_placeholder-img.scss +15 -0
- package/src/scss/_scrolling.scss +16 -0
- package/src/scss/_settings.scss +37 -0
- package/src/scss/_sidebar.scss +161 -0
- package/src/scss/_skippy.scss +7 -0
- package/src/scss/_syntax.scss +158 -0
- package/src/scss/_toc.scss +117 -0
- package/src/scss/_variables.scss +78 -0
- package/src/scss/fonts.scss +1 -0
- package/src/scss/main.scss +90 -0
- package/src/scss/search.scss +26 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chassis Icons SVG Sprite Loader
|
|
3
|
+
*
|
|
4
|
+
* Efficiently loads and caches the chassis-icons.svg sprite using localStorage.
|
|
5
|
+
* This reduces page size by avoiding inline SVG and enables browser caching.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - localStorage caching with version control
|
|
9
|
+
* - Automatic fallback on cache miss or corruption
|
|
10
|
+
* - Network request deduplication
|
|
11
|
+
* - Small footprint and fast execution
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
;(function () {
|
|
15
|
+
'use strict'
|
|
16
|
+
|
|
17
|
+
// Configuration
|
|
18
|
+
const CACHE_KEY = 'chassis-icons-sprite'
|
|
19
|
+
const VERSION_KEY = 'chassis-icons-version'
|
|
20
|
+
const CURRENT_VERSION = '1.0.0' // Update when icons change
|
|
21
|
+
const SVG_URL = '/static/icons/chassis-icons.svg' // Icons SVG sprite path
|
|
22
|
+
|
|
23
|
+
// State management
|
|
24
|
+
let loadPromise = null
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get SVG sprite from localStorage cache
|
|
28
|
+
* @returns {string|null} Cached SVG content or null if not found/invalid
|
|
29
|
+
*/
|
|
30
|
+
function getCachedSprite() {
|
|
31
|
+
try {
|
|
32
|
+
const cachedVersion = localStorage.getItem(VERSION_KEY)
|
|
33
|
+
const cachedSprite = localStorage.getItem(CACHE_KEY)
|
|
34
|
+
|
|
35
|
+
if (cachedVersion === CURRENT_VERSION && cachedSprite) {
|
|
36
|
+
return cachedSprite
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.warn('Failed to read icon cache:', error)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Store SVG sprite in localStorage cache
|
|
47
|
+
* @param {string} spriteContent - SVG content to cache
|
|
48
|
+
*/
|
|
49
|
+
function setCachedSprite(spriteContent) {
|
|
50
|
+
try {
|
|
51
|
+
localStorage.setItem(VERSION_KEY, CURRENT_VERSION)
|
|
52
|
+
localStorage.setItem(CACHE_KEY, spriteContent)
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.warn('Failed to cache icon sprite:', error)
|
|
55
|
+
// Handle localStorage quota exceeded or other errors
|
|
56
|
+
// Clear old cache and try again
|
|
57
|
+
try {
|
|
58
|
+
localStorage.removeItem(CACHE_KEY)
|
|
59
|
+
localStorage.removeItem(VERSION_KEY)
|
|
60
|
+
localStorage.setItem(VERSION_KEY, CURRENT_VERSION)
|
|
61
|
+
localStorage.setItem(CACHE_KEY, spriteContent)
|
|
62
|
+
} catch (retryError) {
|
|
63
|
+
console.warn('Failed to cache icon sprite after retry:', retryError)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Fetch SVG sprite from network
|
|
70
|
+
* @returns {Promise<string>} Promise resolving to SVG content
|
|
71
|
+
*/
|
|
72
|
+
async function fetchSprite() {
|
|
73
|
+
const response = await fetch(SVG_URL)
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`Failed to fetch icon sprite: ${response.status} ${response.statusText}`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const spriteContent = await response.text()
|
|
80
|
+
|
|
81
|
+
// Basic validation - ensure it's actually SVG content
|
|
82
|
+
if (!spriteContent.includes('<svg') || !spriteContent.includes('</svg>')) {
|
|
83
|
+
throw new Error('Invalid SVG content received')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return spriteContent
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Inject SVG sprite into the DOM
|
|
91
|
+
* @param {string} spriteContent - SVG content to inject
|
|
92
|
+
*/
|
|
93
|
+
function injectSprite(spriteContent) {
|
|
94
|
+
// Remove any existing sprite container
|
|
95
|
+
const existingContainer = document.getElementById('chassis-icons-sprite')
|
|
96
|
+
if (existingContainer) {
|
|
97
|
+
existingContainer.remove()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create hidden container for the sprite
|
|
101
|
+
const container = document.createElement('div')
|
|
102
|
+
container.id = 'chassis-icons-sprite'
|
|
103
|
+
container.style.display = 'none'
|
|
104
|
+
container.setAttribute('aria-hidden', 'true')
|
|
105
|
+
container.innerHTML = spriteContent
|
|
106
|
+
|
|
107
|
+
// Insert at the beginning of body
|
|
108
|
+
document.body.insertBefore(container, document.body.firstChild)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Load and inject the icon sprite
|
|
113
|
+
* @returns {Promise<void>} Promise that resolves when sprite is loaded
|
|
114
|
+
*/
|
|
115
|
+
function loadIconSprite() {
|
|
116
|
+
// Return existing promise if already loading
|
|
117
|
+
if (loadPromise) {
|
|
118
|
+
return loadPromise
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check cache first
|
|
122
|
+
const cachedSprite = getCachedSprite()
|
|
123
|
+
if (cachedSprite) {
|
|
124
|
+
injectSprite(cachedSprite)
|
|
125
|
+
return Promise.resolve()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Fetch from network
|
|
129
|
+
loadPromise = fetchSprite()
|
|
130
|
+
.then((spriteContent) => {
|
|
131
|
+
// Cache the content
|
|
132
|
+
setCachedSprite(spriteContent)
|
|
133
|
+
|
|
134
|
+
// Inject into DOM
|
|
135
|
+
injectSprite(spriteContent)
|
|
136
|
+
})
|
|
137
|
+
.catch((error) => {
|
|
138
|
+
console.error('Failed to load icon sprite:', error)
|
|
139
|
+
// Don't throw - let the page continue to work without icons
|
|
140
|
+
})
|
|
141
|
+
.finally(() => {
|
|
142
|
+
loadPromise = null
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
return loadPromise
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Initialize icon loading
|
|
150
|
+
* Runs immediately if DOM is ready, or waits for DOMContentLoaded
|
|
151
|
+
*/
|
|
152
|
+
function init() {
|
|
153
|
+
if (document.readyState === 'loading') {
|
|
154
|
+
document.addEventListener('DOMContentLoaded', loadIconSprite)
|
|
155
|
+
} else {
|
|
156
|
+
loadIconSprite()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Public API for manual cache management
|
|
161
|
+
window.ChassisIcons = {
|
|
162
|
+
/**
|
|
163
|
+
* Manually reload the icon sprite (bypasses cache)
|
|
164
|
+
* @returns {Promise<void>}
|
|
165
|
+
*/
|
|
166
|
+
reload: async function () {
|
|
167
|
+
try {
|
|
168
|
+
// Clear cache
|
|
169
|
+
localStorage.removeItem(CACHE_KEY)
|
|
170
|
+
localStorage.removeItem(VERSION_KEY)
|
|
171
|
+
|
|
172
|
+
// Force reload
|
|
173
|
+
loadPromise = null
|
|
174
|
+
await loadIconSprite()
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('Failed to reload icon sprite:', error)
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Clear the icon cache
|
|
182
|
+
*/
|
|
183
|
+
clearCache: function () {
|
|
184
|
+
try {
|
|
185
|
+
localStorage.removeItem(CACHE_KEY)
|
|
186
|
+
localStorage.removeItem(VERSION_KEY)
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.warn('Failed to clear icon cache:', error)
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get cache information
|
|
194
|
+
* @returns {object} Cache status and size info
|
|
195
|
+
*/
|
|
196
|
+
getCacheInfo: function () {
|
|
197
|
+
try {
|
|
198
|
+
const version = localStorage.getItem(VERSION_KEY)
|
|
199
|
+
const sprite = localStorage.getItem(CACHE_KEY)
|
|
200
|
+
return {
|
|
201
|
+
version: version,
|
|
202
|
+
cached: !!sprite,
|
|
203
|
+
size: sprite ? sprite.length : 0,
|
|
204
|
+
currentVersion: CURRENT_VERSION
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
version: null,
|
|
209
|
+
cached: false,
|
|
210
|
+
size: 0,
|
|
211
|
+
currentVersion: CURRENT_VERSION,
|
|
212
|
+
error: error.message
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Initialize
|
|
219
|
+
init()
|
|
220
|
+
})()
|
package/src/js/search.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
|
|
2
|
+
// IT'S ALL JUST JUNK FOR OUR DOCS!
|
|
3
|
+
// ++++++++++++++++++++++++++++++++++++++++++
|
|
4
|
+
|
|
5
|
+
/*!
|
|
6
|
+
* JavaScript for Chassis's docs (https://chassis-ui.com/)
|
|
7
|
+
* Copyright 2024-2025 The Chassis Authors
|
|
8
|
+
* Licensed under the Creative Commons Attribution 3.0 Unported License.
|
|
9
|
+
* For details, see https://creativecommons.org/licenses/by/3.0/.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import docsearch from '@docsearch/js'
|
|
13
|
+
;(() => {
|
|
14
|
+
// These values will be replaced by Astro's Vite plugin
|
|
15
|
+
const CONFIG = {
|
|
16
|
+
apiKey: '__API_KEY__',
|
|
17
|
+
indexName: '__INDEX_NAME__',
|
|
18
|
+
appId: '__APP_ID__'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const searchElement = document.getElementById('docsearch')
|
|
22
|
+
|
|
23
|
+
if (!searchElement) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const siteDocsVersion = searchElement.getAttribute('data-cxd-docs-version')
|
|
28
|
+
|
|
29
|
+
docsearch({
|
|
30
|
+
apiKey: CONFIG.apiKey,
|
|
31
|
+
indexName: CONFIG.indexName,
|
|
32
|
+
appId: CONFIG.appId,
|
|
33
|
+
container: searchElement,
|
|
34
|
+
searchParameters: {
|
|
35
|
+
facetFilters: [`version:${siteDocsVersion}`]
|
|
36
|
+
},
|
|
37
|
+
transformItems(items) {
|
|
38
|
+
return items.map((item) => {
|
|
39
|
+
const liveUrl = 'https://chassis-ui.com/'
|
|
40
|
+
|
|
41
|
+
item.url = window.location.origin.startsWith(liveUrl)
|
|
42
|
+
? // On production, return the result as is
|
|
43
|
+
item.url
|
|
44
|
+
: // On development or Netlify, replace `item.url` with a trailing slash,
|
|
45
|
+
// so that the result link is relative to the server root
|
|
46
|
+
item.url.replace(liveUrl, '/')
|
|
47
|
+
|
|
48
|
+
// Prevent jumping to first header
|
|
49
|
+
if (item.anchor === 'content') {
|
|
50
|
+
item.url = item.url.replace(/#content$/, '')
|
|
51
|
+
item.anchor = null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return item
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
})()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT
|
|
2
|
+
// IT'S ALL JUST JUNK FOR OUR DOCS!
|
|
3
|
+
// ++++++++++++++++++++++++++++++++++++++++++
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* JavaScript for Chassis's docs (https://chassis-ui.com/)
|
|
7
|
+
* Copyright 2011-2025 The Chassis Authors
|
|
8
|
+
* Licensed under the Creative Commons Attribution 3.0 Unported License.
|
|
9
|
+
* For details, see https://creativecommons.org/licenses/by/3.0/.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
;(() => {
|
|
13
|
+
// Scroll the active sidebar link into view
|
|
14
|
+
const sidenav = document.querySelector('.cxd-sidebar')
|
|
15
|
+
const sidenavActiveLink = document.querySelector('.cxd-links-nav .active')
|
|
16
|
+
|
|
17
|
+
if (!sidenav || !sidenavActiveLink) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const sidenavHeight = sidenav.clientHeight
|
|
22
|
+
const sidenavActiveLinkTop = sidenavActiveLink.offsetTop
|
|
23
|
+
const sidenavActiveLinkHeight = sidenavActiveLink.clientHeight
|
|
24
|
+
const viewportTop = sidenavActiveLinkTop
|
|
25
|
+
const viewportBottom = viewportTop - sidenavHeight + sidenavActiveLinkHeight
|
|
26
|
+
|
|
27
|
+
if (sidenav.scrollTop > viewportTop || sidenav.scrollTop < viewportBottom) {
|
|
28
|
+
sidenav.scrollTop = viewportTop - sidenavHeight / 2 + sidenavActiveLinkHeight / 2
|
|
29
|
+
}
|
|
30
|
+
})()
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Dynamic theme color based on scroll position
|
|
2
|
+
;(() => {
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
// Configuration
|
|
6
|
+
const CONFIG = {
|
|
7
|
+
scrollThreshold: 100,
|
|
8
|
+
defaultColor: '#00A4CC'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// DOM elements
|
|
12
|
+
const elements = {
|
|
13
|
+
themeColorMeta: document.querySelector('meta[name="theme-color"]'),
|
|
14
|
+
header: document.querySelector('header'),
|
|
15
|
+
footer: document.querySelector('.cxd-footer')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Early return if required elements are missing
|
|
19
|
+
if (!elements.themeColorMeta || !elements.header || !elements.footer) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extracts RGB values from a CSS color string
|
|
25
|
+
* @param {string} color - CSS color string (rgb/rgba)
|
|
26
|
+
* @returns {Object|null} RGB object or null if invalid
|
|
27
|
+
*/
|
|
28
|
+
function extractRGB(color) {
|
|
29
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/)
|
|
30
|
+
return match
|
|
31
|
+
? {
|
|
32
|
+
r: parseInt(match[1], 10),
|
|
33
|
+
g: parseInt(match[2], 10),
|
|
34
|
+
b: parseInt(match[3], 10)
|
|
35
|
+
}
|
|
36
|
+
: null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Converts RGB values to hex color string
|
|
41
|
+
* @param {number} r - Red value (0-255)
|
|
42
|
+
* @param {number} g - Green value (0-255)
|
|
43
|
+
* @param {number} b - Blue value (0-255)
|
|
44
|
+
* @returns {string} Hex color string
|
|
45
|
+
*/
|
|
46
|
+
function rgbToHex(r, g, b) {
|
|
47
|
+
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Gets the computed background color of an element as hex
|
|
52
|
+
* @param {HTMLElement} element - DOM element
|
|
53
|
+
* @returns {string} Hex color string or default color
|
|
54
|
+
*/
|
|
55
|
+
function getElementBackgroundColor(element) {
|
|
56
|
+
const bgColor = getComputedStyle(element).backgroundColor
|
|
57
|
+
const rgb = extractRGB(bgColor)
|
|
58
|
+
return rgb ? rgbToHex(rgb.r, rgb.g, rgb.b) : CONFIG.defaultColor
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Determines the current scroll position context
|
|
63
|
+
* @returns {string} 'top', 'bottom', or 'middle'
|
|
64
|
+
*/
|
|
65
|
+
function getScrollContext() {
|
|
66
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
|
67
|
+
const windowHeight = window.innerHeight
|
|
68
|
+
const documentHeight = document.documentElement.scrollHeight
|
|
69
|
+
|
|
70
|
+
if (scrollTop < CONFIG.scrollThreshold) {
|
|
71
|
+
return 'top'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (scrollTop + windowHeight >= documentHeight - CONFIG.scrollThreshold) {
|
|
75
|
+
return 'bottom'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return 'middle'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Updates the theme color meta tag based on scroll position
|
|
83
|
+
*/
|
|
84
|
+
function updateThemeColor() {
|
|
85
|
+
const context = getScrollContext()
|
|
86
|
+
let color = CONFIG.defaultColor
|
|
87
|
+
|
|
88
|
+
switch (context) {
|
|
89
|
+
case 'top':
|
|
90
|
+
color = getElementBackgroundColor(elements.header)
|
|
91
|
+
break
|
|
92
|
+
case 'bottom':
|
|
93
|
+
color = getElementBackgroundColor(elements.footer)
|
|
94
|
+
break
|
|
95
|
+
case 'middle':
|
|
96
|
+
default:
|
|
97
|
+
color = CONFIG.defaultColor
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
elements.themeColorMeta.setAttribute('content', color)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Throttle function for performance optimization
|
|
105
|
+
function throttle(func, limit) {
|
|
106
|
+
let inThrottle
|
|
107
|
+
return function () {
|
|
108
|
+
const args = arguments
|
|
109
|
+
const context = this
|
|
110
|
+
if (!inThrottle) {
|
|
111
|
+
func.apply(context, args)
|
|
112
|
+
inThrottle = true
|
|
113
|
+
setTimeout(() => (inThrottle = false), limit)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Event listeners with throttling for better performance
|
|
119
|
+
const throttledUpdate = throttle(updateThemeColor, 16) // ~60fps
|
|
120
|
+
|
|
121
|
+
window.addEventListener('scroll', throttledUpdate, { passive: true })
|
|
122
|
+
window.addEventListener('resize', throttledUpdate, { passive: true })
|
|
123
|
+
|
|
124
|
+
// Initial update
|
|
125
|
+
updateThemeColor()
|
|
126
|
+
})()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { CollectionEntry } from 'astro:content'
|
|
3
|
+
import { getConfig } from '@libs/config'
|
|
4
|
+
import type { Layout, LayoutOverridesHTMLAttributes } from '../libs/layout'
|
|
5
|
+
import Head from './head/Head.astro'
|
|
6
|
+
import Header from './header/Header.astro'
|
|
7
|
+
import Scripts from './footer/Scripts.astro'
|
|
8
|
+
import Footer from './footer/Footer.astro'
|
|
9
|
+
import { stripMarkdown } from '../libs/utils'
|
|
10
|
+
|
|
11
|
+
// The following props can be directly passed to the base layout component from any page or layout extending it,
|
|
12
|
+
// e.g. <BaseLayout layout="docs" robots="noindex" />.
|
|
13
|
+
type Props = {
|
|
14
|
+
// A specific layout to use for the current page used to determine if some components should be rendered or not.
|
|
15
|
+
// Available layouts are defined in `src/libs/layout.ts`.
|
|
16
|
+
layout?: Layout
|
|
17
|
+
// An object containing HTML attributes that can be overridden for some HTML elements used in the base layout keyed by
|
|
18
|
+
// HTML element names.
|
|
19
|
+
overrides?: {
|
|
20
|
+
body?: LayoutOverridesHTMLAttributes<'body'>
|
|
21
|
+
// Note that main can also be overridden by the Astro slot named "main" and that the slot will take precedence over
|
|
22
|
+
// any override.
|
|
23
|
+
main?: LayoutOverridesHTMLAttributes<'main'>
|
|
24
|
+
}
|
|
25
|
+
// A string containing the robots meta tag content. If not set, the tag will not be rendered.
|
|
26
|
+
robots?: string
|
|
27
|
+
// An override for the page title. If not defined, the title will either be the content of the `title` frontmatter
|
|
28
|
+
// property when rendering a markdown page or default back to the one defined in the `config.yml` file.
|
|
29
|
+
title?: string
|
|
30
|
+
} & MarkdownProps
|
|
31
|
+
|
|
32
|
+
// The following props are automatically set by Astro (if defined) based on the markdown frontmatter when rendering a
|
|
33
|
+
// markdown page. They can be accessed through the `Astro.props.frontmatter` object but note that they won't be set when
|
|
34
|
+
// not rendering a markdown page.
|
|
35
|
+
type MarkdownProps = { frontmatter?: Partial<CollectionEntry<'docs' | 'blog'>['data']> }
|
|
36
|
+
|
|
37
|
+
const { frontmatter, layout, overrides, robots } = Astro.props
|
|
38
|
+
|
|
39
|
+
const title = Astro.props.title ?? frontmatter?.title ?? getConfig().title
|
|
40
|
+
const description = frontmatter?.description
|
|
41
|
+
? stripMarkdown(frontmatter.description)
|
|
42
|
+
: getConfig().description
|
|
43
|
+
const thumbnail = frontmatter?.thumbnail
|
|
44
|
+
? `img/${frontmatter.thumbnail}`
|
|
45
|
+
: 'logo/chassis-logo-default.svg'
|
|
46
|
+
|
|
47
|
+
const bodyProps = overrides?.body ?? {}
|
|
48
|
+
const mainProps = overrides?.main ?? {}
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
<!doctype html>
|
|
52
|
+
<html lang="en" data-cx-theme="auto">
|
|
53
|
+
<head>
|
|
54
|
+
<Head
|
|
55
|
+
description={description}
|
|
56
|
+
layout={layout}
|
|
57
|
+
robots={robots}
|
|
58
|
+
thumbnail={thumbnail}
|
|
59
|
+
title={title}
|
|
60
|
+
/>
|
|
61
|
+
</head>
|
|
62
|
+
<body {...bodyProps}>
|
|
63
|
+
<Header layout={layout} title={title} />
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
Astro.slots.has('main') ? (
|
|
67
|
+
<slot name="main" />
|
|
68
|
+
) : (
|
|
69
|
+
<main {...mainProps}>
|
|
70
|
+
<slot />
|
|
71
|
+
</main>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
<Footer />
|
|
76
|
+
|
|
77
|
+
<Scripts layout={layout}>
|
|
78
|
+
<slot name="scripts" slot="scripts" />
|
|
79
|
+
</Scripts>
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
frontmatter?.extra_js &&
|
|
83
|
+
frontmatter.extra_js.map((js) => <script is:inline async={js.async} src={js.src} />)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
(layout === 'docs' || layout === 'icons') && (
|
|
88
|
+
<div class="position-fixed" aria-hidden="true">
|
|
89
|
+
<input type="text" tabindex="-1" />
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
</body>
|
|
94
|
+
</html>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { MarkdownHeading } from 'astro'
|
|
3
|
+
import type { CollectionEntry } from 'astro:content'
|
|
4
|
+
import { getConfig } from '@libs/config'
|
|
5
|
+
import { getChassisDocsPath } from '@libs/path'
|
|
6
|
+
import type { LayoutOverridesHTMLAttributes } from '../libs/layout'
|
|
7
|
+
import { getSlug, processMarkdownToHtml } from '../libs/utils'
|
|
8
|
+
import BaseLayout from './BaseLayout.astro'
|
|
9
|
+
import DocsSidebar from '../components/DocsSidebar.astro'
|
|
10
|
+
import TableOfContents from '../components/TableOfContents.astro'
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
frontmatter: CollectionEntry<'docs'>['data']
|
|
14
|
+
headings?: MarkdownHeading[]
|
|
15
|
+
id: CollectionEntry<'docs'>['id']
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { frontmatter, headings, id } = Astro.props
|
|
19
|
+
|
|
20
|
+
// Extract the directory/section from the ID (format: "directory/filename.mdx")
|
|
21
|
+
const parentDirectory = id.includes('/') ? id.split('/')[0] : ''
|
|
22
|
+
|
|
23
|
+
const bodyProps: LayoutOverridesHTMLAttributes<'body'> = {}
|
|
24
|
+
|
|
25
|
+
if (frontmatter.toc) {
|
|
26
|
+
bodyProps['data-cx-spy'] = 'scroll'
|
|
27
|
+
bodyProps['data-cx-target'] = '#TableOfContents'
|
|
28
|
+
}
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<BaseLayout {...Astro.props} layout="docs" overrides={{ body: bodyProps }}>
|
|
32
|
+
<div slot="main" class="container-fluid cxd-gutter cxd-layout">
|
|
33
|
+
<aside class="cxd-sidebar">
|
|
34
|
+
<div
|
|
35
|
+
class="offcanvas-large offcanvas-start"
|
|
36
|
+
tabindex="-1"
|
|
37
|
+
id="cxdSidebar"
|
|
38
|
+
aria-labelledby="cxdSidebarOffcanvasLabel"
|
|
39
|
+
>
|
|
40
|
+
<div class="offcanvas-header border-bottom">
|
|
41
|
+
<h5 class="offcanvas-title" id="cxdSidebarOffcanvasLabel">Browse docs</h5>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
class="close-button"
|
|
45
|
+
data-cx-dismiss="offcanvas"
|
|
46
|
+
aria-label="Close"
|
|
47
|
+
data-cx-target="#cxdSidebar"></button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="offcanvas-body">
|
|
51
|
+
<DocsSidebar />
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</aside>
|
|
55
|
+
|
|
56
|
+
<main class="cxd-main">
|
|
57
|
+
<div class="cxd-intro">
|
|
58
|
+
<div
|
|
59
|
+
class="d-medium-flex flex-medium-row-reverse justify-content-between align-items-start"
|
|
60
|
+
>
|
|
61
|
+
<div class="mb-medium mb-medium-0 d-flex text-nowrap">
|
|
62
|
+
{
|
|
63
|
+
// This is needed because we want to show the badge if show_badge isn't present or is set to false
|
|
64
|
+
frontmatter.added &&
|
|
65
|
+
((frontmatter.added.show_badge !== undefined &&
|
|
66
|
+
frontmatter.added.show_badge === true) ||
|
|
67
|
+
frontmatter.added.show_badge === undefined) && (
|
|
68
|
+
<span class="badge success smooth rounded-medium me-xsmall">
|
|
69
|
+
Added in v{frontmatter.added.version}
|
|
70
|
+
</span>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
<a
|
|
74
|
+
class="button default small"
|
|
75
|
+
href={`${getConfig().repo}/blob/${id}.mdx`}
|
|
76
|
+
title="View and edit this file on GitHub"
|
|
77
|
+
target="_blank"
|
|
78
|
+
rel="noopener"
|
|
79
|
+
>
|
|
80
|
+
View on GitHub
|
|
81
|
+
</a>
|
|
82
|
+
</div>
|
|
83
|
+
<h1 class="cxd-title mb-0" id="content">{frontmatter.title}</h1>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="cxd-subtitle">
|
|
86
|
+
{
|
|
87
|
+
frontmatter.description && (
|
|
88
|
+
<Fragment set:html={processMarkdownToHtml(frontmatter.description)} />
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
frontmatter.toc && headings && (
|
|
96
|
+
<div class="cxd-toc">
|
|
97
|
+
<button
|
|
98
|
+
class="button default mb-large text-decoration-none cxd-toc-toggle d-medium-none"
|
|
99
|
+
type="button"
|
|
100
|
+
data-cx-toggle="collapse"
|
|
101
|
+
data-cx-target="#tocContents"
|
|
102
|
+
aria-expanded="false"
|
|
103
|
+
aria-controls="tocContents"
|
|
104
|
+
>
|
|
105
|
+
On this page
|
|
106
|
+
<svg class="icon icon-small" aria-hidden="true">
|
|
107
|
+
<use xlink:href="#chevron-sort-solid" />
|
|
108
|
+
</svg>
|
|
109
|
+
</button>
|
|
110
|
+
<strong class="d-none d-medium-block h6 my-xsmall">On this page</strong>
|
|
111
|
+
<hr class="d-none d-medium-block my-xsmall" />
|
|
112
|
+
<div class="collapse cxd-toc-collapse" id="tocContents">
|
|
113
|
+
<nav id="TableOfContents">
|
|
114
|
+
<TableOfContents headings={headings} config={getConfig()} />
|
|
115
|
+
</nav>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
<div class="cxd-content">
|
|
122
|
+
{
|
|
123
|
+
frontmatter.sections && (
|
|
124
|
+
<div class="row g-xlarge">
|
|
125
|
+
{frontmatter.sections.map((section) => (
|
|
126
|
+
<div class="col-medium-6">
|
|
127
|
+
<a
|
|
128
|
+
class="d-block text-decoration-none"
|
|
129
|
+
href={getChassisDocsPath(
|
|
130
|
+
`${parentDirectory ? parentDirectory + '/' : ''}${getSlug(section.title)}/`
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
<strong class="d-block h5 mb-2xsmall">{section.title}</strong>
|
|
134
|
+
<span class="fg-subtle">{section.description}</span>
|
|
135
|
+
</a>
|
|
136
|
+
</div>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
<slot />
|
|
143
|
+
</div>
|
|
144
|
+
</main>
|
|
145
|
+
</div>
|
|
146
|
+
<slot name="scripts" slot="scripts" />
|
|
147
|
+
</BaseLayout>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { LayoutOverridesHTMLAttributes } from '../libs/layout'
|
|
3
|
+
import BaseLayout from './BaseLayout.astro'
|
|
4
|
+
|
|
5
|
+
const bodyProps: LayoutOverridesHTMLAttributes<'body'> = {}
|
|
6
|
+
|
|
7
|
+
bodyProps['id'] = 'icons-body'
|
|
8
|
+
bodyProps['data-cx-spy'] = 'scroll'
|
|
9
|
+
bodyProps['data-cx-target'] = '#icons-nav'
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<BaseLayout {...Astro.props} layout="icons" overrides={{ body: bodyProps }}>
|
|
13
|
+
<div
|
|
14
|
+
slot="main"
|
|
15
|
+
class="container-2xlarge px-medium px-medium-4xlarge cxd-gutter icon-docs icon-reset pt-medium-large pb-medium-large"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</div>
|
|
19
|
+
</BaseLayout>
|