-
Notifications
You must be signed in to change notification settings - Fork 0
Implement light/dark theme toggle with CSS tokens and chart theming #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
bed22cd
Merge pull request #83 from alvin000009238/main
alvin000009238 a2ef854
feat: add accessible light theme toggle
alvin000009238 8043951
Merge pull request #84 from alvin000009238/codex/add-light-mode-and-t…
alvin000009238 615b603
fix: prevent header action text clipping in light theme
alvin000009238 b5e66f6
Merge branch 'dev' into codex/add-light-mode-and-theme-toggle-harb4d
alvin000009238 8ec25fa
Merge pull request #85 from alvin000009238/codex/add-light-mode-and-t…
alvin000009238 61ddf8f
fix: compact header actions to reduce space usage
alvin000009238 ea82ecd
Merge branch 'dev' into codex/add-light-mode-and-theme-toggle-ggawno
alvin000009238 176f982
Merge pull request #86 from alvin000009238/codex/add-light-mode-and-t…
alvin000009238 a8bd092
fix: improve chart text contrast across theme changes
alvin000009238 1a786e6
Merge branch 'dev' into codex/add-light-mode-and-theme-toggle-zya5rs
alvin000009238 d49917d
Merge pull request #87 from alvin000009238/codex/add-light-mode-and-t…
alvin000009238 5c2e031
fix: use manual dark default theme and md3 toggle icons
alvin000009238 285e939
Merge branch 'dev' into codex/add-light-mode-and-theme-toggle-p94oog
alvin000009238 a288985
Merge pull request #88 from alvin000009238/codex/add-light-mode-and-t…
alvin000009238 45abbf6
fix: swap theme toggle to md3-style svg icons
alvin000009238 b666c85
Merge branch 'dev' into codex/add-light-mode-and-theme-toggle-efl5ta
alvin000009238 52bc78a
Merge pull request #89 from alvin000009238/codex/add-light-mode-and-t…
alvin000009238 2e9d751
Update light and dark mode toggle SVG icons
alvin000009238 aa30858
Merge pull request #90 from alvin000009238/codex/replace-light-and-da…
alvin000009238 974f03f
Fix typo in heading from 'vs' to 'vs.'
alvin000009238 5099d53
Fix theme init CSP and improve theme/chart test coverage
alvin000009238 ede308a
Merge pull request #92 from alvin000009238/codex/fix-csp-issue-with-i…
alvin000009238 4f84465
Fix chart palette staleness and stabilize theme tests
alvin000009238 007dfab
Merge branch 'dev' into codex/fix-csp-issue-with-inline-theme-init-sc…
alvin000009238 6f9ee92
Merge pull request #93 from alvin000009238/codex/fix-csp-issue-with-i…
alvin000009238 d36cb9b
Harden early theme init against storage access failures
alvin000009238 1c75fec
Merge branch 'dev' into codex/fix-csp-issue-with-inline-theme-init-sc…
alvin000009238 8c9a35f
Merge pull request #95 from alvin000009238/codex/fix-csp-issue-with-i…
alvin000009238 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,20 +12,31 @@ let radarChartInstance = null; | |||||||||||||||||||||||||||||||
| let barChartInstance = null; | ||||||||||||||||||||||||||||||||
| let chartJsLoadPromise = null; | ||||||||||||||||||||||||||||||||
| let latestRenderToken = 0; | ||||||||||||||||||||||||||||||||
| let cachedChartPalette = null; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (typeof document !== 'undefined') { | ||||||||||||||||||||||||||||||||
| document.addEventListener('themechange', () => { | ||||||||||||||||||||||||||||||||
| cachedChartPalette = null; | ||||||||||||||||||||||||||||||||
| applyThemeToExistingCharts(); | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export function generateCharts(subjects) { | ||||||||||||||||||||||||||||||||
| if (!Array.isArray(subjects) || subjects.length === 0) return; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const labels = subjects.map((subject) => shortenName(subject.SubjectName)); | ||||||||||||||||||||||||||||||||
| const myScores = subjects.map((subject) => subject.scoreValue ?? getNumericScore(subject.ScoreDisplay, subject.Score)); | ||||||||||||||||||||||||||||||||
| const avgScores = subjects.map((subject) => subject.classAvgValue ?? getNumericScore(subject.ClassAVGScoreDisplay, subject.ClassAVGScore)); | ||||||||||||||||||||||||||||||||
| const palette = getChartThemePalette(); | ||||||||||||||||||||||||||||||||
| const renderToken = ++latestRenderToken; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ensureChartJsLoaded() | ||||||||||||||||||||||||||||||||
| .then((ChartCtor) => { | ||||||||||||||||||||||||||||||||
| if (renderToken !== latestRenderToken) return; | ||||||||||||||||||||||||||||||||
| updateRadarChart(ChartCtor, labels, myScores, avgScores); | ||||||||||||||||||||||||||||||||
| updateBarChart(ChartCtor, labels, myScores, avgScores); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const palette = getCachedChartThemePalette(); | ||||||||||||||||||||||||||||||||
| updateRadarChart(ChartCtor, labels, myScores, avgScores, palette); | ||||||||||||||||||||||||||||||||
| updateBarChart(ChartCtor, labels, myScores, avgScores, palette); | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| .catch((error) => { | ||||||||||||||||||||||||||||||||
| console.warn('Failed to load Chart.js', error); | ||||||||||||||||||||||||||||||||
|
|
@@ -98,7 +109,7 @@ function ensureChartJsLoaded() { | |||||||||||||||||||||||||||||||
| return chartJsLoadPromise; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| function updateRadarChart(ChartCtor, labels, myScores, avgScores) { | ||||||||||||||||||||||||||||||||
| function updateRadarChart(ChartCtor, labels, myScores, avgScores, palette) { | ||||||||||||||||||||||||||||||||
| const radarCanvas = document.getElementById('radarChart'); | ||||||||||||||||||||||||||||||||
| const radarCtx = radarCanvas?.getContext('2d'); | ||||||||||||||||||||||||||||||||
| if (!radarCtx) return; | ||||||||||||||||||||||||||||||||
|
|
@@ -116,8 +127,8 @@ function updateRadarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| borderColor: '#6366f1', | ||||||||||||||||||||||||||||||||
| borderWidth: 2, | ||||||||||||||||||||||||||||||||
| pointBackgroundColor: '#6366f1', | ||||||||||||||||||||||||||||||||
| pointBorderColor: '#fff', | ||||||||||||||||||||||||||||||||
| pointHoverBackgroundColor: '#fff', | ||||||||||||||||||||||||||||||||
| pointBorderColor: palette.surface, | ||||||||||||||||||||||||||||||||
| pointHoverBackgroundColor: palette.surface, | ||||||||||||||||||||||||||||||||
| pointHoverBorderColor: '#6366f1' | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
|
|
@@ -127,8 +138,8 @@ function updateRadarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| borderColor: '#10b981', | ||||||||||||||||||||||||||||||||
| borderWidth: 2, | ||||||||||||||||||||||||||||||||
| pointBackgroundColor: '#10b981', | ||||||||||||||||||||||||||||||||
| pointBorderColor: '#fff', | ||||||||||||||||||||||||||||||||
| pointHoverBackgroundColor: '#fff', | ||||||||||||||||||||||||||||||||
| pointBorderColor: palette.surface, | ||||||||||||||||||||||||||||||||
| pointHoverBackgroundColor: palette.surface, | ||||||||||||||||||||||||||||||||
| pointHoverBorderColor: '#10b981' | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||
|
|
@@ -142,17 +153,17 @@ function updateRadarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| max: 100, | ||||||||||||||||||||||||||||||||
| ticks: { | ||||||||||||||||||||||||||||||||
| stepSize: 20, | ||||||||||||||||||||||||||||||||
| color: '#94a3b8', | ||||||||||||||||||||||||||||||||
| color: palette.textMuted, | ||||||||||||||||||||||||||||||||
| backdropColor: 'transparent' | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| grid: { | ||||||||||||||||||||||||||||||||
| color: 'rgba(148, 163, 184, 0.2)' | ||||||||||||||||||||||||||||||||
| color: palette.grid | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| angleLines: { | ||||||||||||||||||||||||||||||||
| color: 'rgba(148, 163, 184, 0.2)' | ||||||||||||||||||||||||||||||||
| color: palette.grid | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| pointLabels: { | ||||||||||||||||||||||||||||||||
| color: '#f8fafc', | ||||||||||||||||||||||||||||||||
| color: palette.textSecondary, | ||||||||||||||||||||||||||||||||
| font: { | ||||||||||||||||||||||||||||||||
| size: 12 | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
@@ -163,7 +174,7 @@ function updateRadarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| legend: { | ||||||||||||||||||||||||||||||||
| position: 'bottom', | ||||||||||||||||||||||||||||||||
| labels: { | ||||||||||||||||||||||||||||||||
| color: '#f8fafc', | ||||||||||||||||||||||||||||||||
| color: palette.textMain, | ||||||||||||||||||||||||||||||||
| padding: 20, | ||||||||||||||||||||||||||||||||
| font: { | ||||||||||||||||||||||||||||||||
| size: 13 | ||||||||||||||||||||||||||||||||
|
|
@@ -182,7 +193,7 @@ function updateRadarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| radarChartInstance.update('none'); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| function updateBarChart(ChartCtor, labels, myScores, avgScores) { | ||||||||||||||||||||||||||||||||
| function updateBarChart(ChartCtor, labels, myScores, avgScores, palette) { | ||||||||||||||||||||||||||||||||
| const barCanvas = document.getElementById('barChart'); | ||||||||||||||||||||||||||||||||
| const barCtx = barCanvas?.getContext('2d'); | ||||||||||||||||||||||||||||||||
| if (!barCtx) return; | ||||||||||||||||||||||||||||||||
|
|
@@ -217,28 +228,28 @@ function updateBarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| scales: { | ||||||||||||||||||||||||||||||||
| x: { | ||||||||||||||||||||||||||||||||
| grid: { | ||||||||||||||||||||||||||||||||
| color: 'rgba(148, 163, 184, 0.1)' | ||||||||||||||||||||||||||||||||
| color: palette.gridSubtle | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| ticks: { | ||||||||||||||||||||||||||||||||
| color: '#94a3b8' | ||||||||||||||||||||||||||||||||
| color: palette.textMuted | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| y: { | ||||||||||||||||||||||||||||||||
| beginAtZero: true, | ||||||||||||||||||||||||||||||||
| max: 100, | ||||||||||||||||||||||||||||||||
| grid: { | ||||||||||||||||||||||||||||||||
| color: 'rgba(148, 163, 184, 0.1)' | ||||||||||||||||||||||||||||||||
| color: palette.gridSubtle | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| ticks: { | ||||||||||||||||||||||||||||||||
| color: '#94a3b8' | ||||||||||||||||||||||||||||||||
| color: palette.textMuted | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| plugins: { | ||||||||||||||||||||||||||||||||
| legend: { | ||||||||||||||||||||||||||||||||
| position: 'bottom', | ||||||||||||||||||||||||||||||||
| labels: { | ||||||||||||||||||||||||||||||||
| color: '#f8fafc', | ||||||||||||||||||||||||||||||||
| color: palette.textMain, | ||||||||||||||||||||||||||||||||
| padding: 20, | ||||||||||||||||||||||||||||||||
| font: { | ||||||||||||||||||||||||||||||||
| size: 13 | ||||||||||||||||||||||||||||||||
|
|
@@ -257,6 +268,81 @@ function updateBarChart(ChartCtor, labels, myScores, avgScores) { | |||||||||||||||||||||||||||||||
| barChartInstance.update('none'); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| function getChartThemePalette() { | ||||||||||||||||||||||||||||||||
| const styles = getComputedStyle(document.documentElement); | ||||||||||||||||||||||||||||||||
| const readVar = (name, fallback) => { | ||||||||||||||||||||||||||||||||
| const value = styles.getPropertyValue(name).trim(); | ||||||||||||||||||||||||||||||||
| return value || fallback; | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||
| textMain: readVar('--color-text-main', '#0f172a'), | ||||||||||||||||||||||||||||||||
| textSecondary: readVar('--color-text-secondary', '#334155'), | ||||||||||||||||||||||||||||||||
| textMuted: readVar('--color-text-muted', '#64748b'), | ||||||||||||||||||||||||||||||||
| surface: readVar('--color-surface-elevated', '#ffffff'), | ||||||||||||||||||||||||||||||||
| grid: readVar('--color-border-subtle', 'rgba(148, 163, 184, 0.22)'), | ||||||||||||||||||||||||||||||||
| gridSubtle: readVar('--color-border-extra-subtle', 'rgba(148, 163, 184, 0.16)') | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| function getCachedChartThemePalette() { | ||||||||||||||||||||||||||||||||
| if (!cachedChartPalette) { | ||||||||||||||||||||||||||||||||
| cachedChartPalette = getChartThemePalette(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return cachedChartPalette; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| function applyThemeToExistingCharts() { | ||||||||||||||||||||||||||||||||
| const palette = getCachedChartThemePalette(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (radarChartInstance) { | ||||||||||||||||||||||||||||||||
| const radarOptions = radarChartInstance.options; | ||||||||||||||||||||||||||||||||
| const radarScale = radarOptions.scales?.r; | ||||||||||||||||||||||||||||||||
| const radarLegendLabels = radarOptions.plugins?.legend?.labels; | ||||||||||||||||||||||||||||||||
| if (radarScale) { | ||||||||||||||||||||||||||||||||
| radarScale.ticks.color = palette.textMuted; | ||||||||||||||||||||||||||||||||
| radarScale.grid.color = palette.grid; | ||||||||||||||||||||||||||||||||
| radarScale.angleLines.color = palette.grid; | ||||||||||||||||||||||||||||||||
| radarScale.pointLabels.color = palette.textSecondary; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if (radarLegendLabels) { | ||||||||||||||||||||||||||||||||
| radarLegendLabels.color = palette.textMain; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| radarChartInstance.data.datasets.forEach((dataset) => { | ||||||||||||||||||||||||||||||||
| dataset.pointBorderColor = palette.surface; | ||||||||||||||||||||||||||||||||
| dataset.pointHoverBackgroundColor = palette.surface; | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| radarChartInstance.update('none'); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (barChartInstance) { | ||||||||||||||||||||||||||||||||
| const barOptions = barChartInstance.options; | ||||||||||||||||||||||||||||||||
| const xScale = barOptions.scales?.x; | ||||||||||||||||||||||||||||||||
| const yScale = barOptions.scales?.y; | ||||||||||||||||||||||||||||||||
| const barLegendLabels = barOptions.plugins?.legend?.labels; | ||||||||||||||||||||||||||||||||
| if (xScale) { | ||||||||||||||||||||||||||||||||
| xScale.grid.color = palette.gridSubtle; | ||||||||||||||||||||||||||||||||
| xScale.ticks.color = palette.textMuted; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if (yScale) { | ||||||||||||||||||||||||||||||||
| yScale.grid.color = palette.gridSubtle; | ||||||||||||||||||||||||||||||||
| yScale.ticks.color = palette.textMuted; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| if (barLegendLabels) { | ||||||||||||||||||||||||||||||||
| barLegendLabels.color = palette.textMain; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| barChartInstance.update('none'); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export function __setChartInstancesForTest(instances = {}) { | ||||||||||||||||||||||||||||||||
| if (Object.hasOwn(instances, 'radar')) radarChartInstance = instances.radar; | ||||||||||||||||||||||||||||||||
| if (Object.hasOwn(instances, 'bar')) barChartInstance = instances.bar; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
Comment on lines
+341
to
+345
|
||||||||||||||||||||||||||||||||
| export function __setChartInstancesForTest(instances = {}) { | |
| if (Object.hasOwn(instances, 'radar')) radarChartInstance = instances.radar; | |
| if (Object.hasOwn(instances, 'bar')) barChartInstance = instances.bar; | |
| } | |
| function __setChartInstancesForTest(instances = {}) { | |
| if (Object.hasOwn(instances, 'radar')) radarChartInstance = instances.radar; | |
| if (Object.hasOwn(instances, 'bar')) barChartInstance = instances.bar; | |
| } | |
| if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'test') { | |
| // Expose test-only hook via a global in test environments without making it a public export. | |
| // This avoids leaking a test helper into the formal production API surface. | |
| // eslint-disable-next-line no-undef | |
| globalThis.__setChartInstancesForTest = __setChartInstancesForTest; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,7 +132,7 @@ | |
|
|
||
| ::selection { | ||
|
|
||
| background: rgba(0, 122, 255, 0.25); | ||
| background: var(--color-selection); | ||
|
|
||
| } | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generateCharts() 內的
const palette = getChartThemePalette();目前沒有被使用,反而每次呼叫都會額外觸發一次 getComputedStyle()(成本不低)。建議移除這個未使用的變數,或如果原意是要在載入 Chart.js 前先快照當下主題,請改成實際使用(例如初始化 cachedChartPalette)。