@mhmo91/schmancy 0.9.22 → 0.9.24
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/custom-elements.json +116 -0
- package/dist/agent/schmancy.agent.js +582 -262
- package/dist/agent/schmancy.agent.js.map +1 -1
- package/dist/agent/schmancy.manifest.json +139 -0
- package/dist/badge.cjs +1 -1
- package/dist/badge.js +1 -1
- package/dist/content-drawer.cjs +1 -1
- package/dist/content-drawer.js +1 -1
- package/dist/handover/agent-runtime-followups.md +1 -1
- package/dist/handover/agent-runtime-v1.md +3 -3
- package/dist/handover/agent-runtime-v2-loopback.md +5 -5
- package/dist/handover/claude-design-brief.md +54 -19
- package/dist/index.cjs +1 -1
- package/dist/index.js +27 -26
- package/dist/metric.cjs +69 -0
- package/dist/metric.cjs.map +1 -0
- package/dist/metric.js +93 -0
- package/dist/metric.js.map +1 -0
- package/dist/nav-drawer.cjs +1 -1
- package/dist/nav-drawer.js +1 -1
- package/dist/navigation-bar.cjs +1 -1
- package/dist/navigation-bar.js +1 -1
- package/dist/{src-C9DpHCWF.cjs → src-Ci10_Iwf.cjs} +1 -1
- package/dist/{src-C9DpHCWF.cjs.map → src-Ci10_Iwf.cjs.map} +1 -1
- package/dist/{src-B0jLOpqn.js → src-DNuhxDig.js} +1 -0
- package/dist/{src-B0jLOpqn.js.map → src-DNuhxDig.js.map} +1 -1
- package/dist/teleport.cjs +1 -1
- package/dist/teleport.js +1 -1
- package/dist/typography.cjs +3 -3
- package/dist/typography.cjs.map +1 -1
- package/dist/typography.js +107 -10
- package/dist/typography.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/metric/index.ts +1 -0
- package/src/metric/metric.ts +127 -0
- package/src/typography/typography.ts +94 -1
- package/types/src/index.d.ts +1 -0
- package/types/src/metric/index.d.ts +1 -0
- package/types/src/metric/metric.d.ts +39 -0
- package/types/src/typography/typography.d.ts +32 -0
package/dist/teleport.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./src-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./src-Ci10_Iwf.cjs`);exports.HereMorty=e.r,Object.defineProperty(exports,`SchmancyTeleportation`,{enumerable:!0,get:function(){return e.n}}),exports.WhereAreYouRicky=e.i,exports.teleport=e.a;
|
package/dist/teleport.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, r } from "./src-
|
|
1
|
+
import { a as e, i as t, n, r } from "./src-DNuhxDig.js";
|
|
2
2
|
export { r as HereMorty, n as SchmancyTeleportation, t as WhereAreYouRicky, e as teleport };
|
package/dist/typography.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunk-CncqDLb2.cjs`);const e=require(`./tailwind.mixin-BHX99hgX.cjs`),t=require(`./decorate-F9CuyeHg.cjs`);let n=require(`rxjs`),r=require(`rxjs/operators`),i=require(`lit/decorators.js`),a=require(`lit`),o=require(`lit/directives/ref.js`);var s=class extends e.t(a.css`
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunk-CncqDLb2.cjs`);const e=require(`./tailwind.mixin-BHX99hgX.cjs`),t=require(`./decorate-F9CuyeHg.cjs`);let n=require(`rxjs`),r=require(`rxjs/operators`),i=require(`lit/decorators.js`),a=require(`lit`),o=require(`lit/directives/ref.js`),s=require(`lit/static-html.js`);var c={display:{type:`display`,token:`lg`},"display-lg":{type:`display`,token:`lg`},"display-md":{type:`display`,token:`md`},"display-sm":{type:`display`,token:`sm`},"heading-lg":{type:`headline`,token:`lg`},"heading-md":{type:`headline`,token:`md`},"heading-sm":{type:`headline`,token:`sm`},"title-lg":{type:`title`,token:`lg`},"title-md":{type:`title`,token:`md`},"title-sm":{type:`title`,token:`sm`},"body-lg":{type:`body`,token:`lg`},"body-md":{type:`body`,token:`md`},"body-sm":{type:`body`,token:`sm`},"label-lg":{type:`label`,token:`lg`},"label-md":{type:`label`,token:`md`},"label-sm":{type:`label`,token:`sm`},caption:{type:`label`,token:`sm`}},l={h1:s.literal`h1`,h2:s.literal`h2`,h3:s.literal`h3`,h4:s.literal`h4`,h5:s.literal`h5`,h6:s.literal`h6`,p:s.literal`p`,span:s.literal`span`,div:s.literal`div`},u=class extends e.t(a.css`
|
|
2
2
|
:host {
|
|
3
3
|
display: block;
|
|
4
4
|
font-family: inherit;
|
|
@@ -274,9 +274,9 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./ch
|
|
|
274
274
|
display: block;
|
|
275
275
|
opacity: 0.35;
|
|
276
276
|
}
|
|
277
|
-
`){constructor(...e){super(...e),this.type=`body`,this.token=`md`,this.editable=!1,this.value=``,this.placeholder=``,this._editRef=(0,o.createRef)()}static{this.shadowRootOptions={mode:`open`,delegatesFocus:!0}}selectAll(){let e=this._editRef.value;if(!e)return;e.focus();let t=window.getSelection();if(t&&e.textContent){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}}connectedCallback(){super.connectedCallback(),(0,n.fromEvent)(this,`focusout`).pipe((0,r.filter)(()=>this.editable),(0,r.tap)(()=>{let e=this._editRef.value;if(!e)return;let t=e.innerText.trim();t!==this.value&&this.dispatchEvent(new CustomEvent(`change`,{detail:{value:t},bubbles:!0,composed:!0})),t||(e.textContent=``)}),(0,r.takeUntil)(this.disconnecting)).subscribe(),(0,n.fromEvent)(this,`input`).pipe((0,r.filter)(()=>this.editable),(0,r.tap)(()=>{let e=this._editRef.value;e&&!e.innerText.trim()&&(e.textContent=``)}),(0,r.takeUntil)(this.disconnecting)).subscribe(),(0,n.fromEvent)(this,`keydown`).pipe((0,r.filter)(()=>this.editable),(0,r.filter)(e=>e.key===`Enter`),(0,r.tap)(e=>{e.preventDefault(),(this._editRef.value??this).blur()}),(0,r.takeUntil)(this.disconnecting)).subscribe()}updated(e){if(super.updated(e),e.has(`maxLines`)&&(this.classList.remove(`line-clamp-1`,`line-clamp-2`,`line-clamp-3`,`line-clamp-4`,`line-clamp-5`,`line-clamp-6`),this.maxLines&&this.classList.add(`line-clamp-${this.maxLines}`)),(e.has(`value`)||e.has(`editable`))&&this.editable){let e=this._editRef.value;e&&document.activeElement!==e&&(this.value?e.innerText=this.value:e.textContent=``)}}render(){
|
|
277
|
+
`){constructor(...e){super(...e),this.type=`body`,this.token=`md`,this.editable=!1,this.value=``,this.placeholder=``,this._editRef=(0,o.createRef)()}static{this.shadowRootOptions={mode:`open`,delegatesFocus:!0}}willUpdate(e){if(super.willUpdate?.(e),e.has(`preset`)&&this.preset&&c[this.preset]){let{type:e,token:t}=c[this.preset];this.type=e,this.token=t}}selectAll(){let e=this._editRef.value;if(!e)return;e.focus();let t=window.getSelection();if(t&&e.textContent){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}}connectedCallback(){super.connectedCallback(),(0,n.fromEvent)(this,`focusout`).pipe((0,r.filter)(()=>this.editable),(0,r.tap)(()=>{let e=this._editRef.value;if(!e)return;let t=e.innerText.trim();t!==this.value&&this.dispatchEvent(new CustomEvent(`change`,{detail:{value:t},bubbles:!0,composed:!0})),t||(e.textContent=``)}),(0,r.takeUntil)(this.disconnecting)).subscribe(),(0,n.fromEvent)(this,`input`).pipe((0,r.filter)(()=>this.editable),(0,r.tap)(()=>{let e=this._editRef.value;e&&!e.innerText.trim()&&(e.textContent=``)}),(0,r.takeUntil)(this.disconnecting)).subscribe(),(0,n.fromEvent)(this,`keydown`).pipe((0,r.filter)(()=>this.editable),(0,r.filter)(e=>e.key===`Enter`),(0,r.tap)(e=>{e.preventDefault(),(this._editRef.value??this).blur()}),(0,r.takeUntil)(this.disconnecting)).subscribe()}updated(e){if(super.updated(e),e.has(`maxLines`)&&(this.classList.remove(`line-clamp-1`,`line-clamp-2`,`line-clamp-3`,`line-clamp-4`,`line-clamp-5`,`line-clamp-6`),this.maxLines&&this.classList.add(`line-clamp-${this.maxLines}`)),(e.has(`value`)||e.has(`editable`))&&this.editable){let e=this._editRef.value;e&&document.activeElement!==e&&(this.value?e.innerText=this.value:e.textContent=``)}}render(){if(this.editable)return a.html`<div
|
|
278
278
|
${(0,o.ref)(this._editRef)}
|
|
279
279
|
class="edit"
|
|
280
280
|
contenteditable="true"
|
|
281
281
|
data-placeholder=${this.placeholder??``}
|
|
282
|
-
></div
|
|
282
|
+
></div>`;if(this.as&&l[this.as]){let e=l[this.as];return s.html`<${e}><slot></slot></${e}>`}return a.html`<slot></slot>`}};t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`type`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`preset`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`as`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`token`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`align`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`weight`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],u.prototype,`transform`,void 0),t.t([(0,i.property)({type:Number})],u.prototype,`maxLines`,void 0),t.t([(0,i.property)({type:Boolean,reflect:!0})],u.prototype,`editable`,void 0),t.t([(0,i.property)({type:String})],u.prototype,`value`,void 0),t.t([(0,i.property)({type:String})],u.prototype,`placeholder`,void 0),u=t.t([(0,i.customElement)(`schmancy-typography`)],u),Object.defineProperty(exports,`SchmancyTypography`,{enumerable:!0,get:function(){return u}});
|
package/dist/typography.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typography.cjs","names":[],"sources":["../src/typography/typography.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/tailwind.mixin'\nimport { css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { fromEvent } from 'rxjs'\nimport { filter, tap, takeUntil } from 'rxjs/operators'\n\n// Material Design 3 typography - https://m3.material.io/styles/typography/type-scale-tokens\n\n/**\n * @element schmancy-typography\n * @slot - The text for the typography.\n */\n@customElement('schmancy-typography')\nexport class SchmancyTypography extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tfont-family: inherit;\n\t\thyphens: none;\n\t}\n\n\t/* Text alignment */\n\t:host([align='center']) {\n\t\ttext-align: center;\n\t}\n\n\t:host([align='left']) {\n\t\ttext-align: start;\n\t}\n\n\t:host([align='right']) {\n\t\ttext-align: right;\n\t}\n\n\t:host([align='justify']) {\n\t\ttext-align: justify;\n\t}\n\n\t/* Font weight */\n\t:host([weight='bold']) {\n\t\tfont-weight: 700;\n\t}\n\n\t:host([weight='medium']) {\n\t\tfont-weight: 500;\n\t}\n\n\t:host([weight='normal']) {\n\t\tfont-weight: 400;\n\t}\n\n\t/* Text transform */\n\t:host([transform='uppercase']) {\n\t\ttext-transform: uppercase;\n\t}\n\n\t:host([transform='lowercase']) {\n\t\ttext-transform: lowercase;\n\t}\n\n\t:host([transform='capitalize']) {\n\t\ttext-transform: capitalize;\n\t}\n\n\t:host([transform='normal']) {\n\t\ttext-transform: none;\n\t}\n\n\t/* Type-based weight defaults (when using Tailwind classes without token) */\n\t:host([type='display']),\n\t:host([type='headline']),\n\t:host([type='body']) {\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='label']),\n\t:host([type='subtitle']),\n\t:host([type='title']) {\n\t\tfont-weight: 500;\n\t}\n\n\t/* Display typography variants - Material Design 3 + Extended */\n\t:host([type='display'][token='xl']) {\n\t\tfont-size: 72px;\n\t\tline-height: 80px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='lg']) {\n\t\tfont-size: 57px;\n\t\tline-height: 64px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='md']) {\n\t\tfont-size: 45px;\n\t\tline-height: 52px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='sm']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='xs']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Headline typography variants - Material Design 3 + Extended */\n\t:host([type='headline'][token='xl']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='lg']) {\n\t\tfont-size: 32px;\n\t\tline-height: 40px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='md']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='sm']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='xs']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Title typography variants - Material Design 3 + Extended */\n\t:host([type='title'][token='xl']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='lg']) {\n\t\tfont-size: 22px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Subtitle typography variants - Extended from Material Design 3 */\n\t:host([type='subtitle'][token='xl']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='lg']) {\n\t\tfont-size: 18px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Body typography variants - Material Design 3 + Extended */\n\t:host([type='body'][token='xl']) {\n\t\tfont-size: 18px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='lg']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='md']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='sm']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Label typography variants - Material Design 3 + Extended */\n\t:host([type='label'][token='xl']) {\n\t\tfont-size: 16px;\n\t\tline-height: 22px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='lg']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='md']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='sm']) {\n\t\tfont-size: 11px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Note: Custom letter-spacing, font-size, and line-height should be applied via inline styles or Tailwind classes */\n\n\t:host([editable]) {\n\t\tcursor: text;\n\t\tborder-radius: 4px;\n\t\ttransition: background 150ms;\n\t\tmin-height: 1em;\n\t}\n\t/* Editable div lives in shadow DOM so light DOM (Lit markers) is untouched */\n\t.edit {\n\t\toutline: none;\n\t\tmin-height: 1em;\n\t\tfont: inherit;\n\t\tcolor: inherit;\n\t\tletter-spacing: inherit;\n\t\tline-height: inherit;\n\t}\n\t.edit:empty::before {\n\t\tcontent: attr(data-placeholder);\n\t\tpointer-events: none;\n\t\tdisplay: block;\n\t\topacity: 0.35;\n\t}\n`) {\n\tstatic shadowRootOptions: ShadowRootInit = {\n\t\tmode: 'open',\n\t\tdelegatesFocus: true,\n\t}\n\n\t/**\n\t * @attr type - The type of the typography.\n\t * @default 'body'\n\t * @type {'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label'}\n\t */\n\t@property({ type: String, reflect: true })\n\ttype: 'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label' = 'body'\n\n\t/**\n\t * @attr token - The size token.\n\t * @deprecated Prefer using Tailwind responsive text classes for better responsive design.\n\t * Set token=\"\" and use class=\"text-sm md:text-base lg:text-lg\" instead.\n\t * Example: <schmancy-typography type=\"display\" token=\"\" class=\"text-2xl sm:text-3xl md:text-4xl\">\n\t * @default 'md'\n\t * @type {'xs' | 'sm' | 'md' | 'lg' | 'xl' | ''}\n\t */\n\t@property({ type: String, reflect: true })\n\ttoken: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '' = 'md'\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'left' |'center' |'right'}\n\t */\n\t@property({ type: String, reflect: true })\n\talign: 'left' | 'center' | 'justify' | 'right' | undefined\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'normal' | 'medium' |'bold'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true })\n\tweight: 'normal' | 'medium' | 'bold' | undefined\n\t\n\t/**\n\t *\n\t * @attr\n\t * @default inherit\n\t * @type {'uppercase' |'lowercase' |'capitalize' |'normal'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true }) \n\ttransform: 'uppercase' | 'lowercase' | 'capitalize' | 'normal' | undefined\n\n\t@property({ type: Number })\n\tmaxLines: 1 | 2 | 3 | 4 | 5 | 6 | undefined\n\n\t/** When true, the element becomes contenteditable and dispatches 'change' events on blur/Enter */\n\t@property({ type: Boolean, reflect: true }) editable = false\n\t/** The text value when in editable mode. Set via property binding: .value=${...} */\n\t@property({ type: String }) value = ''\n\t/** Placeholder shown when editable and empty */\n\t@property({ type: String }) placeholder = ''\n\n\tprivate _editRef = createRef<HTMLDivElement>()\n\n\t/** Focus and select all text in editable mode */\n\tselectAll() {\n\t\tconst el = this._editRef.value\n\t\tif (!el) return\n\t\tel.focus()\n\t\tconst sel = window.getSelection()\n\t\tif (sel && el.textContent) {\n\t\t\tconst range = document.createRange()\n\t\t\trange.selectNodeContents(el)\n\t\t\tsel.removeAllRanges()\n\t\t\tsel.addRange(range)\n\t\t}\n\t}\n\n\tconnectedCallback() {\n\t\tsuper.connectedCallback()\n\n\t\tfromEvent<FocusEvent>(this, 'focusout').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (!el) return\n\t\t\t\tconst newValue = el.innerText.trim()\n\t\t\t\tif (newValue !== this.value) {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('change', {\n\t\t\t\t\t\tdetail: { value: newValue },\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t\t// Ensure truly empty so :empty CSS placeholder works\n\t\t\t\tif (!newValue) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\t// Clean stray <br> / whitespace nodes so :empty CSS matches\n\t\tfromEvent(this, 'input').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (el && !el.innerText.trim()) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\tfromEvent<KeyboardEvent>(this, 'keydown').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\tfilter(e => e.key === 'Enter'),\n\t\t\ttap(e => { e.preventDefault(); (this._editRef.value ?? this).blur() }),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\t}\n\n\tprotected updated(changedProperties: Map<string, unknown>): void {\n\t\tsuper.updated(changedProperties)\n\t\tif (changedProperties.has('maxLines')) {\n\t\t\t// Remove all line-clamp classes\n\t\t\tthis.classList.remove('line-clamp-1', 'line-clamp-2', 'line-clamp-3', 'line-clamp-4', 'line-clamp-5', 'line-clamp-6')\n\t\t\t// Add the appropriate one\n\t\t\tif (this.maxLines) {\n\t\t\t\tthis.classList.add(`line-clamp-${this.maxLines}`)\n\t\t\t}\n\t\t}\n\t\tif ((changedProperties.has('value') || changedProperties.has('editable')) && this.editable) {\n\t\t\tconst el = this._editRef.value\n\t\t\tif (el && document.activeElement !== el) {\n\t\t\t\tif (this.value) {\n\t\t\t\t\tel.innerText = this.value\n\t\t\t\t} else {\n\t\t\t\t\tel.textContent = ''\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected render(): unknown {\n\t\tif (this.editable) {\n\t\t\treturn html`<div\n\t\t\t\t${ref(this._editRef)}\n\t\t\t\tclass=\"edit\"\n\t\t\t\tcontenteditable=\"true\"\n\t\t\t\tdata-placeholder=${this.placeholder ?? ''}\n\t\t\t></div>`\n\t\t}\n\t\treturn html`<slot></slot>`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-typography': SchmancyTypography\n\t}\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"typography.cjs","names":[],"sources":["../src/typography/typography.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/tailwind.mixin'\nimport { css, html, type PropertyValues } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { html as staticHtml, literal } from 'lit/static-html.js'\nimport { fromEvent } from 'rxjs'\nimport { filter, tap, takeUntil } from 'rxjs/operators'\n\n/**\n * Preset → (type, token) shorthand. Saves the two-decision-per-text-node\n * fatigue that 50+ typography nodes in a single page cause.\n */\nexport type TypographyPreset =\n\t| 'display' | 'display-lg' | 'display-md' | 'display-sm'\n\t| 'heading-lg' | 'heading-md' | 'heading-sm'\n\t| 'title-lg' | 'title-md' | 'title-sm'\n\t| 'body-lg' | 'body-md' | 'body-sm'\n\t| 'label-lg' | 'label-md' | 'label-sm'\n\t| 'caption'\n\nconst PRESET_MAP: Record<TypographyPreset, { type: string; token: string }> = {\n\t'display': { type: 'display', token: 'lg' },\n\t'display-lg': { type: 'display', token: 'lg' },\n\t'display-md': { type: 'display', token: 'md' },\n\t'display-sm': { type: 'display', token: 'sm' },\n\t'heading-lg': { type: 'headline', token: 'lg' },\n\t'heading-md': { type: 'headline', token: 'md' },\n\t'heading-sm': { type: 'headline', token: 'sm' },\n\t'title-lg': { type: 'title', token: 'lg' },\n\t'title-md': { type: 'title', token: 'md' },\n\t'title-sm': { type: 'title', token: 'sm' },\n\t'body-lg': { type: 'body', token: 'lg' },\n\t'body-md': { type: 'body', token: 'md' },\n\t'body-sm': { type: 'body', token: 'sm' },\n\t'label-lg': { type: 'label', token: 'lg' },\n\t'label-md': { type: 'label', token: 'md' },\n\t'label-sm': { type: 'label', token: 'sm' },\n\t'caption': { type: 'label', token: 'sm' },\n}\n\n/**\n * Allowed semantic tag names for the `as` prop. Closed enum so we can\n * use `literal` template parts safely with `lit/static-html`.\n */\nexport type TypographyTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'div'\n\nconst TAG_LITERALS: Record<TypographyTag, ReturnType<typeof literal>> = {\n\th1: literal`h1`,\n\th2: literal`h2`,\n\th3: literal`h3`,\n\th4: literal`h4`,\n\th5: literal`h5`,\n\th6: literal`h6`,\n\tp: literal`p`,\n\tspan: literal`span`,\n\tdiv: literal`div`,\n}\n\n// Material Design 3 typography - https://m3.material.io/styles/typography/type-scale-tokens\n\n/**\n * @element schmancy-typography\n * @slot - The text for the typography.\n */\n@customElement('schmancy-typography')\nexport class SchmancyTypography extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tfont-family: inherit;\n\t\thyphens: none;\n\t}\n\n\t/* Text alignment */\n\t:host([align='center']) {\n\t\ttext-align: center;\n\t}\n\n\t:host([align='left']) {\n\t\ttext-align: start;\n\t}\n\n\t:host([align='right']) {\n\t\ttext-align: right;\n\t}\n\n\t:host([align='justify']) {\n\t\ttext-align: justify;\n\t}\n\n\t/* Font weight */\n\t:host([weight='bold']) {\n\t\tfont-weight: 700;\n\t}\n\n\t:host([weight='medium']) {\n\t\tfont-weight: 500;\n\t}\n\n\t:host([weight='normal']) {\n\t\tfont-weight: 400;\n\t}\n\n\t/* Text transform */\n\t:host([transform='uppercase']) {\n\t\ttext-transform: uppercase;\n\t}\n\n\t:host([transform='lowercase']) {\n\t\ttext-transform: lowercase;\n\t}\n\n\t:host([transform='capitalize']) {\n\t\ttext-transform: capitalize;\n\t}\n\n\t:host([transform='normal']) {\n\t\ttext-transform: none;\n\t}\n\n\t/* Type-based weight defaults (when using Tailwind classes without token) */\n\t:host([type='display']),\n\t:host([type='headline']),\n\t:host([type='body']) {\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='label']),\n\t:host([type='subtitle']),\n\t:host([type='title']) {\n\t\tfont-weight: 500;\n\t}\n\n\t/* Display typography variants - Material Design 3 + Extended */\n\t:host([type='display'][token='xl']) {\n\t\tfont-size: 72px;\n\t\tline-height: 80px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='lg']) {\n\t\tfont-size: 57px;\n\t\tline-height: 64px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='md']) {\n\t\tfont-size: 45px;\n\t\tline-height: 52px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='sm']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='xs']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Headline typography variants - Material Design 3 + Extended */\n\t:host([type='headline'][token='xl']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='lg']) {\n\t\tfont-size: 32px;\n\t\tline-height: 40px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='md']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='sm']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='xs']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Title typography variants - Material Design 3 + Extended */\n\t:host([type='title'][token='xl']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='lg']) {\n\t\tfont-size: 22px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Subtitle typography variants - Extended from Material Design 3 */\n\t:host([type='subtitle'][token='xl']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='lg']) {\n\t\tfont-size: 18px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Body typography variants - Material Design 3 + Extended */\n\t:host([type='body'][token='xl']) {\n\t\tfont-size: 18px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='lg']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='md']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='sm']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Label typography variants - Material Design 3 + Extended */\n\t:host([type='label'][token='xl']) {\n\t\tfont-size: 16px;\n\t\tline-height: 22px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='lg']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='md']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='sm']) {\n\t\tfont-size: 11px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Note: Custom letter-spacing, font-size, and line-height should be applied via inline styles or Tailwind classes */\n\n\t:host([editable]) {\n\t\tcursor: text;\n\t\tborder-radius: 4px;\n\t\ttransition: background 150ms;\n\t\tmin-height: 1em;\n\t}\n\t/* Editable div lives in shadow DOM so light DOM (Lit markers) is untouched */\n\t.edit {\n\t\toutline: none;\n\t\tmin-height: 1em;\n\t\tfont: inherit;\n\t\tcolor: inherit;\n\t\tletter-spacing: inherit;\n\t\tline-height: inherit;\n\t}\n\t.edit:empty::before {\n\t\tcontent: attr(data-placeholder);\n\t\tpointer-events: none;\n\t\tdisplay: block;\n\t\topacity: 0.35;\n\t}\n`) {\n\tstatic shadowRootOptions: ShadowRootInit = {\n\t\tmode: 'open',\n\t\tdelegatesFocus: true,\n\t}\n\n\t/**\n\t * @attr type - The type of the typography.\n\t * @default 'body'\n\t * @type {'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label'}\n\t */\n\t@property({ type: String, reflect: true })\n\ttype: 'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label' = 'body'\n\n\t/**\n\t * Shorthand for picking a (type, token) pair in one go. When set, derives\n\t * `type` and `token` automatically — saves the two-decisions-per-text-node\n\t * fatigue that hits when a single page has 50+ typography nodes.\n\t *\n\t * @attr preset\n\t * @type {TypographyPreset}\n\t * @example <schmancy-typography preset=\"heading-md\">Title</schmancy-typography>\n\t */\n\t@property({ type: String, reflect: true })\n\tpreset?: TypographyPreset\n\n\t/**\n\t * Render the slot wrapped in the requested semantic HTML element so screen\n\t * readers expose the right role / heading level. Without `as`, the slot\n\t * sits directly in the shadow root and the host is a generic element.\n\t *\n\t * @attr as\n\t * @type {TypographyTag}\n\t * @example <schmancy-typography preset=\"heading-md\" as=\"h2\">Section</schmancy-typography>\n\t */\n\t@property({ type: String, reflect: true })\n\tas?: TypographyTag\n\n\t/**\n\t * @attr token - The size token.\n\t * @deprecated Prefer using Tailwind responsive text classes for better responsive design.\n\t * Set token=\"\" and use class=\"text-sm md:text-base lg:text-lg\" instead.\n\t * Example: <schmancy-typography type=\"display\" token=\"\" class=\"text-2xl sm:text-3xl md:text-4xl\">\n\t * @default 'md'\n\t * @type {'xs' | 'sm' | 'md' | 'lg' | 'xl' | ''}\n\t */\n\t@property({ type: String, reflect: true })\n\ttoken: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '' = 'md'\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'left' |'center' |'right'}\n\t */\n\t@property({ type: String, reflect: true })\n\talign: 'left' | 'center' | 'justify' | 'right' | undefined\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'normal' | 'medium' |'bold'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true })\n\tweight: 'normal' | 'medium' | 'bold' | undefined\n\t\n\t/**\n\t *\n\t * @attr\n\t * @default inherit\n\t * @type {'uppercase' |'lowercase' |'capitalize' |'normal'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true }) \n\ttransform: 'uppercase' | 'lowercase' | 'capitalize' | 'normal' | undefined\n\n\t@property({ type: Number })\n\tmaxLines: 1 | 2 | 3 | 4 | 5 | 6 | undefined\n\n\t/** When true, the element becomes contenteditable and dispatches 'change' events on blur/Enter */\n\t@property({ type: Boolean, reflect: true }) editable = false\n\t/** The text value when in editable mode. Set via property binding: .value=${...} */\n\t@property({ type: String }) value = ''\n\t/** Placeholder shown when editable and empty */\n\t@property({ type: String }) placeholder = ''\n\n\tprivate _editRef = createRef<HTMLDivElement>()\n\n\tprotected override willUpdate(changed: PropertyValues): void {\n\t\tsuper.willUpdate?.(changed)\n\t\t// `preset` shorthand expands to (type, token) so the existing CSS\n\t\t// selectors keep matching without duplicating the size scale.\n\t\tif (changed.has('preset') && this.preset && PRESET_MAP[this.preset]) {\n\t\t\tconst { type, token } = PRESET_MAP[this.preset]\n\t\t\tthis.type = type as typeof this.type\n\t\t\tthis.token = token as typeof this.token\n\t\t}\n\t}\n\n\t/** Focus and select all text in editable mode */\n\tselectAll() {\n\t\tconst el = this._editRef.value\n\t\tif (!el) return\n\t\tel.focus()\n\t\tconst sel = window.getSelection()\n\t\tif (sel && el.textContent) {\n\t\t\tconst range = document.createRange()\n\t\t\trange.selectNodeContents(el)\n\t\t\tsel.removeAllRanges()\n\t\t\tsel.addRange(range)\n\t\t}\n\t}\n\n\tconnectedCallback() {\n\t\tsuper.connectedCallback()\n\n\t\tfromEvent<FocusEvent>(this, 'focusout').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (!el) return\n\t\t\t\tconst newValue = el.innerText.trim()\n\t\t\t\tif (newValue !== this.value) {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('change', {\n\t\t\t\t\t\tdetail: { value: newValue },\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t\t// Ensure truly empty so :empty CSS placeholder works\n\t\t\t\tif (!newValue) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\t// Clean stray <br> / whitespace nodes so :empty CSS matches\n\t\tfromEvent(this, 'input').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (el && !el.innerText.trim()) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\tfromEvent<KeyboardEvent>(this, 'keydown').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\tfilter(e => e.key === 'Enter'),\n\t\t\ttap(e => { e.preventDefault(); (this._editRef.value ?? this).blur() }),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\t}\n\n\tprotected updated(changedProperties: Map<string, unknown>): void {\n\t\tsuper.updated(changedProperties)\n\t\tif (changedProperties.has('maxLines')) {\n\t\t\t// Remove all line-clamp classes\n\t\t\tthis.classList.remove('line-clamp-1', 'line-clamp-2', 'line-clamp-3', 'line-clamp-4', 'line-clamp-5', 'line-clamp-6')\n\t\t\t// Add the appropriate one\n\t\t\tif (this.maxLines) {\n\t\t\t\tthis.classList.add(`line-clamp-${this.maxLines}`)\n\t\t\t}\n\t\t}\n\t\tif ((changedProperties.has('value') || changedProperties.has('editable')) && this.editable) {\n\t\t\tconst el = this._editRef.value\n\t\t\tif (el && document.activeElement !== el) {\n\t\t\t\tif (this.value) {\n\t\t\t\t\tel.innerText = this.value\n\t\t\t\t} else {\n\t\t\t\t\tel.textContent = ''\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected render(): unknown {\n\t\tif (this.editable) {\n\t\t\treturn html`<div\n\t\t\t\t${ref(this._editRef)}\n\t\t\t\tclass=\"edit\"\n\t\t\t\tcontenteditable=\"true\"\n\t\t\t\tdata-placeholder=${this.placeholder ?? ''}\n\t\t\t></div>`\n\t\t}\n\t\t// `as` wraps the slot in the requested semantic tag so heading levels\n\t\t// land in the accessibility tree. Without `as` the slot is bare and\n\t\t// the host element carries the visual styling only.\n\t\tif (this.as && TAG_LITERALS[this.as]) {\n\t\t\tconst tag = TAG_LITERALS[this.as]\n\t\t\treturn staticHtml`<${tag}><slot></slot></${tag}>`\n\t\t}\n\t\treturn html`<slot></slot>`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-typography': SchmancyTypography\n\t}\n}"],"mappings":"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"}
|
package/dist/typography.js
CHANGED
|
@@ -5,7 +5,87 @@ import { filter as r, takeUntil as i, tap as a } from "rxjs/operators";
|
|
|
5
5
|
import { customElement as o, property as s } from "lit/decorators.js";
|
|
6
6
|
import { css as c, html as l } from "lit";
|
|
7
7
|
import { createRef as u, ref as d } from "lit/directives/ref.js";
|
|
8
|
-
|
|
8
|
+
import { html as f, literal as p } from "lit/static-html.js";
|
|
9
|
+
var m = {
|
|
10
|
+
display: {
|
|
11
|
+
type: "display",
|
|
12
|
+
token: "lg"
|
|
13
|
+
},
|
|
14
|
+
"display-lg": {
|
|
15
|
+
type: "display",
|
|
16
|
+
token: "lg"
|
|
17
|
+
},
|
|
18
|
+
"display-md": {
|
|
19
|
+
type: "display",
|
|
20
|
+
token: "md"
|
|
21
|
+
},
|
|
22
|
+
"display-sm": {
|
|
23
|
+
type: "display",
|
|
24
|
+
token: "sm"
|
|
25
|
+
},
|
|
26
|
+
"heading-lg": {
|
|
27
|
+
type: "headline",
|
|
28
|
+
token: "lg"
|
|
29
|
+
},
|
|
30
|
+
"heading-md": {
|
|
31
|
+
type: "headline",
|
|
32
|
+
token: "md"
|
|
33
|
+
},
|
|
34
|
+
"heading-sm": {
|
|
35
|
+
type: "headline",
|
|
36
|
+
token: "sm"
|
|
37
|
+
},
|
|
38
|
+
"title-lg": {
|
|
39
|
+
type: "title",
|
|
40
|
+
token: "lg"
|
|
41
|
+
},
|
|
42
|
+
"title-md": {
|
|
43
|
+
type: "title",
|
|
44
|
+
token: "md"
|
|
45
|
+
},
|
|
46
|
+
"title-sm": {
|
|
47
|
+
type: "title",
|
|
48
|
+
token: "sm"
|
|
49
|
+
},
|
|
50
|
+
"body-lg": {
|
|
51
|
+
type: "body",
|
|
52
|
+
token: "lg"
|
|
53
|
+
},
|
|
54
|
+
"body-md": {
|
|
55
|
+
type: "body",
|
|
56
|
+
token: "md"
|
|
57
|
+
},
|
|
58
|
+
"body-sm": {
|
|
59
|
+
type: "body",
|
|
60
|
+
token: "sm"
|
|
61
|
+
},
|
|
62
|
+
"label-lg": {
|
|
63
|
+
type: "label",
|
|
64
|
+
token: "lg"
|
|
65
|
+
},
|
|
66
|
+
"label-md": {
|
|
67
|
+
type: "label",
|
|
68
|
+
token: "md"
|
|
69
|
+
},
|
|
70
|
+
"label-sm": {
|
|
71
|
+
type: "label",
|
|
72
|
+
token: "sm"
|
|
73
|
+
},
|
|
74
|
+
caption: {
|
|
75
|
+
type: "label",
|
|
76
|
+
token: "sm"
|
|
77
|
+
}
|
|
78
|
+
}, h = {
|
|
79
|
+
h1: p`h1`,
|
|
80
|
+
h2: p`h2`,
|
|
81
|
+
h3: p`h3`,
|
|
82
|
+
h4: p`h4`,
|
|
83
|
+
h5: p`h5`,
|
|
84
|
+
h6: p`h6`,
|
|
85
|
+
p: p`p`,
|
|
86
|
+
span: p`span`,
|
|
87
|
+
div: p`div`
|
|
88
|
+
}, g = class extends e(c`
|
|
9
89
|
:host {
|
|
10
90
|
display: block;
|
|
11
91
|
font-family: inherit;
|
|
@@ -291,6 +371,12 @@ var f = class extends e(c`
|
|
|
291
371
|
delegatesFocus: !0
|
|
292
372
|
};
|
|
293
373
|
}
|
|
374
|
+
willUpdate(e) {
|
|
375
|
+
if (super.willUpdate?.(e), e.has("preset") && this.preset && m[this.preset]) {
|
|
376
|
+
let { type: e, token: t } = m[this.preset];
|
|
377
|
+
this.type = e, this.token = t;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
294
380
|
selectAll() {
|
|
295
381
|
let e = this._editRef.value;
|
|
296
382
|
if (!e) return;
|
|
@@ -325,31 +411,42 @@ var f = class extends e(c`
|
|
|
325
411
|
}
|
|
326
412
|
}
|
|
327
413
|
render() {
|
|
328
|
-
|
|
414
|
+
if (this.editable) return l`<div
|
|
329
415
|
${d(this._editRef)}
|
|
330
416
|
class="edit"
|
|
331
417
|
contenteditable="true"
|
|
332
418
|
data-placeholder=${this.placeholder ?? ""}
|
|
333
|
-
></div
|
|
419
|
+
></div>`;
|
|
420
|
+
if (this.as && h[this.as]) {
|
|
421
|
+
let e = h[this.as];
|
|
422
|
+
return f`<${e}><slot></slot></${e}>`;
|
|
423
|
+
}
|
|
424
|
+
return l`<slot></slot>`;
|
|
334
425
|
}
|
|
335
426
|
};
|
|
336
427
|
t([s({
|
|
337
428
|
type: String,
|
|
338
429
|
reflect: !0
|
|
339
|
-
})],
|
|
430
|
+
})], g.prototype, "type", void 0), t([s({
|
|
431
|
+
type: String,
|
|
432
|
+
reflect: !0
|
|
433
|
+
})], g.prototype, "preset", void 0), t([s({
|
|
434
|
+
type: String,
|
|
435
|
+
reflect: !0
|
|
436
|
+
})], g.prototype, "as", void 0), t([s({
|
|
340
437
|
type: String,
|
|
341
438
|
reflect: !0
|
|
342
|
-
})],
|
|
439
|
+
})], g.prototype, "token", void 0), t([s({
|
|
343
440
|
type: String,
|
|
344
441
|
reflect: !0
|
|
345
|
-
})],
|
|
442
|
+
})], g.prototype, "align", void 0), t([s({
|
|
346
443
|
type: String,
|
|
347
444
|
reflect: !0
|
|
348
|
-
})],
|
|
445
|
+
})], g.prototype, "weight", void 0), t([s({
|
|
349
446
|
type: String,
|
|
350
447
|
reflect: !0
|
|
351
|
-
})],
|
|
448
|
+
})], g.prototype, "transform", void 0), t([s({ type: Number })], g.prototype, "maxLines", void 0), t([s({
|
|
352
449
|
type: Boolean,
|
|
353
450
|
reflect: !0
|
|
354
|
-
})],
|
|
355
|
-
export {
|
|
451
|
+
})], g.prototype, "editable", void 0), t([s({ type: String })], g.prototype, "value", void 0), t([s({ type: String })], g.prototype, "placeholder", void 0), g = t([o("schmancy-typography")], g);
|
|
452
|
+
export { g as SchmancyTypography };
|
package/dist/typography.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typography.js","names":[],"sources":["../src/typography/typography.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/tailwind.mixin'\nimport { css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { fromEvent } from 'rxjs'\nimport { filter, tap, takeUntil } from 'rxjs/operators'\n\n// Material Design 3 typography - https://m3.material.io/styles/typography/type-scale-tokens\n\n/**\n * @element schmancy-typography\n * @slot - The text for the typography.\n */\n@customElement('schmancy-typography')\nexport class SchmancyTypography extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tfont-family: inherit;\n\t\thyphens: none;\n\t}\n\n\t/* Text alignment */\n\t:host([align='center']) {\n\t\ttext-align: center;\n\t}\n\n\t:host([align='left']) {\n\t\ttext-align: start;\n\t}\n\n\t:host([align='right']) {\n\t\ttext-align: right;\n\t}\n\n\t:host([align='justify']) {\n\t\ttext-align: justify;\n\t}\n\n\t/* Font weight */\n\t:host([weight='bold']) {\n\t\tfont-weight: 700;\n\t}\n\n\t:host([weight='medium']) {\n\t\tfont-weight: 500;\n\t}\n\n\t:host([weight='normal']) {\n\t\tfont-weight: 400;\n\t}\n\n\t/* Text transform */\n\t:host([transform='uppercase']) {\n\t\ttext-transform: uppercase;\n\t}\n\n\t:host([transform='lowercase']) {\n\t\ttext-transform: lowercase;\n\t}\n\n\t:host([transform='capitalize']) {\n\t\ttext-transform: capitalize;\n\t}\n\n\t:host([transform='normal']) {\n\t\ttext-transform: none;\n\t}\n\n\t/* Type-based weight defaults (when using Tailwind classes without token) */\n\t:host([type='display']),\n\t:host([type='headline']),\n\t:host([type='body']) {\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='label']),\n\t:host([type='subtitle']),\n\t:host([type='title']) {\n\t\tfont-weight: 500;\n\t}\n\n\t/* Display typography variants - Material Design 3 + Extended */\n\t:host([type='display'][token='xl']) {\n\t\tfont-size: 72px;\n\t\tline-height: 80px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='lg']) {\n\t\tfont-size: 57px;\n\t\tline-height: 64px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='md']) {\n\t\tfont-size: 45px;\n\t\tline-height: 52px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='sm']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='xs']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Headline typography variants - Material Design 3 + Extended */\n\t:host([type='headline'][token='xl']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='lg']) {\n\t\tfont-size: 32px;\n\t\tline-height: 40px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='md']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='sm']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='xs']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Title typography variants - Material Design 3 + Extended */\n\t:host([type='title'][token='xl']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='lg']) {\n\t\tfont-size: 22px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Subtitle typography variants - Extended from Material Design 3 */\n\t:host([type='subtitle'][token='xl']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='lg']) {\n\t\tfont-size: 18px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Body typography variants - Material Design 3 + Extended */\n\t:host([type='body'][token='xl']) {\n\t\tfont-size: 18px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='lg']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='md']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='sm']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Label typography variants - Material Design 3 + Extended */\n\t:host([type='label'][token='xl']) {\n\t\tfont-size: 16px;\n\t\tline-height: 22px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='lg']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='md']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='sm']) {\n\t\tfont-size: 11px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Note: Custom letter-spacing, font-size, and line-height should be applied via inline styles or Tailwind classes */\n\n\t:host([editable]) {\n\t\tcursor: text;\n\t\tborder-radius: 4px;\n\t\ttransition: background 150ms;\n\t\tmin-height: 1em;\n\t}\n\t/* Editable div lives in shadow DOM so light DOM (Lit markers) is untouched */\n\t.edit {\n\t\toutline: none;\n\t\tmin-height: 1em;\n\t\tfont: inherit;\n\t\tcolor: inherit;\n\t\tletter-spacing: inherit;\n\t\tline-height: inherit;\n\t}\n\t.edit:empty::before {\n\t\tcontent: attr(data-placeholder);\n\t\tpointer-events: none;\n\t\tdisplay: block;\n\t\topacity: 0.35;\n\t}\n`) {\n\tstatic shadowRootOptions: ShadowRootInit = {\n\t\tmode: 'open',\n\t\tdelegatesFocus: true,\n\t}\n\n\t/**\n\t * @attr type - The type of the typography.\n\t * @default 'body'\n\t * @type {'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label'}\n\t */\n\t@property({ type: String, reflect: true })\n\ttype: 'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label' = 'body'\n\n\t/**\n\t * @attr token - The size token.\n\t * @deprecated Prefer using Tailwind responsive text classes for better responsive design.\n\t * Set token=\"\" and use class=\"text-sm md:text-base lg:text-lg\" instead.\n\t * Example: <schmancy-typography type=\"display\" token=\"\" class=\"text-2xl sm:text-3xl md:text-4xl\">\n\t * @default 'md'\n\t * @type {'xs' | 'sm' | 'md' | 'lg' | 'xl' | ''}\n\t */\n\t@property({ type: String, reflect: true })\n\ttoken: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '' = 'md'\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'left' |'center' |'right'}\n\t */\n\t@property({ type: String, reflect: true })\n\talign: 'left' | 'center' | 'justify' | 'right' | undefined\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'normal' | 'medium' |'bold'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true })\n\tweight: 'normal' | 'medium' | 'bold' | undefined\n\t\n\t/**\n\t *\n\t * @attr\n\t * @default inherit\n\t * @type {'uppercase' |'lowercase' |'capitalize' |'normal'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true }) \n\ttransform: 'uppercase' | 'lowercase' | 'capitalize' | 'normal' | undefined\n\n\t@property({ type: Number })\n\tmaxLines: 1 | 2 | 3 | 4 | 5 | 6 | undefined\n\n\t/** When true, the element becomes contenteditable and dispatches 'change' events on blur/Enter */\n\t@property({ type: Boolean, reflect: true }) editable = false\n\t/** The text value when in editable mode. Set via property binding: .value=${...} */\n\t@property({ type: String }) value = ''\n\t/** Placeholder shown when editable and empty */\n\t@property({ type: String }) placeholder = ''\n\n\tprivate _editRef = createRef<HTMLDivElement>()\n\n\t/** Focus and select all text in editable mode */\n\tselectAll() {\n\t\tconst el = this._editRef.value\n\t\tif (!el) return\n\t\tel.focus()\n\t\tconst sel = window.getSelection()\n\t\tif (sel && el.textContent) {\n\t\t\tconst range = document.createRange()\n\t\t\trange.selectNodeContents(el)\n\t\t\tsel.removeAllRanges()\n\t\t\tsel.addRange(range)\n\t\t}\n\t}\n\n\tconnectedCallback() {\n\t\tsuper.connectedCallback()\n\n\t\tfromEvent<FocusEvent>(this, 'focusout').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (!el) return\n\t\t\t\tconst newValue = el.innerText.trim()\n\t\t\t\tif (newValue !== this.value) {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('change', {\n\t\t\t\t\t\tdetail: { value: newValue },\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t\t// Ensure truly empty so :empty CSS placeholder works\n\t\t\t\tif (!newValue) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\t// Clean stray <br> / whitespace nodes so :empty CSS matches\n\t\tfromEvent(this, 'input').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (el && !el.innerText.trim()) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\tfromEvent<KeyboardEvent>(this, 'keydown').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\tfilter(e => e.key === 'Enter'),\n\t\t\ttap(e => { e.preventDefault(); (this._editRef.value ?? this).blur() }),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\t}\n\n\tprotected updated(changedProperties: Map<string, unknown>): void {\n\t\tsuper.updated(changedProperties)\n\t\tif (changedProperties.has('maxLines')) {\n\t\t\t// Remove all line-clamp classes\n\t\t\tthis.classList.remove('line-clamp-1', 'line-clamp-2', 'line-clamp-3', 'line-clamp-4', 'line-clamp-5', 'line-clamp-6')\n\t\t\t// Add the appropriate one\n\t\t\tif (this.maxLines) {\n\t\t\t\tthis.classList.add(`line-clamp-${this.maxLines}`)\n\t\t\t}\n\t\t}\n\t\tif ((changedProperties.has('value') || changedProperties.has('editable')) && this.editable) {\n\t\t\tconst el = this._editRef.value\n\t\t\tif (el && document.activeElement !== el) {\n\t\t\t\tif (this.value) {\n\t\t\t\t\tel.innerText = this.value\n\t\t\t\t} else {\n\t\t\t\t\tel.textContent = ''\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected render(): unknown {\n\t\tif (this.editable) {\n\t\t\treturn html`<div\n\t\t\t\t${ref(this._editRef)}\n\t\t\t\tclass=\"edit\"\n\t\t\t\tcontenteditable=\"true\"\n\t\t\t\tdata-placeholder=${this.placeholder ?? ''}\n\t\t\t></div>`\n\t\t}\n\t\treturn html`<slot></slot>`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-typography': SchmancyTypography\n\t}\n}"],"mappings":";;;;;;;AAcO,IAAA,IAAA,cAAiC,EAAgB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAgSe,QAAA,KAAA,QAW1B,MAAA,KAAA,WAAA,CAiCQ,GAAA,KAAA,QAEnB,IAAA,KAAA,cAEM,IAAA,KAAA,WAEvB,GAAA;;CAAA;AAAA,OAAA,oBA7DwB;GAC1C,MAAM;GACN,gBAAA,CAAgB;GAAA;;CA8DjB,YAAA;EACC,IAAM,IAAK,KAAK,SAAS;AACzB,MAAA,CAAK,EAAI;AACT,IAAG,OAAA;EACH,IAAM,IAAM,OAAO,cAAA;AACnB,MAAI,KAAO,EAAG,aAAa;GAC1B,IAAM,IAAQ,SAAS,aAAA;AACvB,KAAM,mBAAmB,EAAA,EACzB,EAAI,iBAAA,EACJ,EAAI,SAAS,EAAA;;;CAIf,oBAAA;AACC,QAAM,mBAAA,EAEN,EAAsB,MAAM,WAAA,CAAY,KACvC,QAAa,KAAK,SAAA,EAClB,QAAA;GACC,IAAM,IAAK,KAAK,SAAS;AACzB,OAAA,CAAK,EAAI;GACT,IAAM,IAAW,EAAG,UAAU,MAAA;AAC1B,SAAa,KAAK,SACrB,KAAK,cAAc,IAAI,YAAY,UAAU;IAC5C,QAAQ,EAAE,OAAO,GAAA;IACjB,SAAA,CAAS;IACT,UAAA,CAAU;IAAA,CAAA,CAAA,EAIP,MAAU,EAAG,cAAc;IAAA,EAEjC,EAAU,KAAK,cAAA,CAAA,CACd,WAAA,EAGF,EAAU,MAAM,QAAA,CAAS,KACxB,QAAa,KAAK,SAAA,EAClB,QAAA;GACC,IAAM,IAAK,KAAK,SAAS;AACrB,QAAA,CAAO,EAAG,UAAU,MAAA,KAAQ,EAAG,cAAc;IAAA,EAElD,EAAU,KAAK,cAAA,CAAA,CACd,WAAA,EAEF,EAAyB,MAAM,UAAA,CAAW,KACzC,QAAa,KAAK,SAAA,EAClB,GAAO,MAAK,EAAE,QAAQ,QAAR,EACd,GAAI,MAAA;AAAO,KAAE,gBAAA,GAAmB,KAAK,SAAS,SAAS,MAAM,MAAA;IAAA,EAC7D,EAAU,KAAK,cAAA,CAAA,CACd,WAAA;;CAGH,QAAkB,GAAA;AAUjB,MATA,MAAM,QAAQ,EAAA,EACV,EAAkB,IAAI,WAAA,KAEzB,KAAK,UAAU,OAAO,gBAAgB,gBAAgB,gBAAgB,gBAAgB,gBAAgB,eAAA,EAElG,KAAK,YACR,KAAK,UAAU,IAAI,cAAc,KAAK,WAAA,IAGnC,EAAkB,IAAI,QAAA,IAAY,EAAkB,IAAI,WAAA,KAAgB,KAAK,UAAU;GAC3F,IAAM,IAAK,KAAK,SAAS;AACrB,QAAM,SAAS,kBAAkB,MAChC,KAAK,QACR,EAAG,YAAY,KAAK,QAEpB,EAAG,cAAc;;;CAMrB,SAAA;AACC,SAAI,KAAK,WACD,CAAI;MACR,EAAI,KAAK,SAAA,CAAA;;;uBAGQ,KAAK,eAAe,GAAA;cAGlC,CAAI;;;AAAA,EAAA,CA1IX,EAAS;CAAE,MAAM;CAAQ,SAAA,CAAS;CAAA,CAAA,CAAA,EAAO,EAAA,WAAA,QAAA,KAAA,EAAA,EAAA,EAAA,CAWzC,EAAS;CAAE,MAAM;CAAQ,SAAA,CAAS;CAAA,CAAA,CAAA,EAAO,EAAA,WAAA,SAAA,KAAA,EAAA,EAAA,EAAA,CAQzC,EAAS;CAAE,MAAM;CAAQ,SAAA,CAAS;CAAA,CAAA,CAAA,EAAO,EAAA,WAAA,SAAA,KAAA,EAAA,EAAA,EAAA,CASzC,EAAS;CAAE,MAAM;CAAQ,SAAA,CAAS;CAAA,CAAA,CAAA,EAAO,EAAA,WAAA,UAAA,KAAA,EAAA,EAAA,EAAA,CAUzC,EAAS;CAAE,MAAM;CAAQ,SAAA,CAAS;CAAA,CAAA,CAAA,EAAO,EAAA,WAAA,aAAA,KAAA,EAAA,EAAA,EAAA,CAGzC,EAAS,EAAE,MAAM,QAAA,CAAA,CAAA,EAAS,EAAA,WAAA,YAAA,KAAA,EAAA,EAAA,EAAA,CAI1B,EAAS;CAAE,MAAM;CAAS,SAAA,CAAS;CAAA,CAAA,CAAA,EAAO,EAAA,WAAA,YAAA,KAAA,EAAA,EAAA,EAAA,CAE1C,EAAS,EAAE,MAAM,QAAA,CAAA,CAAA,EAAS,EAAA,WAAA,SAAA,KAAA,EAAA,EAAA,EAAA,CAE1B,EAAS,EAAE,MAAM,QAAA,CAAA,CAAA,EAAS,EAAA,WAAA,eAAA,KAAA,EAAA,EAAA,IAAA,EAAA,CAjV3B,EAAc,sBAAA,CAAA,EAAsB,EAAA;AAAA,SAAA,KAAA"}
|
|
1
|
+
{"version":3,"file":"typography.js","names":[],"sources":["../src/typography/typography.ts"],"sourcesContent":["import { TailwindElement } from '@mixins/tailwind.mixin'\nimport { css, html, type PropertyValues } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { createRef, ref } from 'lit/directives/ref.js'\nimport { html as staticHtml, literal } from 'lit/static-html.js'\nimport { fromEvent } from 'rxjs'\nimport { filter, tap, takeUntil } from 'rxjs/operators'\n\n/**\n * Preset → (type, token) shorthand. Saves the two-decision-per-text-node\n * fatigue that 50+ typography nodes in a single page cause.\n */\nexport type TypographyPreset =\n\t| 'display' | 'display-lg' | 'display-md' | 'display-sm'\n\t| 'heading-lg' | 'heading-md' | 'heading-sm'\n\t| 'title-lg' | 'title-md' | 'title-sm'\n\t| 'body-lg' | 'body-md' | 'body-sm'\n\t| 'label-lg' | 'label-md' | 'label-sm'\n\t| 'caption'\n\nconst PRESET_MAP: Record<TypographyPreset, { type: string; token: string }> = {\n\t'display': { type: 'display', token: 'lg' },\n\t'display-lg': { type: 'display', token: 'lg' },\n\t'display-md': { type: 'display', token: 'md' },\n\t'display-sm': { type: 'display', token: 'sm' },\n\t'heading-lg': { type: 'headline', token: 'lg' },\n\t'heading-md': { type: 'headline', token: 'md' },\n\t'heading-sm': { type: 'headline', token: 'sm' },\n\t'title-lg': { type: 'title', token: 'lg' },\n\t'title-md': { type: 'title', token: 'md' },\n\t'title-sm': { type: 'title', token: 'sm' },\n\t'body-lg': { type: 'body', token: 'lg' },\n\t'body-md': { type: 'body', token: 'md' },\n\t'body-sm': { type: 'body', token: 'sm' },\n\t'label-lg': { type: 'label', token: 'lg' },\n\t'label-md': { type: 'label', token: 'md' },\n\t'label-sm': { type: 'label', token: 'sm' },\n\t'caption': { type: 'label', token: 'sm' },\n}\n\n/**\n * Allowed semantic tag names for the `as` prop. Closed enum so we can\n * use `literal` template parts safely with `lit/static-html`.\n */\nexport type TypographyTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'div'\n\nconst TAG_LITERALS: Record<TypographyTag, ReturnType<typeof literal>> = {\n\th1: literal`h1`,\n\th2: literal`h2`,\n\th3: literal`h3`,\n\th4: literal`h4`,\n\th5: literal`h5`,\n\th6: literal`h6`,\n\tp: literal`p`,\n\tspan: literal`span`,\n\tdiv: literal`div`,\n}\n\n// Material Design 3 typography - https://m3.material.io/styles/typography/type-scale-tokens\n\n/**\n * @element schmancy-typography\n * @slot - The text for the typography.\n */\n@customElement('schmancy-typography')\nexport class SchmancyTypography extends TailwindElement(css`\n\t:host {\n\t\tdisplay: block;\n\t\tfont-family: inherit;\n\t\thyphens: none;\n\t}\n\n\t/* Text alignment */\n\t:host([align='center']) {\n\t\ttext-align: center;\n\t}\n\n\t:host([align='left']) {\n\t\ttext-align: start;\n\t}\n\n\t:host([align='right']) {\n\t\ttext-align: right;\n\t}\n\n\t:host([align='justify']) {\n\t\ttext-align: justify;\n\t}\n\n\t/* Font weight */\n\t:host([weight='bold']) {\n\t\tfont-weight: 700;\n\t}\n\n\t:host([weight='medium']) {\n\t\tfont-weight: 500;\n\t}\n\n\t:host([weight='normal']) {\n\t\tfont-weight: 400;\n\t}\n\n\t/* Text transform */\n\t:host([transform='uppercase']) {\n\t\ttext-transform: uppercase;\n\t}\n\n\t:host([transform='lowercase']) {\n\t\ttext-transform: lowercase;\n\t}\n\n\t:host([transform='capitalize']) {\n\t\ttext-transform: capitalize;\n\t}\n\n\t:host([transform='normal']) {\n\t\ttext-transform: none;\n\t}\n\n\t/* Type-based weight defaults (when using Tailwind classes without token) */\n\t:host([type='display']),\n\t:host([type='headline']),\n\t:host([type='body']) {\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='label']),\n\t:host([type='subtitle']),\n\t:host([type='title']) {\n\t\tfont-weight: 500;\n\t}\n\n\t/* Display typography variants - Material Design 3 + Extended */\n\t:host([type='display'][token='xl']) {\n\t\tfont-size: 72px;\n\t\tline-height: 80px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='lg']) {\n\t\tfont-size: 57px;\n\t\tline-height: 64px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='md']) {\n\t\tfont-size: 45px;\n\t\tline-height: 52px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='sm']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='display'][token='xs']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Headline typography variants - Material Design 3 + Extended */\n\t:host([type='headline'][token='xl']) {\n\t\tfont-size: 36px;\n\t\tline-height: 44px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='lg']) {\n\t\tfont-size: 32px;\n\t\tline-height: 40px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='md']) {\n\t\tfont-size: 28px;\n\t\tline-height: 36px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='sm']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='headline'][token='xs']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Title typography variants - Material Design 3 + Extended */\n\t:host([type='title'][token='xl']) {\n\t\tfont-size: 24px;\n\t\tline-height: 32px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='lg']) {\n\t\tfont-size: 22px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='title'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='title'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Subtitle typography variants - Extended from Material Design 3 */\n\t:host([type='subtitle'][token='xl']) {\n\t\tfont-size: 20px;\n\t\tline-height: 28px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='lg']) {\n\t\tfont-size: 18px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='md']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='sm']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='subtitle'][token='xs']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Body typography variants - Material Design 3 + Extended */\n\t:host([type='body'][token='xl']) {\n\t\tfont-size: 18px;\n\t\tline-height: 28px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='lg']) {\n\t\tfont-size: 16px;\n\t\tline-height: 24px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='md']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='sm']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 400;\n\t}\n\n\t:host([type='body'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 400;\n\t}\n\n\t/* Label typography variants - Material Design 3 + Extended */\n\t:host([type='label'][token='xl']) {\n\t\tfont-size: 16px;\n\t\tline-height: 22px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='lg']) {\n\t\tfont-size: 14px;\n\t\tline-height: 20px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='md']) {\n\t\tfont-size: 12px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='sm']) {\n\t\tfont-size: 11px;\n\t\tline-height: 16px;\n\t\tfont-weight: 500;\n\t}\n\n\t:host([type='label'][token='xs']) {\n\t\tfont-size: 10px;\n\t\tline-height: 14px;\n\t\tfont-weight: 500;\n\t}\n\n\t/* Note: Custom letter-spacing, font-size, and line-height should be applied via inline styles or Tailwind classes */\n\n\t:host([editable]) {\n\t\tcursor: text;\n\t\tborder-radius: 4px;\n\t\ttransition: background 150ms;\n\t\tmin-height: 1em;\n\t}\n\t/* Editable div lives in shadow DOM so light DOM (Lit markers) is untouched */\n\t.edit {\n\t\toutline: none;\n\t\tmin-height: 1em;\n\t\tfont: inherit;\n\t\tcolor: inherit;\n\t\tletter-spacing: inherit;\n\t\tline-height: inherit;\n\t}\n\t.edit:empty::before {\n\t\tcontent: attr(data-placeholder);\n\t\tpointer-events: none;\n\t\tdisplay: block;\n\t\topacity: 0.35;\n\t}\n`) {\n\tstatic shadowRootOptions: ShadowRootInit = {\n\t\tmode: 'open',\n\t\tdelegatesFocus: true,\n\t}\n\n\t/**\n\t * @attr type - The type of the typography.\n\t * @default 'body'\n\t * @type {'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label'}\n\t */\n\t@property({ type: String, reflect: true })\n\ttype: 'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label' = 'body'\n\n\t/**\n\t * Shorthand for picking a (type, token) pair in one go. When set, derives\n\t * `type` and `token` automatically — saves the two-decisions-per-text-node\n\t * fatigue that hits when a single page has 50+ typography nodes.\n\t *\n\t * @attr preset\n\t * @type {TypographyPreset}\n\t * @example <schmancy-typography preset=\"heading-md\">Title</schmancy-typography>\n\t */\n\t@property({ type: String, reflect: true })\n\tpreset?: TypographyPreset\n\n\t/**\n\t * Render the slot wrapped in the requested semantic HTML element so screen\n\t * readers expose the right role / heading level. Without `as`, the slot\n\t * sits directly in the shadow root and the host is a generic element.\n\t *\n\t * @attr as\n\t * @type {TypographyTag}\n\t * @example <schmancy-typography preset=\"heading-md\" as=\"h2\">Section</schmancy-typography>\n\t */\n\t@property({ type: String, reflect: true })\n\tas?: TypographyTag\n\n\t/**\n\t * @attr token - The size token.\n\t * @deprecated Prefer using Tailwind responsive text classes for better responsive design.\n\t * Set token=\"\" and use class=\"text-sm md:text-base lg:text-lg\" instead.\n\t * Example: <schmancy-typography type=\"display\" token=\"\" class=\"text-2xl sm:text-3xl md:text-4xl\">\n\t * @default 'md'\n\t * @type {'xs' | 'sm' | 'md' | 'lg' | 'xl' | ''}\n\t */\n\t@property({ type: String, reflect: true })\n\ttoken: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '' = 'md'\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'left' |'center' |'right'}\n\t */\n\t@property({ type: String, reflect: true })\n\talign: 'left' | 'center' | 'justify' | 'right' | undefined\n\n\t/**\n\t * @attr\n\t * @default inherit\n\t * @type {'normal' | 'medium' |'bold'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true })\n\tweight: 'normal' | 'medium' | 'bold' | undefined\n\t\n\t/**\n\t *\n\t * @attr\n\t * @default inherit\n\t * @type {'uppercase' |'lowercase' |'capitalize' |'normal'}\n\t * @public\n\t */\n\t@property({ type: String, reflect: true }) \n\ttransform: 'uppercase' | 'lowercase' | 'capitalize' | 'normal' | undefined\n\n\t@property({ type: Number })\n\tmaxLines: 1 | 2 | 3 | 4 | 5 | 6 | undefined\n\n\t/** When true, the element becomes contenteditable and dispatches 'change' events on blur/Enter */\n\t@property({ type: Boolean, reflect: true }) editable = false\n\t/** The text value when in editable mode. Set via property binding: .value=${...} */\n\t@property({ type: String }) value = ''\n\t/** Placeholder shown when editable and empty */\n\t@property({ type: String }) placeholder = ''\n\n\tprivate _editRef = createRef<HTMLDivElement>()\n\n\tprotected override willUpdate(changed: PropertyValues): void {\n\t\tsuper.willUpdate?.(changed)\n\t\t// `preset` shorthand expands to (type, token) so the existing CSS\n\t\t// selectors keep matching without duplicating the size scale.\n\t\tif (changed.has('preset') && this.preset && PRESET_MAP[this.preset]) {\n\t\t\tconst { type, token } = PRESET_MAP[this.preset]\n\t\t\tthis.type = type as typeof this.type\n\t\t\tthis.token = token as typeof this.token\n\t\t}\n\t}\n\n\t/** Focus and select all text in editable mode */\n\tselectAll() {\n\t\tconst el = this._editRef.value\n\t\tif (!el) return\n\t\tel.focus()\n\t\tconst sel = window.getSelection()\n\t\tif (sel && el.textContent) {\n\t\t\tconst range = document.createRange()\n\t\t\trange.selectNodeContents(el)\n\t\t\tsel.removeAllRanges()\n\t\t\tsel.addRange(range)\n\t\t}\n\t}\n\n\tconnectedCallback() {\n\t\tsuper.connectedCallback()\n\n\t\tfromEvent<FocusEvent>(this, 'focusout').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (!el) return\n\t\t\t\tconst newValue = el.innerText.trim()\n\t\t\t\tif (newValue !== this.value) {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('change', {\n\t\t\t\t\t\tdetail: { value: newValue },\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcomposed: true,\n\t\t\t\t\t}))\n\t\t\t\t}\n\t\t\t\t// Ensure truly empty so :empty CSS placeholder works\n\t\t\t\tif (!newValue) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\t// Clean stray <br> / whitespace nodes so :empty CSS matches\n\t\tfromEvent(this, 'input').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\ttap(() => {\n\t\t\t\tconst el = this._editRef.value\n\t\t\t\tif (el && !el.innerText.trim()) el.textContent = ''\n\t\t\t}),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\n\t\tfromEvent<KeyboardEvent>(this, 'keydown').pipe(\n\t\t\tfilter(() => this.editable),\n\t\t\tfilter(e => e.key === 'Enter'),\n\t\t\ttap(e => { e.preventDefault(); (this._editRef.value ?? this).blur() }),\n\t\t\ttakeUntil(this.disconnecting),\n\t\t).subscribe()\n\t}\n\n\tprotected updated(changedProperties: Map<string, unknown>): void {\n\t\tsuper.updated(changedProperties)\n\t\tif (changedProperties.has('maxLines')) {\n\t\t\t// Remove all line-clamp classes\n\t\t\tthis.classList.remove('line-clamp-1', 'line-clamp-2', 'line-clamp-3', 'line-clamp-4', 'line-clamp-5', 'line-clamp-6')\n\t\t\t// Add the appropriate one\n\t\t\tif (this.maxLines) {\n\t\t\t\tthis.classList.add(`line-clamp-${this.maxLines}`)\n\t\t\t}\n\t\t}\n\t\tif ((changedProperties.has('value') || changedProperties.has('editable')) && this.editable) {\n\t\t\tconst el = this._editRef.value\n\t\t\tif (el && document.activeElement !== el) {\n\t\t\t\tif (this.value) {\n\t\t\t\t\tel.innerText = this.value\n\t\t\t\t} else {\n\t\t\t\t\tel.textContent = ''\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected render(): unknown {\n\t\tif (this.editable) {\n\t\t\treturn html`<div\n\t\t\t\t${ref(this._editRef)}\n\t\t\t\tclass=\"edit\"\n\t\t\t\tcontenteditable=\"true\"\n\t\t\t\tdata-placeholder=${this.placeholder ?? ''}\n\t\t\t></div>`\n\t\t}\n\t\t// `as` wraps the slot in the requested semantic tag so heading levels\n\t\t// land in the accessibility tree. Without `as` the slot is bare and\n\t\t// the host element carries the visual styling only.\n\t\tif (this.as && TAG_LITERALS[this.as]) {\n\t\t\tconst tag = TAG_LITERALS[this.as]\n\t\t\treturn staticHtml`<${tag}><slot></slot></${tag}>`\n\t\t}\n\t\treturn html`<slot></slot>`\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'schmancy-typography': SchmancyTypography\n\t}\n}"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './metric';
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { TailwindElement } from '@mixins/index'
|
|
2
|
+
import { css, html, nothing } from 'lit'
|
|
3
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
4
|
+
|
|
5
|
+
export type MetricTrend = 'up' | 'down' | 'neutral'
|
|
6
|
+
export type MetricSize = 'sm' | 'md' | 'lg'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* KPI metric — a label + value pair for dashboards, with optional trend + delta indicators.
|
|
10
|
+
*
|
|
11
|
+
* @element schmancy-metric
|
|
12
|
+
* @summary The building block of dashboards and meta bars. Pass `label` + `value` for a basic stat; add `trend` / `delta` for the delta-from-last-period pattern. Use multiple metrics side-by-side with Tailwind flex/grid utilities.
|
|
13
|
+
* @example
|
|
14
|
+
* <schmancy-metric label="In flight" value="4"></schmancy-metric>
|
|
15
|
+
* <schmancy-metric label="Open value" value="€165,900" trend="up" delta="+12%"></schmancy-metric>
|
|
16
|
+
* <schmancy-metric label="Error rate" value="0.3%" trend="down" delta="-0.1%"></schmancy-metric>
|
|
17
|
+
* @platform div - Styled `<div>` with two text lines + optional trend arrow. Degrades to a plain div+spans if the tag never registers.
|
|
18
|
+
* @slot - Optional custom value rendering (overrides the `value` attribute if present).
|
|
19
|
+
* @slot label - Optional custom label rendering (overrides the `label` attribute if present).
|
|
20
|
+
* @csspart label - The label line.
|
|
21
|
+
* @csspart value - The value line.
|
|
22
|
+
* @csspart delta - The delta pill (only when `delta` is set).
|
|
23
|
+
*/
|
|
24
|
+
@customElement('schmancy-metric')
|
|
25
|
+
export class SchmancyMetric extends TailwindElement(css`
|
|
26
|
+
:host {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
gap: 0.25rem;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
}
|
|
32
|
+
:host([size='sm']) .label { font-size: 0.6875rem; }
|
|
33
|
+
:host([size='sm']) .value { font-size: 1.25rem; }
|
|
34
|
+
:host([size='md']) .label { font-size: 0.75rem; }
|
|
35
|
+
:host([size='md']) .value { font-size: 1.75rem; }
|
|
36
|
+
:host([size='lg']) .label { font-size: 0.8125rem; }
|
|
37
|
+
:host([size='lg']) .value { font-size: 2.5rem; }
|
|
38
|
+
.label {
|
|
39
|
+
font-weight: 500;
|
|
40
|
+
letter-spacing: 0.08em;
|
|
41
|
+
text-transform: uppercase;
|
|
42
|
+
color: var(--schmancy-sys-color-surface-onVariant);
|
|
43
|
+
}
|
|
44
|
+
.value {
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
line-height: 1.1;
|
|
47
|
+
color: var(--schmancy-sys-color-surface-on);
|
|
48
|
+
font-variant-numeric: tabular-nums;
|
|
49
|
+
}
|
|
50
|
+
.delta {
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 0.125rem;
|
|
54
|
+
font-size: 0.75rem;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
padding: 0.125rem 0.375rem;
|
|
57
|
+
border-radius: 999px;
|
|
58
|
+
font-variant-numeric: tabular-nums;
|
|
59
|
+
}
|
|
60
|
+
.delta[data-trend='up'] {
|
|
61
|
+
color: var(--schmancy-sys-color-success-on);
|
|
62
|
+
background: var(--schmancy-sys-color-success-default);
|
|
63
|
+
}
|
|
64
|
+
.delta[data-trend='down'] {
|
|
65
|
+
color: var(--schmancy-sys-color-error-on);
|
|
66
|
+
background: var(--schmancy-sys-color-error-default);
|
|
67
|
+
}
|
|
68
|
+
.delta[data-trend='neutral'] {
|
|
69
|
+
color: var(--schmancy-sys-color-surface-on);
|
|
70
|
+
background: var(--schmancy-sys-color-surface-containerHigh);
|
|
71
|
+
}
|
|
72
|
+
.arrow {
|
|
73
|
+
font-size: 0.625rem;
|
|
74
|
+
line-height: 1;
|
|
75
|
+
}
|
|
76
|
+
.row {
|
|
77
|
+
display: inline-flex;
|
|
78
|
+
align-items: baseline;
|
|
79
|
+
gap: 0.5rem;
|
|
80
|
+
flex-wrap: wrap;
|
|
81
|
+
}
|
|
82
|
+
`) {
|
|
83
|
+
/** Upper-case caption shown above the value. */
|
|
84
|
+
@property({ type: String }) label = ''
|
|
85
|
+
|
|
86
|
+
/** Primary metric value, rendered large. Pre-format numbers/currency yourself. */
|
|
87
|
+
@property({ type: String }) value = ''
|
|
88
|
+
|
|
89
|
+
/** Optional trend direction. Controls the color + arrow on the delta pill. */
|
|
90
|
+
@property({ type: String, reflect: true }) trend?: MetricTrend
|
|
91
|
+
|
|
92
|
+
/** Optional delta copy displayed in a pill next to the value (e.g. `+12%`). */
|
|
93
|
+
@property({ type: String }) delta?: string
|
|
94
|
+
|
|
95
|
+
/** Size scale affecting label + value typography. */
|
|
96
|
+
@property({ type: String, reflect: true }) size: MetricSize = 'md'
|
|
97
|
+
|
|
98
|
+
private _arrowFor(trend: MetricTrend | undefined): string {
|
|
99
|
+
if (trend === 'up') return '↑'
|
|
100
|
+
if (trend === 'down') return '↓'
|
|
101
|
+
return '→'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
protected render(): unknown {
|
|
105
|
+
const trend = this.trend ?? 'neutral'
|
|
106
|
+
return html`
|
|
107
|
+
<span class="label" part="label"><slot name="label">${this.label}</slot></span>
|
|
108
|
+
<span class="row">
|
|
109
|
+
<span class="value" part="value"><slot>${this.value}</slot></span>
|
|
110
|
+
${this.delta
|
|
111
|
+
? html`
|
|
112
|
+
<span class="delta" part="delta" data-trend=${trend}>
|
|
113
|
+
${this.trend ? html`<span class="arrow" aria-hidden="true">${this._arrowFor(this.trend)}</span>` : nothing}
|
|
114
|
+
${this.delta}
|
|
115
|
+
</span>
|
|
116
|
+
`
|
|
117
|
+
: nothing}
|
|
118
|
+
</span>
|
|
119
|
+
`
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare global {
|
|
124
|
+
interface HTMLElementTagNameMap {
|
|
125
|
+
'schmancy-metric': SchmancyMetric
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -1,10 +1,61 @@
|
|
|
1
1
|
import { TailwindElement } from '@mixins/tailwind.mixin'
|
|
2
|
-
import { css, html } from 'lit'
|
|
2
|
+
import { css, html, type PropertyValues } from 'lit'
|
|
3
3
|
import { customElement, property } from 'lit/decorators.js'
|
|
4
4
|
import { createRef, ref } from 'lit/directives/ref.js'
|
|
5
|
+
import { html as staticHtml, literal } from 'lit/static-html.js'
|
|
5
6
|
import { fromEvent } from 'rxjs'
|
|
6
7
|
import { filter, tap, takeUntil } from 'rxjs/operators'
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Preset → (type, token) shorthand. Saves the two-decision-per-text-node
|
|
11
|
+
* fatigue that 50+ typography nodes in a single page cause.
|
|
12
|
+
*/
|
|
13
|
+
export type TypographyPreset =
|
|
14
|
+
| 'display' | 'display-lg' | 'display-md' | 'display-sm'
|
|
15
|
+
| 'heading-lg' | 'heading-md' | 'heading-sm'
|
|
16
|
+
| 'title-lg' | 'title-md' | 'title-sm'
|
|
17
|
+
| 'body-lg' | 'body-md' | 'body-sm'
|
|
18
|
+
| 'label-lg' | 'label-md' | 'label-sm'
|
|
19
|
+
| 'caption'
|
|
20
|
+
|
|
21
|
+
const PRESET_MAP: Record<TypographyPreset, { type: string; token: string }> = {
|
|
22
|
+
'display': { type: 'display', token: 'lg' },
|
|
23
|
+
'display-lg': { type: 'display', token: 'lg' },
|
|
24
|
+
'display-md': { type: 'display', token: 'md' },
|
|
25
|
+
'display-sm': { type: 'display', token: 'sm' },
|
|
26
|
+
'heading-lg': { type: 'headline', token: 'lg' },
|
|
27
|
+
'heading-md': { type: 'headline', token: 'md' },
|
|
28
|
+
'heading-sm': { type: 'headline', token: 'sm' },
|
|
29
|
+
'title-lg': { type: 'title', token: 'lg' },
|
|
30
|
+
'title-md': { type: 'title', token: 'md' },
|
|
31
|
+
'title-sm': { type: 'title', token: 'sm' },
|
|
32
|
+
'body-lg': { type: 'body', token: 'lg' },
|
|
33
|
+
'body-md': { type: 'body', token: 'md' },
|
|
34
|
+
'body-sm': { type: 'body', token: 'sm' },
|
|
35
|
+
'label-lg': { type: 'label', token: 'lg' },
|
|
36
|
+
'label-md': { type: 'label', token: 'md' },
|
|
37
|
+
'label-sm': { type: 'label', token: 'sm' },
|
|
38
|
+
'caption': { type: 'label', token: 'sm' },
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Allowed semantic tag names for the `as` prop. Closed enum so we can
|
|
43
|
+
* use `literal` template parts safely with `lit/static-html`.
|
|
44
|
+
*/
|
|
45
|
+
export type TypographyTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'div'
|
|
46
|
+
|
|
47
|
+
const TAG_LITERALS: Record<TypographyTag, ReturnType<typeof literal>> = {
|
|
48
|
+
h1: literal`h1`,
|
|
49
|
+
h2: literal`h2`,
|
|
50
|
+
h3: literal`h3`,
|
|
51
|
+
h4: literal`h4`,
|
|
52
|
+
h5: literal`h5`,
|
|
53
|
+
h6: literal`h6`,
|
|
54
|
+
p: literal`p`,
|
|
55
|
+
span: literal`span`,
|
|
56
|
+
div: literal`div`,
|
|
57
|
+
}
|
|
58
|
+
|
|
8
59
|
// Material Design 3 typography - https://m3.material.io/styles/typography/type-scale-tokens
|
|
9
60
|
|
|
10
61
|
/**
|
|
@@ -302,6 +353,30 @@ export class SchmancyTypography extends TailwindElement(css`
|
|
|
302
353
|
@property({ type: String, reflect: true })
|
|
303
354
|
type: 'display' | 'headline' | 'title' | 'subtitle' | 'body' | 'label' = 'body'
|
|
304
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Shorthand for picking a (type, token) pair in one go. When set, derives
|
|
358
|
+
* `type` and `token` automatically — saves the two-decisions-per-text-node
|
|
359
|
+
* fatigue that hits when a single page has 50+ typography nodes.
|
|
360
|
+
*
|
|
361
|
+
* @attr preset
|
|
362
|
+
* @type {TypographyPreset}
|
|
363
|
+
* @example <schmancy-typography preset="heading-md">Title</schmancy-typography>
|
|
364
|
+
*/
|
|
365
|
+
@property({ type: String, reflect: true })
|
|
366
|
+
preset?: TypographyPreset
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Render the slot wrapped in the requested semantic HTML element so screen
|
|
370
|
+
* readers expose the right role / heading level. Without `as`, the slot
|
|
371
|
+
* sits directly in the shadow root and the host is a generic element.
|
|
372
|
+
*
|
|
373
|
+
* @attr as
|
|
374
|
+
* @type {TypographyTag}
|
|
375
|
+
* @example <schmancy-typography preset="heading-md" as="h2">Section</schmancy-typography>
|
|
376
|
+
*/
|
|
377
|
+
@property({ type: String, reflect: true })
|
|
378
|
+
as?: TypographyTag
|
|
379
|
+
|
|
305
380
|
/**
|
|
306
381
|
* @attr token - The size token.
|
|
307
382
|
* @deprecated Prefer using Tailwind responsive text classes for better responsive design.
|
|
@@ -352,6 +427,17 @@ export class SchmancyTypography extends TailwindElement(css`
|
|
|
352
427
|
|
|
353
428
|
private _editRef = createRef<HTMLDivElement>()
|
|
354
429
|
|
|
430
|
+
protected override willUpdate(changed: PropertyValues): void {
|
|
431
|
+
super.willUpdate?.(changed)
|
|
432
|
+
// `preset` shorthand expands to (type, token) so the existing CSS
|
|
433
|
+
// selectors keep matching without duplicating the size scale.
|
|
434
|
+
if (changed.has('preset') && this.preset && PRESET_MAP[this.preset]) {
|
|
435
|
+
const { type, token } = PRESET_MAP[this.preset]
|
|
436
|
+
this.type = type as typeof this.type
|
|
437
|
+
this.token = token as typeof this.token
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
355
441
|
/** Focus and select all text in editable mode */
|
|
356
442
|
selectAll() {
|
|
357
443
|
const el = this._editRef.value
|
|
@@ -437,6 +523,13 @@ export class SchmancyTypography extends TailwindElement(css`
|
|
|
437
523
|
data-placeholder=${this.placeholder ?? ''}
|
|
438
524
|
></div>`
|
|
439
525
|
}
|
|
526
|
+
// `as` wraps the slot in the requested semantic tag so heading levels
|
|
527
|
+
// land in the accessibility tree. Without `as` the slot is bare and
|
|
528
|
+
// the host element carries the visual styling only.
|
|
529
|
+
if (this.as && TAG_LITERALS[this.as]) {
|
|
530
|
+
const tag = TAG_LITERALS[this.as]
|
|
531
|
+
return staticHtml`<${tag}><slot></slot></${tag}>`
|
|
532
|
+
}
|
|
440
533
|
return html`<slot></slot>`
|
|
441
534
|
}
|
|
442
535
|
}
|