diff --git a/package.json b/package.json index 090ee18..bf32583 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@rolemodel/spider", "description": "Shared high level web components for RoleModel Software and beyond", "packageManager": "yarn@4.12.0", - "version": "0.0.9", + "version": "0.0.10", "author": "RoleModel Software", "license": "MIT", "type": "module", diff --git a/src/components/pdf-viewer/pdf-viewer.js b/src/components/pdf-viewer/pdf-viewer.js index dcaaddc..af32b8a 100644 --- a/src/components/pdf-viewer/pdf-viewer.js +++ b/src/components/pdf-viewer/pdf-viewer.js @@ -1,4 +1,4 @@ -import { html } from 'lit' +import { html, adoptStyles } from 'lit' import { ContextProvider } from '@lit/context' import './url-parse-polyfill.js' import * as pdfjsLib from 'pdfjs-dist' @@ -8,7 +8,7 @@ import openjpegWasmUrl from 'pdfjs-dist/wasm/openjpeg.wasm?url' import qcmsWasmUrl from 'pdfjs-dist/wasm/qcms_bg.wasm?url' import styles from './pdf-viewer.styles.js' import { pdfContext } from './pdf-context.js' -import { createThemeStyleSheet } from './theme-config.js' +import { createThemeStyles } from './theme-config.js' import { normalizeText } from './helpers/text-helper.js' import './toolbar/pdf-toolbar.js' import './sidebar/pdf-sidebar.js' @@ -83,7 +83,7 @@ export default class PDFViewer extends RoleModelElement { this.searchOpen = false this.error = null this.loading = false - this.themeStyleSheet = createThemeStyleSheet(this.themeHue, this.themeSaturation) + this.themeStyleSheet = createThemeStyles(this.themeHue, this.themeSaturation) this.fitToScreenScale = null this._provider = new ContextProvider(this, { @@ -381,11 +381,8 @@ export default class PDFViewer extends RoleModelElement { } _updateThemeColors() { - this.themeStyleSheet = createThemeStyleSheet(this.themeHue, this.themeSaturation) - this.shadowRoot.adoptedStyleSheets = [ - ...this.constructor.elementStyles.map(s => s.styleSheet), - this.themeStyleSheet - ] + this.themeStyleSheet = createThemeStyles(this.themeHue, this.themeSaturation) + adoptStyles(this.shadowRoot, [...this.constructor.elementStyles, this.themeStyleSheet]) } async firstUpdated() { diff --git a/src/components/pdf-viewer/theme-config.js b/src/components/pdf-viewer/theme-config.js index 6c339f1..df51c9e 100644 --- a/src/components/pdf-viewer/theme-config.js +++ b/src/components/pdf-viewer/theme-config.js @@ -1,5 +1,9 @@ -export const createThemeStyleSheet = (hue = 217, saturation = 89) => { - const css = ` +import { css } from 'lit' + +// Returns a lazy CSSResult instead of constructing a CSSStyleSheet directly: +// `new CSSStyleSheet()` throws "Illegal constructor" on browsers without +// constructable stylesheet support (e.g. Safari < 16.4). +export const createThemeStyles = (hue = 217, saturation = 89) => css` :host { --theme-primary: light-dark(hsl(${hue}, ${saturation}%, 50%), hsl(${hue}, ${saturation}%, 60%)); --theme-primary-light: light-dark(hsl(${hue}, ${saturation}%, 90%), hsl(${hue}, ${saturation}%, 30%)); @@ -44,8 +48,3 @@ export const createThemeStyleSheet = (hue = 217, saturation = 89) => { --theme-icon-size-lg: 18px; } ` - - const sheet = new CSSStyleSheet() - sheet.replaceSync(css) - return sheet -} diff --git a/test/components/theme-config.test.js b/test/components/theme-config.test.js index d528304..9880637 100644 --- a/test/components/theme-config.test.js +++ b/test/components/theme-config.test.js @@ -1,32 +1,30 @@ import { describe, it, expect } from 'vitest' -import { createThemeStyleSheet } from '../../src/components/pdf-viewer/theme-config.js' +import { CSSResult } from 'lit' +import { createThemeStyles } from '../../src/components/pdf-viewer/theme-config.js' describe('Theme Configuration', () => { - describe('createThemeStyleSheet', () => { - it('should create a CSSStyleSheet with default values', () => { - const sheet = createThemeStyleSheet() + describe('createThemeStyles', () => { + it('should create a CSSResult with default values', () => { + const styles = createThemeStyles() - expect(sheet).toBeInstanceOf(CSSStyleSheet) - expect(sheet.cssRules.length).toBeGreaterThan(0) + expect(styles).toBeInstanceOf(CSSResult) + expect(styles.cssText.length).toBeGreaterThan(0) }) - it('should create a stylesheet with custom hue', () => { - const sheet = createThemeStyleSheet(150, 89) + it('should create styles with custom hue', () => { + const styles = createThemeStyles(150, 89) - expect(sheet).toBeInstanceOf(CSSStyleSheet) - expect(sheet.cssRules[0].cssText).toContain('150') + expect(styles.cssText).toContain('150') }) - it('should create a stylesheet with custom saturation', () => { - const sheet = createThemeStyleSheet(217, 50) + it('should create styles with custom saturation', () => { + const styles = createThemeStyles(217, 50) - expect(sheet).toBeInstanceOf(CSSStyleSheet) - expect(sheet.cssRules[0].cssText).toContain('50%') + expect(styles.cssText).toContain('50%') }) it('should include all theme CSS variables', () => { - const sheet = createThemeStyleSheet() - const cssText = sheet.cssRules[0].cssText + const cssText = createThemeStyles().cssText expect(cssText).toContain('--theme-primary') expect(cssText).toContain('--theme-neutral-') @@ -37,10 +35,22 @@ describe('Theme Configuration', () => { }) it('should use light-dark() function for color properties', () => { - const sheet = createThemeStyleSheet() - const cssText = sheet.cssRules[0].cssText + const cssText = createThemeStyles().cssText expect(cssText).toContain('light-dark') }) + + it('should not construct a CSSStyleSheet eagerly (Safari < 16.4 has no constructor)', () => { + const originalCSSStyleSheet = globalThis.CSSStyleSheet + globalThis.CSSStyleSheet = function () { + throw new TypeError('Illegal constructor') + } + + try { + expect(() => createThemeStyles(217, 89)).not.toThrow() + } finally { + globalThis.CSSStyleSheet = originalCSSStyleSheet + } + }) }) })