@furystack/shades-common-components 13.0.1 → 13.2.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/CHANGELOG.md +67 -0
- package/esm/components/app-bar.d.ts.map +1 -1
- package/esm/components/app-bar.js +12 -3
- package/esm/components/app-bar.js.map +1 -1
- package/esm/components/avatar.d.ts.map +1 -1
- package/esm/components/avatar.js +3 -5
- package/esm/components/avatar.js.map +1 -1
- package/esm/components/cache-view.d.ts +19 -27
- package/esm/components/cache-view.d.ts.map +1 -1
- package/esm/components/cache-view.js +2 -20
- package/esm/components/cache-view.js.map +1 -1
- package/esm/components/cache-view.spec.js +44 -0
- package/esm/components/cache-view.spec.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.d.ts +1 -2
- package/esm/components/command-palette/command-palette-input.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-input.js +14 -36
- package/esm/components/command-palette/command-palette-input.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.spec.js +14 -116
- package/esm/components/command-palette/command-palette-input.spec.js.map +1 -1
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +3 -0
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/drawer/index.d.ts.map +1 -1
- package/esm/components/drawer/index.js +4 -0
- package/esm/components/drawer/index.js.map +1 -1
- package/esm/components/drawer/index.spec.js +47 -0
- package/esm/components/drawer/index.spec.js.map +1 -1
- package/esm/components/noty-list.d.ts.map +1 -1
- package/esm/components/noty-list.js +1 -3
- package/esm/components/noty-list.js.map +1 -1
- package/esm/services/css-variable-theme.d.ts +1 -1
- package/esm/services/css-variable-theme.d.ts.map +1 -1
- package/esm/services/css-variable-theme.js +5 -5
- package/esm/services/css-variable-theme.js.map +1 -1
- package/esm/services/css-variable-theme.spec.js +29 -1
- package/esm/services/css-variable-theme.spec.js.map +1 -1
- package/esm/services/layout-service.d.ts +8 -0
- package/esm/services/layout-service.d.ts.map +1 -1
- package/esm/services/layout-service.js +16 -0
- package/esm/services/layout-service.js.map +1 -1
- package/esm/services/layout-service.spec.js +55 -0
- package/esm/services/layout-service.spec.js.map +1 -1
- package/esm/services/theme-provider-service.d.ts +11 -10
- package/esm/services/theme-provider-service.d.ts.map +1 -1
- package/esm/services/theme-provider-service.js +3 -2
- package/esm/services/theme-provider-service.js.map +1 -1
- package/esm/services/theme-provider-service.spec.js +35 -1
- package/esm/services/theme-provider-service.spec.js.map +1 -1
- package/esm/themes/architect-theme.d.ts +1 -0
- package/esm/themes/architect-theme.d.ts.map +1 -1
- package/esm/themes/architect-theme.js +1 -0
- package/esm/themes/architect-theme.js.map +1 -1
- package/esm/themes/auditore-theme.d.ts +1 -0
- package/esm/themes/auditore-theme.d.ts.map +1 -1
- package/esm/themes/auditore-theme.js +1 -0
- package/esm/themes/auditore-theme.js.map +1 -1
- package/esm/themes/black-mesa-theme.d.ts +1 -0
- package/esm/themes/black-mesa-theme.d.ts.map +1 -1
- package/esm/themes/black-mesa-theme.js +1 -0
- package/esm/themes/black-mesa-theme.js.map +1 -1
- package/esm/themes/default-dark-theme.d.ts +1 -0
- package/esm/themes/default-dark-theme.d.ts.map +1 -1
- package/esm/themes/default-dark-theme.js +1 -0
- package/esm/themes/default-dark-theme.js.map +1 -1
- package/esm/themes/default-light-theme.d.ts +1 -0
- package/esm/themes/default-light-theme.d.ts.map +1 -1
- package/esm/themes/default-light-theme.js +1 -0
- package/esm/themes/default-light-theme.js.map +1 -1
- package/esm/themes/dragonborn-theme.d.ts +1 -0
- package/esm/themes/dragonborn-theme.d.ts.map +1 -1
- package/esm/themes/dragonborn-theme.js +1 -0
- package/esm/themes/dragonborn-theme.js.map +1 -1
- package/esm/themes/hawkins-theme.d.ts +1 -0
- package/esm/themes/hawkins-theme.d.ts.map +1 -1
- package/esm/themes/hawkins-theme.js +1 -0
- package/esm/themes/hawkins-theme.js.map +1 -1
- package/esm/themes/jedi-theme.d.ts +1 -0
- package/esm/themes/jedi-theme.d.ts.map +1 -1
- package/esm/themes/jedi-theme.js +1 -0
- package/esm/themes/jedi-theme.js.map +1 -1
- package/esm/themes/neon-runner-theme.d.ts +1 -0
- package/esm/themes/neon-runner-theme.d.ts.map +1 -1
- package/esm/themes/neon-runner-theme.js +1 -0
- package/esm/themes/neon-runner-theme.js.map +1 -1
- package/esm/themes/plumber-theme.d.ts +1 -0
- package/esm/themes/plumber-theme.d.ts.map +1 -1
- package/esm/themes/plumber-theme.js +1 -0
- package/esm/themes/plumber-theme.js.map +1 -1
- package/esm/themes/replicant-theme.d.ts +1 -0
- package/esm/themes/replicant-theme.d.ts.map +1 -1
- package/esm/themes/replicant-theme.js +1 -0
- package/esm/themes/replicant-theme.js.map +1 -1
- package/esm/themes/sandworm-theme.d.ts +1 -0
- package/esm/themes/sandworm-theme.d.ts.map +1 -1
- package/esm/themes/sandworm-theme.js +1 -0
- package/esm/themes/sandworm-theme.js.map +1 -1
- package/esm/themes/shadow-broker-theme.d.ts +1 -0
- package/esm/themes/shadow-broker-theme.d.ts.map +1 -1
- package/esm/themes/shadow-broker-theme.js +1 -0
- package/esm/themes/shadow-broker-theme.js.map +1 -1
- package/esm/themes/sith-theme.d.ts +1 -0
- package/esm/themes/sith-theme.d.ts.map +1 -1
- package/esm/themes/sith-theme.js +1 -0
- package/esm/themes/sith-theme.js.map +1 -1
- package/esm/themes/vault-dweller-theme.d.ts +1 -0
- package/esm/themes/vault-dweller-theme.d.ts.map +1 -1
- package/esm/themes/vault-dweller-theme.js +1 -0
- package/esm/themes/vault-dweller-theme.js.map +1 -1
- package/esm/themes/wild-hunt-theme.d.ts +1 -0
- package/esm/themes/wild-hunt-theme.d.ts.map +1 -1
- package/esm/themes/wild-hunt-theme.js +1 -0
- package/esm/themes/wild-hunt-theme.js.map +1 -1
- package/esm/themes/xenomorph-theme.d.ts +1 -0
- package/esm/themes/xenomorph-theme.d.ts.map +1 -1
- package/esm/themes/xenomorph-theme.js +1 -0
- package/esm/themes/xenomorph-theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/app-bar.tsx +12 -3
- package/src/components/avatar.tsx +20 -5
- package/src/components/cache-view.spec.tsx +63 -0
- package/src/components/cache-view.tsx +41 -9
- package/src/components/command-palette/command-palette-input.spec.tsx +14 -156
- package/src/components/command-palette/command-palette-input.tsx +13 -45
- package/src/components/command-palette/index.tsx +4 -0
- package/src/components/drawer/index.spec.tsx +64 -0
- package/src/components/drawer/index.tsx +5 -0
- package/src/components/noty-list.tsx +1 -3
- package/src/services/css-variable-theme.spec.ts +43 -1
- package/src/services/css-variable-theme.ts +5 -5
- package/src/services/layout-service.spec.ts +74 -0
- package/src/services/layout-service.ts +18 -0
- package/src/services/theme-provider-service.spec.ts +49 -1
- package/src/services/theme-provider-service.ts +12 -11
- package/src/themes/architect-theme.ts +1 -0
- package/src/themes/auditore-theme.ts +1 -0
- package/src/themes/black-mesa-theme.ts +1 -0
- package/src/themes/default-dark-theme.ts +1 -0
- package/src/themes/default-light-theme.ts +1 -0
- package/src/themes/dragonborn-theme.ts +1 -0
- package/src/themes/hawkins-theme.ts +1 -0
- package/src/themes/jedi-theme.ts +1 -0
- package/src/themes/neon-runner-theme.ts +1 -0
- package/src/themes/plumber-theme.ts +1 -0
- package/src/themes/replicant-theme.ts +1 -0
- package/src/themes/sandworm-theme.ts +1 -0
- package/src/themes/shadow-broker-theme.ts +1 -0
- package/src/themes/sith-theme.ts +1 -0
- package/src/themes/vault-dweller-theme.ts +1 -0
- package/src/themes/wild-hunt-theme.ts +1 -0
- package/src/themes/xenomorph-theme.ts +1 -0
|
@@ -718,6 +718,70 @@ describe('Drawer component', () => {
|
|
|
718
718
|
})
|
|
719
719
|
})
|
|
720
720
|
|
|
721
|
+
describe('cleanup on disposal', () => {
|
|
722
|
+
it('should call removeDrawer on LayoutService when the component is removed from DOM', async () => {
|
|
723
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
724
|
+
const layoutService = new LayoutService(createMockElement())
|
|
725
|
+
injector.setExplicitInstance(layoutService, LayoutService)
|
|
726
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
727
|
+
|
|
728
|
+
initializeShadeRoot({
|
|
729
|
+
injector,
|
|
730
|
+
rootElement,
|
|
731
|
+
jsxElement: (
|
|
732
|
+
<Drawer position="left" variant="collapsible">
|
|
733
|
+
<div>Drawer</div>
|
|
734
|
+
</Drawer>
|
|
735
|
+
),
|
|
736
|
+
})
|
|
737
|
+
|
|
738
|
+
await flushUpdates()
|
|
739
|
+
expect(layoutService.drawerState.getValue().left).toBeDefined()
|
|
740
|
+
|
|
741
|
+
const removeDrawerSpy = vi.spyOn(layoutService, 'removeDrawer')
|
|
742
|
+
const drawer = document.querySelector('shade-drawer') as HTMLElement
|
|
743
|
+
drawer.remove()
|
|
744
|
+
await flushUpdates()
|
|
745
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
746
|
+
|
|
747
|
+
expect(removeDrawerSpy).toHaveBeenCalledWith('left')
|
|
748
|
+
})
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
it('should only clean up its own drawer position on disposal', async () => {
|
|
752
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
753
|
+
const layoutService = new LayoutService(createMockElement())
|
|
754
|
+
injector.setExplicitInstance(layoutService, LayoutService)
|
|
755
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
756
|
+
|
|
757
|
+
layoutService.initDrawer('left', { open: true, width: '240px', variant: 'collapsible' })
|
|
758
|
+
|
|
759
|
+
initializeShadeRoot({
|
|
760
|
+
injector,
|
|
761
|
+
rootElement,
|
|
762
|
+
jsxElement: (
|
|
763
|
+
<Drawer position="right" variant="temporary">
|
|
764
|
+
<div>Right Drawer</div>
|
|
765
|
+
</Drawer>
|
|
766
|
+
),
|
|
767
|
+
})
|
|
768
|
+
|
|
769
|
+
await flushUpdates()
|
|
770
|
+
expect(layoutService.drawerState.getValue().right).toBeDefined()
|
|
771
|
+
expect(layoutService.drawerState.getValue().left).toBeDefined()
|
|
772
|
+
|
|
773
|
+
const removeDrawerSpy = vi.spyOn(layoutService, 'removeDrawer')
|
|
774
|
+
const drawer = document.querySelector('shade-drawer') as HTMLElement
|
|
775
|
+
drawer.remove()
|
|
776
|
+
await flushUpdates()
|
|
777
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
778
|
+
|
|
779
|
+
expect(removeDrawerSpy).toHaveBeenCalledWith('right')
|
|
780
|
+
expect(removeDrawerSpy).not.toHaveBeenCalledWith('left')
|
|
781
|
+
})
|
|
782
|
+
})
|
|
783
|
+
})
|
|
784
|
+
|
|
721
785
|
describe('preserving user interactions', () => {
|
|
722
786
|
it('should not reset drawer state if already initialized', async () => {
|
|
723
787
|
await usingAsync(new Injector(), async (injector) => {
|
|
@@ -143,6 +143,11 @@ export const Drawer = Shade<DrawerProps>({
|
|
|
143
143
|
layoutService.setDrawerWidth(position, width)
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// Clean up drawer state from LayoutService when this component is disposed
|
|
147
|
+
useDisposable('drawer-cleanup', () => ({
|
|
148
|
+
[Symbol.dispose]: () => layoutService.removeDrawer(position),
|
|
149
|
+
}))
|
|
150
|
+
|
|
146
151
|
// Subscribe to drawer state
|
|
147
152
|
const [drawerState] = useObservable('drawerState', layoutService.drawerState)
|
|
148
153
|
const isOpen = drawerState[position]?.open ?? false
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createComponent, Shade } from '@furystack/shades'
|
|
2
2
|
import { cssVariableTheme } from '../services/css-variable-theme.js'
|
|
3
|
-
import { getTextColor } from '../services/get-text-color.js'
|
|
4
3
|
import type { NotyModel } from '../services/noty-service.js'
|
|
5
4
|
import { NotyService } from '../services/noty-service.js'
|
|
6
5
|
import { ThemeProviderService } from '../services/theme-provider-service.js'
|
|
@@ -97,7 +96,6 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
|
|
|
97
96
|
|
|
98
97
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
99
98
|
const colors = themeProvider.theme.palette[props.model.type]
|
|
100
|
-
const textColor = getTextColor(colors.main)
|
|
101
99
|
|
|
102
100
|
const removeSelf = async () => {
|
|
103
101
|
const hostEl = wrapperRef.current?.closest('shade-noty') as HTMLElement | null
|
|
@@ -123,7 +121,7 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
|
|
|
123
121
|
|
|
124
122
|
useHostProps({
|
|
125
123
|
'data-noty-type': props.model.type,
|
|
126
|
-
style: { '--noty-bg': colors.main, '--noty-text':
|
|
124
|
+
style: { '--noty-bg': colors.main, '--noty-text': colors.mainContrast },
|
|
127
125
|
})
|
|
128
126
|
|
|
129
127
|
return (
|
|
@@ -67,7 +67,7 @@ describe('css-variable-theme', () => {
|
|
|
67
67
|
expect(cssVariableTheme.typography.fontWeight.bold).toBe('var(--shades-theme-typography-font-weight-bold)')
|
|
68
68
|
expect(cssVariableTheme.typography.lineHeight.tight).toBe('var(--shades-theme-typography-line-height-tight)')
|
|
69
69
|
expect(cssVariableTheme.typography.lineHeight.normal).toBe('var(--shades-theme-typography-line-height-normal)')
|
|
70
|
-
expect(cssVariableTheme.typography.textShadow).toBe('var(--shades-theme-typography-text-shadow
|
|
70
|
+
expect(cssVariableTheme.typography.textShadow).toBe('var(--shades-theme-typography-text-shadow)')
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
it('should have transition properties with CSS variable references', () => {
|
|
@@ -446,6 +446,48 @@ describe('css-variable-theme', () => {
|
|
|
446
446
|
expect(root.style.getPropertyValue('--shades-theme-spacing-md')).toBe('16px')
|
|
447
447
|
expect(root.style.getPropertyValue('--shades-theme-spacing-xl')).toBe('32px')
|
|
448
448
|
})
|
|
449
|
+
|
|
450
|
+
it('should set CSS variables on a custom root element instead of :root', () => {
|
|
451
|
+
const customRoot = document.createElement('div')
|
|
452
|
+
document.body.appendChild(customRoot)
|
|
453
|
+
|
|
454
|
+
useThemeCssVariables(
|
|
455
|
+
{
|
|
456
|
+
text: { primary: '#ff0000' },
|
|
457
|
+
divider: '#00ff00',
|
|
458
|
+
},
|
|
459
|
+
customRoot,
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
expect(customRoot.style.getPropertyValue('--shades-theme-text-primary')).toBe('#ff0000')
|
|
463
|
+
expect(customRoot.style.getPropertyValue('--shades-theme-divider')).toBe('#00ff00')
|
|
464
|
+
expect(root.style.getPropertyValue('--shades-theme-text-primary')).toBe('')
|
|
465
|
+
|
|
466
|
+
customRoot.remove()
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
it('should scope nested palette variables to custom root element', () => {
|
|
470
|
+
const customRoot = document.createElement('div')
|
|
471
|
+
document.body.appendChild(customRoot)
|
|
472
|
+
|
|
473
|
+
useThemeCssVariables(
|
|
474
|
+
{
|
|
475
|
+
palette: {
|
|
476
|
+
primary: {
|
|
477
|
+
main: '#1976d2',
|
|
478
|
+
mainContrast: '#ffffff',
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
customRoot,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
expect(customRoot.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('#1976d2')
|
|
486
|
+
expect(customRoot.style.getPropertyValue('--shades-theme-palette-primary-main-contrast')).toBe('#ffffff')
|
|
487
|
+
expect(root.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('')
|
|
488
|
+
|
|
489
|
+
customRoot.remove()
|
|
490
|
+
})
|
|
449
491
|
})
|
|
450
492
|
|
|
451
493
|
describe('buildTransition', () => {
|
|
@@ -17,7 +17,7 @@ export const cssVariableTheme = {
|
|
|
17
17
|
background: {
|
|
18
18
|
default: 'var(--shades-theme-background-default)',
|
|
19
19
|
paper: 'var(--shades-theme-background-paper)',
|
|
20
|
-
paperImage: 'var(--shades-theme-background-paper-image
|
|
20
|
+
paperImage: 'var(--shades-theme-background-paper-image)',
|
|
21
21
|
},
|
|
22
22
|
palette: {
|
|
23
23
|
primary: {
|
|
@@ -87,7 +87,7 @@ export const cssVariableTheme = {
|
|
|
87
87
|
lg: 'var(--shades-theme-shape-border-radius-lg)',
|
|
88
88
|
full: 'var(--shades-theme-shape-border-radius-full)',
|
|
89
89
|
},
|
|
90
|
-
borderWidth: 'var(--shades-theme-shape-border-width
|
|
90
|
+
borderWidth: 'var(--shades-theme-shape-border-width)',
|
|
91
91
|
},
|
|
92
92
|
shadows: {
|
|
93
93
|
none: 'var(--shades-theme-shadows-none)',
|
|
@@ -127,7 +127,7 @@ export const cssVariableTheme = {
|
|
|
127
127
|
wider: 'var(--shades-theme-typography-letter-spacing-wider)',
|
|
128
128
|
widest: 'var(--shades-theme-typography-letter-spacing-widest)',
|
|
129
129
|
},
|
|
130
|
-
textShadow: 'var(--shades-theme-typography-text-shadow
|
|
130
|
+
textShadow: 'var(--shades-theme-typography-text-shadow)',
|
|
131
131
|
},
|
|
132
132
|
transitions: {
|
|
133
133
|
duration: {
|
|
@@ -222,8 +222,8 @@ const assignValue = <T extends object>(
|
|
|
222
222
|
}
|
|
223
223
|
})
|
|
224
224
|
}
|
|
225
|
-
export const useThemeCssVariables = (theme: DeepPartial<Theme
|
|
226
|
-
|
|
225
|
+
export const useThemeCssVariables = (theme: DeepPartial<Theme>, root?: HTMLElement) => {
|
|
226
|
+
root ??= document.querySelector(':root') as HTMLElement
|
|
227
227
|
assignValue(cssVariableTheme, theme, root)
|
|
228
228
|
|
|
229
229
|
if (window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches) {
|
|
@@ -221,6 +221,15 @@ describe('LayoutService', () => {
|
|
|
221
221
|
})
|
|
222
222
|
})
|
|
223
223
|
|
|
224
|
+
it('should overwrite existing drawer config', () => {
|
|
225
|
+
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
226
|
+
service.initDrawer('left', { open: true, width: '240px', variant: 'collapsible' })
|
|
227
|
+
service.initDrawer('left', { open: false, width: '300px', variant: 'permanent' })
|
|
228
|
+
|
|
229
|
+
expect(service.drawerState.getValue().left).toEqual({ open: false, width: '300px', variant: 'permanent' })
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
224
233
|
it('should initialize drawer with all variant types', () => {
|
|
225
234
|
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
226
235
|
service.initDrawer('left', { open: true, width: '240px', variant: 'permanent' })
|
|
@@ -234,6 +243,71 @@ describe('LayoutService', () => {
|
|
|
234
243
|
})
|
|
235
244
|
})
|
|
236
245
|
})
|
|
246
|
+
|
|
247
|
+
describe('removeDrawer', () => {
|
|
248
|
+
it('should remove left drawer state', () => {
|
|
249
|
+
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
250
|
+
service.initDrawer('left', { open: true, width: '240px', variant: 'collapsible' })
|
|
251
|
+
|
|
252
|
+
service.removeDrawer('left')
|
|
253
|
+
|
|
254
|
+
expect(service.drawerState.getValue().left).toBeUndefined()
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should remove right drawer state', () => {
|
|
259
|
+
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
260
|
+
service.initDrawer('right', { open: true, width: '200px', variant: 'temporary' })
|
|
261
|
+
|
|
262
|
+
service.removeDrawer('right')
|
|
263
|
+
|
|
264
|
+
expect(service.drawerState.getValue().right).toBeUndefined()
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('should not affect the other drawer when removing one', () => {
|
|
269
|
+
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
270
|
+
service.initDrawer('left', { open: true, width: '240px', variant: 'collapsible' })
|
|
271
|
+
service.initDrawer('right', { open: true, width: '200px', variant: 'temporary' })
|
|
272
|
+
|
|
273
|
+
service.removeDrawer('left')
|
|
274
|
+
|
|
275
|
+
expect(service.drawerState.getValue().left).toBeUndefined()
|
|
276
|
+
expect(service.drawerState.getValue().right).toEqual({ open: true, width: '200px', variant: 'temporary' })
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it('should be a no-op if the drawer does not exist', () => {
|
|
281
|
+
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
282
|
+
const stateBefore = service.drawerState.getValue()
|
|
283
|
+
|
|
284
|
+
service.removeDrawer('left')
|
|
285
|
+
|
|
286
|
+
expect(service.drawerState.getValue()).toEqual(stateBefore)
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('should be a no-op if the service is already disposed', () => {
|
|
291
|
+
const service = new LayoutService(mockElement as unknown as HTMLElement)
|
|
292
|
+
service.initDrawer('left', { open: true, width: '240px', variant: 'collapsible' })
|
|
293
|
+
service[Symbol.dispose]()
|
|
294
|
+
|
|
295
|
+
expect(() => service.removeDrawer('left')).not.toThrow()
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('should reset CSS variables after removing a drawer', () => {
|
|
299
|
+
using(new LayoutService(mockElement as unknown as HTMLElement), (service) => {
|
|
300
|
+
service.initDrawer('left', { open: true, width: '240px', variant: 'collapsible' })
|
|
301
|
+
mockSetProperty.mockClear()
|
|
302
|
+
|
|
303
|
+
service.removeDrawer('left')
|
|
304
|
+
|
|
305
|
+
expect(mockSetProperty).toHaveBeenCalledWith('--layout-drawer-left-width', '0px')
|
|
306
|
+
expect(mockSetProperty).toHaveBeenCalledWith('--layout-drawer-left-configured-width', '0px')
|
|
307
|
+
expect(mockSetProperty).toHaveBeenCalledWith('--layout-content-margin-left', '0px')
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
})
|
|
237
311
|
})
|
|
238
312
|
|
|
239
313
|
describe('CSS Variables', () => {
|
|
@@ -237,6 +237,24 @@ export class LayoutService implements Disposable {
|
|
|
237
237
|
})
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Removes a drawer from the state, clearing its configuration.
|
|
242
|
+
* Use this when a Drawer component is unmounted to prevent stale state
|
|
243
|
+
* from affecting content margins.
|
|
244
|
+
*
|
|
245
|
+
* @param position - Which drawer to remove ('left' or 'right')
|
|
246
|
+
*/
|
|
247
|
+
public removeDrawer(position: 'left' | 'right'): void {
|
|
248
|
+
if (this.drawerState.isDisposed) return
|
|
249
|
+
|
|
250
|
+
const currentState = this.drawerState.getValue()
|
|
251
|
+
|
|
252
|
+
if (currentState[position]) {
|
|
253
|
+
const { [position]: _, ...rest } = currentState
|
|
254
|
+
this.drawerState.setValue(rest)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
240
258
|
/**
|
|
241
259
|
* Sets the top gap spacing between AppBar and content.
|
|
242
260
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from 'vitest'
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
2
2
|
import { ThemeProviderService } from './theme-provider-service.js'
|
|
3
3
|
|
|
4
4
|
describe('ThemeProviderService', () => {
|
|
@@ -19,4 +19,52 @@ describe('ThemeProviderService', () => {
|
|
|
19
19
|
expect(service.theme.name).toBe('css-variable-theme')
|
|
20
20
|
})
|
|
21
21
|
})
|
|
22
|
+
|
|
23
|
+
describe('setAssignedTheme with custom root', () => {
|
|
24
|
+
let customRoot: HTMLElement
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
customRoot = document.createElement('div')
|
|
28
|
+
document.body.appendChild(customRoot)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
customRoot.remove()
|
|
33
|
+
document.documentElement.style.cssText = ''
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should apply CSS variables to the provided root element', () => {
|
|
37
|
+
service.setAssignedTheme(
|
|
38
|
+
{
|
|
39
|
+
text: { primary: '#abcdef' },
|
|
40
|
+
palette: { primary: { main: '#123456' } },
|
|
41
|
+
},
|
|
42
|
+
customRoot,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
expect(customRoot.style.getPropertyValue('--shades-theme-text-primary')).toBe('#abcdef')
|
|
46
|
+
expect(customRoot.style.getPropertyValue('--shades-theme-palette-primary-main')).toBe('#123456')
|
|
47
|
+
expect(document.documentElement.style.getPropertyValue('--shades-theme-text-primary')).toBe('')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should still update the stored theme when a custom root is provided', () => {
|
|
51
|
+
const theme = { text: { primary: '#111' } }
|
|
52
|
+
service.setAssignedTheme(theme, customRoot)
|
|
53
|
+
|
|
54
|
+
expect(service.getAssignedTheme()).toBe(theme)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should emit themeChanged when a custom root is provided', () => {
|
|
58
|
+
const theme = { text: { primary: '#222' } }
|
|
59
|
+
let emittedTheme: unknown
|
|
60
|
+
|
|
61
|
+
service.subscribe('themeChanged', (t) => {
|
|
62
|
+
emittedTheme = t
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
service.setAssignedTheme(theme, customRoot)
|
|
66
|
+
|
|
67
|
+
expect(emittedTheme).toBe(theme)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
22
70
|
})
|
|
@@ -85,8 +85,8 @@ export interface Background {
|
|
|
85
85
|
default: Color
|
|
86
86
|
/** Elevated surface background (cards, dialogs, etc.) */
|
|
87
87
|
paper: Color
|
|
88
|
-
/**
|
|
89
|
-
paperImage
|
|
88
|
+
/** CSS background-image for paper surfaces (e.g. a tiled texture). Use 'none' for no image. */
|
|
89
|
+
paperImage: string
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
@@ -131,8 +131,8 @@ export type BorderRadiusScale = {
|
|
|
131
131
|
export type Shape = {
|
|
132
132
|
/** Border radius scale */
|
|
133
133
|
borderRadius: BorderRadiusScale
|
|
134
|
-
/** Border width for surface components (paper, card, etc.).
|
|
135
|
-
borderWidth
|
|
134
|
+
/** Border width for surface components (paper, card, etc.). Use '0px' for no border. */
|
|
135
|
+
borderWidth: string
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
@@ -230,9 +230,9 @@ export type ThemeTypography = {
|
|
|
230
230
|
/** Line height scale */
|
|
231
231
|
lineHeight: LineHeightScale
|
|
232
232
|
/** Letter spacing scale */
|
|
233
|
-
letterSpacing
|
|
234
|
-
/** CSS text-shadow value applied globally to text */
|
|
235
|
-
textShadow
|
|
233
|
+
letterSpacing: LetterSpacingScale
|
|
234
|
+
/** CSS text-shadow value applied globally to text. Use 'none' for no shadow. */
|
|
235
|
+
textShadow: string
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
/**
|
|
@@ -358,9 +358,9 @@ export interface Theme {
|
|
|
358
358
|
/** Spacing scale */
|
|
359
359
|
spacing: Spacing
|
|
360
360
|
/** Z-index stacking layers */
|
|
361
|
-
zIndex
|
|
361
|
+
zIndex: ZIndex
|
|
362
362
|
/** Visual effect tokens (blur, backdrop) */
|
|
363
|
-
effects
|
|
363
|
+
effects: Effects
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
/**
|
|
@@ -382,10 +382,11 @@ export class ThemeProviderService extends EventHub<{ themeChanged: DeepPartial<T
|
|
|
382
382
|
/**
|
|
383
383
|
* Assigns a new theme, updates the CSS variables and emits a themeChanged event
|
|
384
384
|
* @param theme The Theme instance
|
|
385
|
+
* @param root Optional HTML element to scope CSS variables to. Defaults to `:root`.
|
|
385
386
|
*/
|
|
386
|
-
public setAssignedTheme(theme: DeepPartial<Theme
|
|
387
|
+
public setAssignedTheme(theme: DeepPartial<Theme>, root?: HTMLElement) {
|
|
387
388
|
this._assignedTheme = theme
|
|
388
|
-
useThemeCssVariables(theme)
|
|
389
|
+
useThemeCssVariables(theme, root)
|
|
389
390
|
this.emit('themeChanged', theme)
|
|
390
391
|
}
|
|
391
392
|
}
|
package/src/themes/jedi-theme.ts
CHANGED
package/src/themes/sith-theme.ts
CHANGED