@mhmo91/schmancy 0.9.23 → 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.
@@ -6696,6 +6696,50 @@
6696
6696
  ],
6697
6697
  "default": "'body'"
6698
6698
  },
6699
+ {
6700
+ "name": "preset",
6701
+ "type": {
6702
+ "text": "TypographyPreset"
6703
+ },
6704
+ "values": [
6705
+ "display",
6706
+ "display-lg",
6707
+ "display-md",
6708
+ "display-sm",
6709
+ "heading-lg",
6710
+ "heading-md",
6711
+ "heading-sm",
6712
+ "title-lg",
6713
+ "title-md",
6714
+ "title-sm",
6715
+ "body-lg",
6716
+ "body-md",
6717
+ "body-sm",
6718
+ "label-lg",
6719
+ "label-md",
6720
+ "label-sm",
6721
+ "caption"
6722
+ ],
6723
+ "description": "Shorthand for picking a (type, token) pair in one go. When set, derives `type` and `token` automatically — saves the two-decisions-per-text-node fatigue that hits when a single page has 50+ typography nodes."
6724
+ },
6725
+ {
6726
+ "name": "as",
6727
+ "type": {
6728
+ "text": "TypographyTag"
6729
+ },
6730
+ "values": [
6731
+ "h1",
6732
+ "h2",
6733
+ "h3",
6734
+ "h4",
6735
+ "h5",
6736
+ "h6",
6737
+ "p",
6738
+ "span",
6739
+ "div"
6740
+ ],
6741
+ "description": "Render the slot wrapped in the requested semantic HTML element so screen readers expose the right role / heading level. Without `as`, the slot sits directly in the shadow root and the host is a generic element."
6742
+ },
6699
6743
  {
6700
6744
  "name": "token",
6701
6745
  "type": {
@@ -203,7 +203,7 @@ The manifest already has everything needed; the package is just the JSON-RPC wra
203
203
 
204
204
  **Problem.** `handover/agent-runtime-v1.md` had `<PENDING>` placeholders that we manually replaced with `0.9.13` after the first publish. Future handover docs will have the same issue.
205
205
 
206
- **Fix.** A build step that substitutes `0.9.23` in `handover/**/*.md` against `package.json`'s `version` field on every build. `dist/handover/**/*.md` gets the rendered version; the source stays templated.
206
+ **Fix.** A build step that substitutes `0.9.24` in `handover/**/*.md` against `package.json`'s `version` field on every build. `dist/handover/**/*.md` gets the rendered version; the source stays templated.
207
207
 
208
208
  **Effort:** ~30 min (one sed step or a tiny script).
209
209
 
@@ -7,8 +7,8 @@
7
7
  ## The URLs you asked for
8
8
 
9
9
  ```
10
- https://esm.sh/@mhmo91/schmancy/agent@0.9.23
11
- https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.23
10
+ https://esm.sh/@mhmo91/schmancy/agent@0.9.24
11
+ https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.24
12
12
  ```
13
13
 
14
14
  `0.9.13` is the first release containing `/agent`; every subsequent publish serves the same subpath. `npm view @mhmo91/schmancy version` always returns the current pin if you want to float forward.
@@ -20,7 +20,7 @@ One script tag. No bundler, no bare specifiers, no npm install.
20
20
  ```html
21
21
  <!doctype html>
22
22
  <script type="module">
23
- import { $dialog, theme } from 'https://esm.sh/@mhmo91/schmancy/agent@0.9.23';
23
+ import { $dialog, theme } from 'https://esm.sh/@mhmo91/schmancy/agent@0.9.24';
24
24
  </script>
25
25
  <schmancy-theme root scheme="dark">
26
26
  <schmancy-surface type="solid" fill="all">
@@ -12,7 +12,7 @@ Four separable PRs, each shippable on its own:
12
12
  | # | Title | Branch | Impact on your probe |
13
13
  |---|---|---|---|
14
14
  | 2 | CI smoke-test gate | `feat/ci-gate-and-version-templating` | None user-facing. `window.schmancy.help()` regressions now fail-closed at publish time instead of shipping silently. |
15
- | 9 | `0.9.23` templating for handover docs | same branch as #2 | None user-facing. Future handover docs will have live esm.sh URLs instead of `<PENDING>` placeholders. |
15
+ | 9 | `0.9.24` templating for handover docs | same branch as #2 | None user-facing. Future handover docs will have live esm.sh URLs instead of `<PENDING>` placeholders. |
16
16
  | 3 | JSDoc backfill (46 components) | `feat/jsdoc-batch-{1,2,3}-*` | **This is what you'll notice.** Every form-control, container, and overlay/nav component now ships `@summary`, `@example`, and `@platform` tags in its manifest entry. `window.schmancy.help('schmancy-button')` returns a non-empty `summary`, a copy-pastable `examples[]`, and a `platformPrimitive` hint for graceful degradation. |
17
17
  | 1 | Lazy vendor chunks | `feat/lazy-{typewriter,code-highlight,qr-scanner}` | Pages that don't render `<schmancy-code>`, `<schmancy-qr-scanner>`, or `<schmancy-typewriter>` no longer fetch `vendor-highlight`, `vendor-jsqr`, or the typewriter chunk on first paint. ~68 KB gzipped saved on cold starts for typical prototypes. |
18
18
 
@@ -21,18 +21,18 @@ The only one that changes the shape of `window.schmancy` is **#3**. The others a
21
21
  ## Pinned URLs (live once the PRs merge)
22
22
 
23
23
  ```
24
- https://esm.sh/@mhmo91/schmancy/agent@0.9.23
25
- https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.23
24
+ https://esm.sh/@mhmo91/schmancy/agent@0.9.24
25
+ https://esm.sh/@mhmo91/schmancy/agent/manifest@0.9.24
26
26
  ```
27
27
 
28
- `0.9.23` is substituted at publish time — see [`agent-runtime-followups.md`](./agent-runtime-followups.md) #9. Until the PRs land, continue pinning `@0.9.14` (the last published version at time of writing).
28
+ `0.9.24` is substituted at publish time — see [`agent-runtime-followups.md`](./agent-runtime-followups.md) #9. Until the PRs land, continue pinning `@0.9.14` (the last published version at time of writing).
29
29
 
30
30
  ## Minimum loop-back test
31
31
 
32
32
  ```html
33
33
  <!doctype html>
34
34
  <script type="module">
35
- import 'https://esm.sh/@mhmo91/schmancy/agent@0.9.23';
35
+ import 'https://esm.sh/@mhmo91/schmancy/agent@0.9.24';
36
36
  </script>
37
37
 
38
38
  <schmancy-theme root scheme="dark">
@@ -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(){return this.editable?a.html`<div
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>`:a.html`<slot></slot>`}};t.t([(0,i.property)({type:String,reflect:!0})],s.prototype,`type`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],s.prototype,`token`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],s.prototype,`align`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],s.prototype,`weight`,void 0),t.t([(0,i.property)({type:String,reflect:!0})],s.prototype,`transform`,void 0),t.t([(0,i.property)({type:Number})],s.prototype,`maxLines`,void 0),t.t([(0,i.property)({type:Boolean,reflect:!0})],s.prototype,`editable`,void 0),t.t([(0,i.property)({type:String})],s.prototype,`value`,void 0),t.t([(0,i.property)({type:String})],s.prototype,`placeholder`,void 0),s=t.t([(0,i.customElement)(`schmancy-typography`)],s),Object.defineProperty(exports,`SchmancyTypography`,{enumerable:!0,get:function(){return s}});
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}});
@@ -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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"}
@@ -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
- var f = class extends e(c`
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
- return this.editable ? l`<div
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>` : l`<slot></slot>`;
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
- })], f.prototype, "type", void 0), t([s({
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
- })], f.prototype, "token", void 0), t([s({
439
+ })], g.prototype, "token", void 0), t([s({
343
440
  type: String,
344
441
  reflect: !0
345
- })], f.prototype, "align", void 0), t([s({
442
+ })], g.prototype, "align", void 0), t([s({
346
443
  type: String,
347
444
  reflect: !0
348
- })], f.prototype, "weight", void 0), t([s({
445
+ })], g.prototype, "weight", void 0), t([s({
349
446
  type: String,
350
447
  reflect: !0
351
- })], f.prototype, "transform", void 0), t([s({ type: Number })], f.prototype, "maxLines", void 0), t([s({
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
- })], f.prototype, "editable", void 0), t([s({ type: String })], f.prototype, "value", void 0), t([s({ type: String })], f.prototype, "placeholder", void 0), f = t([o("schmancy-typography")], f);
355
- export { f as SchmancyTypography };
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 };
@@ -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":"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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhmo91/schmancy",
3
- "version": "0.9.23",
3
+ "version": "0.9.24",
4
4
  "description": "UI library build with web components",
5
5
  "main": "./dist/index.js",
6
6
  "customElements": "custom-elements.json",
@@ -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
  }