diff --git a/.changeset/bright-peas-pull.md b/.changeset/bright-peas-pull.md new file mode 100644 index 00000000..30c26dce --- /dev/null +++ b/.changeset/bright-peas-pull.md @@ -0,0 +1,8 @@ +--- +'number-flow': patch +'@number-flow/svelte': patch +'@number-flow/react': patch +'@number-flow/vue': patch +--- + +Expose parts for styling support diff --git a/packages/number-flow/package.json b/packages/number-flow/package.json index ce1e3d1e..8d7019d0 100644 --- a/packages/number-flow/package.json +++ b/packages/number-flow/package.json @@ -55,9 +55,13 @@ "magic-string": "^0.30.11", "parse-literals": "^1.2.1", "playwright": "^1.48.0", + "rollup-plugin-minify-html-literals-v3": "^1.3.4", "tslib": "^2.7.0", "typescript": "^5.6.2", "vite": "^5.4.3", "vitest": "^2.1.2" + }, + "dependencies": { + "esm-env": "^1.1.4" } } diff --git a/packages/number-flow/src/index.ts b/packages/number-flow/src/index.ts index 09e76cfe..8c66df79 100644 --- a/packages/number-flow/src/index.ts +++ b/packages/number-flow/src/index.ts @@ -22,7 +22,7 @@ import { max } from './util/math' export { define } from './util/dom' export { prefersReducedMotion } from './styles' -export { render, type RenderProps } from './ssr' +export { renderInnerHTML } from './ssr' export * from './formatter' export const canAnimate = supportsMod && supportsLinear && supportsAtProperty @@ -125,6 +125,7 @@ export class NumberFlowLite extends ServerSafeHTMLElement implements Props { if (!this.#created) { this.#data = data + // This will overwrite the DSD if any: this.attachShadow({ mode: 'open' }) // Add stylesheet @@ -140,25 +141,18 @@ export class NumberFlowLite extends ServerSafeHTMLElement implements Props { this.shadowRoot!.appendChild(style) } - this.shadowRoot!.appendChild(createElement('slot')) - this.#pre = new SymbolSection(this, pre, { - inert: true, - ariaHidden: 'true', - justify: 'right' + justify: 'right', + part: 'left' }) this.shadowRoot!.appendChild(this.#pre.el) - this.#num = new Num(this, integer, fraction, { - inert: true, - ariaHidden: 'true' - }) + this.#num = new Num(this, integer, fraction) this.shadowRoot!.appendChild(this.#num.el) this.#post = new SymbolSection(this, post, { - inert: true, - ariaHidden: 'true', - justify: 'left' + justify: 'left', + part: 'right' }) this.shadowRoot!.appendChild(this.#post.el) } else { @@ -260,10 +254,12 @@ class Num { { className, ...props }: HTMLProps<'span'> = {} ) { this.#integer = new NumberSection(flow, integer, { - justify: 'right' + justify: 'right', + part: 'integer' }) this.#fraction = new NumberSection(flow, fraction, { - justify: 'left' + justify: 'left', + part: 'fraction' }) this.#inner = createElement( @@ -277,6 +273,7 @@ class Num { 'span', { ...props, + part: 'number', className: `number ${className ?? ''}` }, [this.#inner] @@ -384,12 +381,14 @@ abstract class Section { protected unpop(char: Char) { char.el.classList.remove('section__exiting') + char.el.style.top = '' char.el.style[this.justify] = '' } protected pop(chars: Map) { // Calculate offsets for removed before popping, to avoid layout thrashing: chars.forEach((char) => { + char.el.style.top = `${char.el.offsetTop}px` char.el.style[this.justify] = `${offset(char.el, this.justify)}px` }) chars.forEach((char) => { @@ -609,7 +608,7 @@ class Digit extends Char { constructor( section: Section, - _: KeyedDigitPart['type'], + type: KeyedDigitPart['type'], value: KeyedDigitPart['value'], readonly place: number, props?: CharProps @@ -626,6 +625,7 @@ class Digit extends Char { const el = createElement( 'span', { + part: `digit ${type}-digit`, className: `digit` }, numbers @@ -736,6 +736,7 @@ class Sym extends Char { createElement( 'span', { + part: type, className: `symbol` }, [val] diff --git a/packages/number-flow/src/ssr.ts b/packages/number-flow/src/ssr.ts index 2692ea7e..6fa604eb 100644 --- a/packages/number-flow/src/ssr.ts +++ b/packages/number-flow/src/ssr.ts @@ -1,14 +1,53 @@ -import type { Data } from './formatter' -import { charHeight, maskHeight, SlottedTag } from './styles' -import { BROWSER } from './util/env' +import type { Data, KeyedNumberPart } from './formatter' +import { css, html } from './util/string' +import { charHeight, halfMaskHeight, maskHeight } from './styles' +import { BROWSER } from 'esm-env' export const ServerSafeHTMLElement = BROWSER ? HTMLElement : (class {} as unknown as typeof HTMLElement) // for types -export type RenderProps = Pick & { willChange?: boolean } +const styles = css` + :host { + display: inline-block; + direction: ltr; + white-space: nowrap; + line-height: ${charHeight} !important; + } -// Could eventually use DSD e.g. -// `