@f-ewald/components 0.7.0 → 1.0.2
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/README.md +76 -47
- package/custom-elements.json +3049 -910
- package/dist/address-autocomplete.d.ts +8 -0
- package/dist/address-autocomplete.d.ts.map +1 -1
- package/dist/address-autocomplete.js +165 -15
- package/dist/address-autocomplete.js.map +1 -1
- package/dist/animate-confetti.d.ts +8 -5
- package/dist/animate-confetti.d.ts.map +1 -1
- package/dist/animate-confetti.js +46 -19
- package/dist/animate-confetti.js.map +1 -1
- package/dist/autocomplete-input.d.ts +8 -0
- package/dist/autocomplete-input.d.ts.map +1 -1
- package/dist/autocomplete-input.js +165 -15
- package/dist/autocomplete-input.js.map +1 -1
- package/dist/calendar-entry.d.ts +33 -0
- package/dist/calendar-entry.d.ts.map +1 -0
- package/dist/calendar-entry.js +64 -0
- package/dist/calendar-entry.js.map +1 -0
- package/dist/calendar-month.d.ts +67 -0
- package/dist/calendar-month.d.ts.map +1 -0
- package/dist/calendar-month.js +469 -0
- package/dist/calendar-month.js.map +1 -0
- package/dist/calendar-year.d.ts +43 -0
- package/dist/calendar-year.d.ts.map +1 -0
- package/dist/calendar-year.js +145 -0
- package/dist/calendar-year.js.map +1 -0
- package/dist/chat-message.d.ts.map +1 -1
- package/dist/chat-message.js +77 -24
- package/dist/chat-message.js.map +1 -1
- package/dist/confirm-dialog.d.ts +8 -1
- package/dist/confirm-dialog.d.ts.map +1 -1
- package/dist/confirm-dialog.js +130 -18
- package/dist/confirm-dialog.js.map +1 -1
- package/dist/copy-link-button.d.ts +2 -0
- package/dist/copy-link-button.d.ts.map +1 -1
- package/dist/copy-link-button.js +35 -3
- package/dist/copy-link-button.js.map +1 -1
- package/dist/data-table.d.ts +2 -0
- package/dist/data-table.d.ts.map +1 -1
- package/dist/data-table.js +83 -11
- package/dist/data-table.js.map +1 -1
- package/dist/distance-value.d.ts +0 -1
- package/dist/distance-value.d.ts.map +1 -1
- package/dist/distance-value.js +3 -8
- package/dist/distance-value.js.map +1 -1
- package/dist/distribution-chart.d.ts.map +1 -1
- package/dist/distribution-chart.js +54 -17
- package/dist/distribution-chart.js.map +1 -1
- package/dist/dropdown-button.d.ts.map +1 -1
- package/dist/dropdown-button.js +60 -11
- package/dist/dropdown-button.js.map +1 -1
- package/dist/editable-text.d.ts.map +1 -1
- package/dist/editable-text.js +46 -13
- package/dist/editable-text.js.map +1 -1
- package/dist/form-select.d.ts +21 -2
- package/dist/form-select.d.ts.map +1 -1
- package/dist/form-select.js +489 -37
- package/dist/form-select.js.map +1 -1
- package/dist/frame-box.d.ts.map +1 -1
- package/dist/frame-box.js +20 -12
- package/dist/frame-box.js.map +1 -1
- package/dist/icon-button.d.ts.map +1 -1
- package/dist/icon-button.js +11 -0
- package/dist/icon-button.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/kbd-hint.d.ts +30 -0
- package/dist/kbd-hint.d.ts.map +1 -0
- package/dist/kbd-hint.js +184 -0
- package/dist/kbd-hint.js.map +1 -0
- package/dist/live-timer.d.ts +0 -1
- package/dist/live-timer.d.ts.map +1 -1
- package/dist/live-timer.js +1 -6
- package/dist/live-timer.js.map +1 -1
- package/dist/map-circle.d.ts +3 -0
- package/dist/map-circle.d.ts.map +1 -1
- package/dist/map-circle.js +19 -1
- package/dist/map-circle.js.map +1 -1
- package/dist/map-pin.d.ts.map +1 -1
- package/dist/map-pin.js +16 -1
- package/dist/map-pin.js.map +1 -1
- package/dist/percent-bar-chart.d.ts +3 -0
- package/dist/percent-bar-chart.d.ts.map +1 -1
- package/dist/percent-bar-chart.js +60 -4
- package/dist/percent-bar-chart.js.map +1 -1
- package/dist/photo-gallery.d.ts.map +1 -1
- package/dist/photo-gallery.js +12 -3
- package/dist/photo-gallery.js.map +1 -1
- package/dist/popover-panel.d.ts.map +1 -1
- package/dist/popover-panel.js +132 -23
- package/dist/popover-panel.js.map +1 -1
- package/dist/price-history-chart.d.ts.map +1 -1
- package/dist/price-history-chart.js +52 -18
- package/dist/price-history-chart.js.map +1 -1
- package/dist/radio-cards.d.ts +2 -0
- package/dist/radio-cards.d.ts.map +1 -1
- package/dist/radio-cards.js +51 -5
- package/dist/radio-cards.js.map +1 -1
- package/dist/radio-pills.d.ts +2 -0
- package/dist/radio-pills.d.ts.map +1 -1
- package/dist/radio-pills.js +46 -4
- package/dist/radio-pills.js.map +1 -1
- package/dist/relative-time.d.ts +0 -1
- package/dist/relative-time.d.ts.map +1 -1
- package/dist/relative-time.js +1 -6
- package/dist/relative-time.js.map +1 -1
- package/dist/reveal-button.d.ts +2 -0
- package/dist/reveal-button.d.ts.map +1 -1
- package/dist/reveal-button.js +42 -5
- package/dist/reveal-button.js.map +1 -1
- package/dist/roman-numeral.js +1 -1
- package/dist/roman-numeral.js.map +1 -1
- package/dist/slide-panel.d.ts +7 -3
- package/dist/slide-panel.d.ts.map +1 -1
- package/dist/slide-panel.js +95 -23
- package/dist/slide-panel.js.map +1 -1
- package/dist/stat-meter.d.ts.map +1 -1
- package/dist/stat-meter.js +17 -7
- package/dist/stat-meter.js.map +1 -1
- package/dist/status-pill.d.ts.map +1 -1
- package/dist/status-pill.js +26 -12
- package/dist/status-pill.js.map +1 -1
- package/dist/tile-grid.d.ts +8 -0
- package/dist/tile-grid.d.ts.map +1 -1
- package/dist/tile-grid.js +88 -12
- package/dist/tile-grid.js.map +1 -1
- package/dist/toast-notification.d.ts.map +1 -1
- package/dist/toast-notification.js +40 -11
- package/dist/toast-notification.js.map +1 -1
- package/dist/tokens.css +23 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +16 -0
- package/dist/tokens.js.map +1 -1
- package/dist/ui-button.d.ts +2 -0
- package/dist/ui-button.d.ts.map +1 -1
- package/dist/ui-button.js +58 -9
- package/dist/ui-button.js.map +1 -1
- package/dist/user-avatar.d.ts.map +1 -1
- package/dist/user-avatar.js +18 -3
- package/dist/user-avatar.js.map +1 -1
- package/dist/utils/calendar.d.ts +77 -0
- package/dist/utils/calendar.d.ts.map +1 -0
- package/dist/utils/calendar.js +135 -0
- package/dist/utils/calendar.js.map +1 -0
- package/dist/utils/form.d.ts +3 -0
- package/dist/utils/form.d.ts.map +1 -0
- package/dist/utils/form.js +14 -0
- package/dist/utils/form.js.map +1 -0
- package/dist/utils/layer-stack.d.ts +9 -0
- package/dist/utils/layer-stack.d.ts.map +1 -0
- package/dist/utils/layer-stack.js +35 -0
- package/dist/utils/layer-stack.js.map +1 -0
- package/dist/weight-bar-chart.d.ts.map +1 -1
- package/dist/weight-bar-chart.js +27 -8
- package/dist/weight-bar-chart.js.map +1 -1
- package/docs/address-autocomplete.md +5 -0
- package/docs/autocomplete-input.md +5 -0
- package/docs/calendar-entry.md +53 -0
- package/docs/calendar-month.md +79 -0
- package/docs/calendar-year.md +53 -0
- package/docs/chat-message.md +4 -0
- package/docs/confirm-dialog.md +2 -0
- package/docs/copy-link-button.md +2 -0
- package/docs/data-table.md +2 -0
- package/docs/design-language.md +149 -0
- package/docs/distribution-chart.md +4 -0
- package/docs/dropdown-button.md +1 -0
- package/docs/form-select.md +15 -3
- package/docs/frame-box.md +2 -0
- package/docs/kbd-hint.md +42 -0
- package/docs/map-circle.md +4 -0
- package/docs/percent-bar-chart.md +4 -1
- package/docs/photo-gallery.md +1 -0
- package/docs/popover-panel.md +2 -0
- package/docs/price-history-chart.md +8 -1
- package/docs/radio-cards.md +2 -0
- package/docs/radio-pills.md +2 -0
- package/docs/reveal-button.md +3 -0
- package/docs/slide-panel.md +4 -2
- package/docs/status-pill.md +3 -0
- package/docs/tile-grid.md +10 -1
- package/docs/toast-notification.md +4 -1
- package/docs/ui-button.md +2 -0
- package/docs/user-avatar.md +1 -0
- package/llms.txt +175 -61
- package/package.json +14 -2
- package/dist/map-point.d.ts +0 -31
- package/dist/map-point.d.ts.map +0 -1
- package/dist/map-point.js +0 -83
- package/dist/map-point.js.map +0 -1
- package/docs/map-point.md +0 -44
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate-confetti.js","sourceRoot":"","sources":["../src/animate-confetti.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\n\ninterface Particle {\n color: string;\n x: number;\n y: number;\n diameter: number;\n tilt: number;\n tiltAngleIncrement: number;\n tiltAngle: number;\n}\n\n/**\n * Fullscreen confetti animation overlay, rendered on a fixed-position canvas.\n * Starts automatically on first render and stops after `duration` ms.\n *\n * @element animate-confetti\n */\n@customElement(\"animate-confetti\")\nexport class AnimateConfetti extends LitElement {\n /** How long the confetti streams before stopping, in ms. */\n @property({ type: Number })\n duration: number = 6000;\n\n private maxParticleCount = 150; // set max confetti count\n private particleSpeed = 2; // set the particle animation speed\n\n private particles: Particle[] = [];\n private streamingConfetti: boolean = false;\n private colors = [\n \"DodgerBlue\",\n \"OliveDrab\",\n \"Gold\",\n \"Pink\",\n \"SlateBlue\",\n \"LightBlue\",\n \"Violet\",\n \"PaleGreen\",\n \"SteelBlue\",\n \"SandyBrown\",\n \"Chocolate\",\n \"Crimson\",\n ];\n private waveAngle: number = 0.0;\n private animationTimer?: number;\n\n override connectedCallback() {\n super.connectedCallback();\n window.addEventListener(\"resize\", this._handleResize);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n window.removeEventListener(\"resize\", this._handleResize);\n this.stopConfetti();\n }\n\n static override styles = css`\n #confetti-canvas {\n display: block;\n position: fixed;\n top: 0;\n left: 0;\n z-index: 999999;\n pointer-events: none;\n }\n `;\n\n /**\n * @returns The canvas element\n */\n private get _canvas(): HTMLCanvasElement {\n return <HTMLCanvasElement>this.shadowRoot!.getElementById(\"confetti-canvas\")!;\n }\n\n /**\n * Resize canvas to browser size.\n */\n private _handleResize = () => {\n const canvas = this._canvas;\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n /**\n * Start confetti once shadow dom has been rendered for the first time.\n */\n protected override firstUpdated(): void {\n this.startConfetti();\n setTimeout(() => {\n this.stopConfetti();\n }, this.duration);\n }\n\n override render() {\n return html`<canvas id=\"confetti-canvas\"></canvas>`;\n }\n\n private startConfetti() {\n const width = window.innerWidth;\n const height = window.innerHeight;\n this._handleResize();\n\n while (this.particles.length < this.maxParticleCount)\n this.particles.push(this.resetParticle({} as Particle, width, height));\n this.streamingConfetti = true;\n if (this.animationTimer == undefined) {\n this.runAnimation();\n }\n }\n\n private runAnimation = () => {\n const canvas = this._canvas;\n const context = canvas.getContext(\"2d\");\n context!.clearRect(0, 0, window.innerWidth, window.innerHeight);\n if (this.particles.length === 0) this.animationTimer = undefined;\n else {\n this.updateParticles();\n this.drawParticles(context);\n this.animationTimer = window.requestAnimationFrame(this.runAnimation);\n }\n };\n\n /**\n * Stop confetti and clean up after reasonable time.\n */\n private stopConfetti() {\n this.streamingConfetti = false;\n setTimeout(this._cleanup, 5000);\n }\n\n /**\n * Removes any associated particle data from internal storage to regain memory.\n */\n private _cleanup(): void {\n this.particles = [];\n }\n\n private updateParticles() {\n const width = window.innerWidth;\n const height = window.innerHeight;\n let particle: Particle;\n this.waveAngle += 0.01;\n for (let i = 0; i < this.particles.length; i++) {\n particle = this.particles[i];\n if (!this.streamingConfetti && particle.y < -15) particle.y = height + 100;\n else {\n particle.tiltAngle += particle.tiltAngleIncrement;\n particle.x += Math.sin(this.waveAngle);\n particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.particleSpeed) * 0.5;\n particle.tilt = Math.sin(particle.tiltAngle) * 15;\n }\n if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {\n if (this.streamingConfetti && this.particles.length <= this.maxParticleCount)\n this.resetParticle(particle, width, height);\n else {\n this.particles.splice(i, 1);\n i--;\n }\n }\n }\n }\n\n private drawParticles(context: CanvasRenderingContext2D | null) {\n if (!context) return;\n let particle: Particle;\n let x: number;\n for (let i = 0; i < this.particles.length; i++) {\n particle = this.particles[i];\n context.beginPath();\n context.lineWidth = particle.diameter;\n context.strokeStyle = particle.color;\n x = particle.x + particle.tilt;\n context.moveTo(x + particle.diameter / 2, particle.y);\n context.lineTo(x, particle.y + particle.tilt + particle.diameter / 2);\n context.stroke();\n }\n }\n\n private resetParticle(particle: Particle, width: number, height: number): Particle {\n particle.color = this.colors[(Math.random() * this.colors.length) | 0];\n particle.x = Math.random() * width;\n particle.y = Math.random() * height - height;\n particle.diameter = Math.random() * 10 + 5;\n particle.tilt = Math.random() * 10 - 10;\n particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;\n particle.tiltAngle = 0;\n return particle;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"animate-confetti\": AnimateConfetti;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"animate-confetti.js","sourceRoot":"","sources":["../src/animate-confetti.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\ninterface Particle {\n color: string;\n x: number;\n y: number;\n diameter: number;\n tilt: number;\n tiltAngleIncrement: number;\n tiltAngle: number;\n}\n\n/**\n * Fullscreen confetti animation overlay, rendered on a fixed-position canvas.\n * Starts automatically on first render and stops after `duration` ms.\n *\n * @element animate-confetti\n */\n@customElement(\"animate-confetti\")\nexport class AnimateConfetti extends LitElement {\n /** How long the confetti streams before stopping, in ms. */\n @property({ type: Number })\n duration: number = 6000;\n\n private maxParticleCount = 150; // set max confetti count\n private particleSpeed = 2; // set the particle animation speed\n\n private particles: Particle[] = [];\n private streamingConfetti: boolean = false;\n private colors = [\n \"DodgerBlue\",\n \"OliveDrab\",\n \"Gold\",\n \"Pink\",\n \"SlateBlue\",\n \"LightBlue\",\n \"Violet\",\n \"PaleGreen\",\n \"SteelBlue\",\n \"SandyBrown\",\n \"Chocolate\",\n \"Crimson\",\n ];\n private waveAngle: number = 0.0;\n private animationTimer?: number;\n private stopTimer?: number;\n private cleanupTimer?: number;\n\n override connectedCallback() {\n super.connectedCallback();\n window.addEventListener(\"resize\", this._handleResize);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n window.removeEventListener(\"resize\", this._handleResize);\n this._clearTimersAndParticles();\n }\n\n static override styles = [\n tokens,\n css`\n #confetti-canvas {\n display: block;\n position: fixed;\n top: 0;\n left: 0;\n z-index: 999999;\n pointer-events: none;\n }\n `,\n ];\n\n /**\n * @returns The canvas element\n */\n private get _canvas(): HTMLCanvasElement {\n return <HTMLCanvasElement>this.shadowRoot!.getElementById(\"confetti-canvas\")!;\n }\n\n /**\n * Resize canvas to browser size.\n */\n private _handleResize = () => {\n const canvas = this._canvas;\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n /**\n * Start confetti once shadow dom has been rendered for the first time.\n */\n protected override firstUpdated(): void {\n this._handleResize();\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n this.startConfetti();\n this.stopTimer = window.setTimeout(() => {\n this.stopTimer = undefined;\n this.stopConfetti();\n }, this.duration);\n }\n\n override render() {\n return html`<canvas id=\"confetti-canvas\" aria-hidden=\"true\"></canvas>`;\n }\n\n /** Creates particles and starts the animation frame loop. */\n private startConfetti() {\n const width = window.innerWidth;\n const height = window.innerHeight;\n this._handleResize();\n\n while (this.particles.length < this.maxParticleCount)\n this.particles.push(this.resetParticle({} as Particle, width, height));\n this.streamingConfetti = true;\n if (this.animationTimer == undefined) {\n this.runAnimation();\n }\n }\n\n private runAnimation = () => {\n const canvas = this._canvas;\n const context = canvas.getContext(\"2d\");\n context!.clearRect(0, 0, window.innerWidth, window.innerHeight);\n if (this.particles.length === 0) this.animationTimer = undefined;\n else {\n this.updateParticles();\n this.drawParticles(context);\n this.animationTimer = window.requestAnimationFrame(this.runAnimation);\n }\n };\n\n /**\n * Stop confetti and clean up after reasonable time.\n */\n private stopConfetti() {\n this.streamingConfetti = false;\n this.cleanupTimer = window.setTimeout(this._cleanup, 5000);\n }\n\n /** Removes particle data and stops the animation frame loop. */\n private readonly _cleanup = (): void => {\n this.particles = [];\n if (this.animationTimer !== undefined) {\n window.cancelAnimationFrame(this.animationTimer);\n this.animationTimer = undefined;\n }\n const canvas = this.shadowRoot?.getElementById(\"confetti-canvas\") as HTMLCanvasElement | null;\n canvas?.getContext(\"2d\")?.clearRect(0, 0, canvas.width, canvas.height);\n this.cleanupTimer = undefined;\n };\n\n /** Cancels all pending work when the component leaves the document. */\n private _clearTimersAndParticles(): void {\n if (this.stopTimer !== undefined) {\n window.clearTimeout(this.stopTimer);\n this.stopTimer = undefined;\n }\n if (this.cleanupTimer !== undefined) {\n window.clearTimeout(this.cleanupTimer);\n this.cleanupTimer = undefined;\n }\n this.streamingConfetti = false;\n this._cleanup();\n }\n\n private updateParticles() {\n const width = window.innerWidth;\n const height = window.innerHeight;\n let particle: Particle;\n this.waveAngle += 0.01;\n for (let i = 0; i < this.particles.length; i++) {\n particle = this.particles[i];\n if (!this.streamingConfetti && particle.y < -15) particle.y = height + 100;\n else {\n particle.tiltAngle += particle.tiltAngleIncrement;\n particle.x += Math.sin(this.waveAngle);\n particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.particleSpeed) * 0.5;\n particle.tilt = Math.sin(particle.tiltAngle) * 15;\n }\n if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {\n if (this.streamingConfetti && this.particles.length <= this.maxParticleCount)\n this.resetParticle(particle, width, height);\n else {\n this.particles.splice(i, 1);\n i--;\n }\n }\n }\n }\n\n private drawParticles(context: CanvasRenderingContext2D | null) {\n if (!context) return;\n let particle: Particle;\n let x: number;\n for (let i = 0; i < this.particles.length; i++) {\n particle = this.particles[i];\n context.beginPath();\n context.lineWidth = particle.diameter;\n context.strokeStyle = particle.color;\n x = particle.x + particle.tilt;\n context.moveTo(x + particle.diameter / 2, particle.y);\n context.lineTo(x, particle.y + particle.tilt + particle.diameter / 2);\n context.stroke();\n }\n }\n\n private resetParticle(particle: Particle, width: number, height: number): Particle {\n particle.color = this.colors[(Math.random() * this.colors.length) | 0];\n particle.x = Math.random() * width;\n particle.y = Math.random() * height - height;\n particle.diameter = Math.random() * 10 + 5;\n particle.tilt = Math.random() * 10 - 10;\n particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;\n particle.tiltAngle = 0;\n return particle;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"animate-confetti\": AnimateConfetti;\n }\n}\n"]}
|
|
@@ -36,6 +36,8 @@ export declare class AutocompleteInput extends LitElement {
|
|
|
36
36
|
placeholder: string;
|
|
37
37
|
/** Marks the input as required for native form validation. */
|
|
38
38
|
required: boolean;
|
|
39
|
+
/** Disables the input and closes its suggestion popup. */
|
|
40
|
+
disabled: boolean;
|
|
39
41
|
/** API endpoint queried in API mode. Ignored when `options` is set. */
|
|
40
42
|
endpoint: string;
|
|
41
43
|
/** Query string parameter name the current input text is sent under. */
|
|
@@ -53,15 +55,21 @@ export declare class AutocompleteInput extends LitElement {
|
|
|
53
55
|
private _suggestions;
|
|
54
56
|
private _open;
|
|
55
57
|
private _activeIndex;
|
|
58
|
+
private _formDisabled;
|
|
56
59
|
/** Last-picked option, or null once the input diverges from it. */
|
|
57
60
|
get selectedOption(): AutocompleteOption | null;
|
|
61
|
+
protected willUpdate(changed: PropertyValues): void;
|
|
58
62
|
protected updated(changed: PropertyValues): void;
|
|
59
63
|
disconnectedCallback(): void;
|
|
60
64
|
/** Resets to empty on `form.reset()`, per the form-associated custom element contract. */
|
|
61
65
|
formResetCallback(): void;
|
|
66
|
+
/** Mirrors ancestor fieldset disabled state onto the native input. */
|
|
67
|
+
formDisabledCallback(disabled: boolean): void;
|
|
62
68
|
private onInput;
|
|
63
69
|
private onKeydown;
|
|
64
70
|
private onBlur;
|
|
71
|
+
private onCompositionStart;
|
|
72
|
+
private onCompositionEnd;
|
|
65
73
|
private renderSuggestions;
|
|
66
74
|
render(): import("lit-html").TemplateResult<1>;
|
|
67
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete-input.d.ts","sourceRoot":"","sources":["../src/autocomplete-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"autocomplete-input.d.ts","sourceRoot":"","sources":["../src/autocomplete-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAO1E,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,iBAAkB,SAAQ,UAAU;;IAC/C,MAAM,CAAC,cAAc,UAAQ;IAE7B,OAAgB,MAAM,4BAoFpB;IAEF,mFAAmF;IACvE,KAAK,SAAM;IACvB,sDAAsD;IAC1C,WAAW,SAAM;IAC7B,8DAA8D;IACjC,QAAQ,UAAS;IAC9C,0DAA0D;IACd,QAAQ,UAAS;IAC7D,uEAAuE;IAC3D,QAAQ,SAAM;IAC1B,wEAAwE;IAChC,UAAU,SAAO;IACzD,uEAAuE;IAC3C,QAAQ,SAAO;IAC3C,yDAAyD;IACJ,SAAS,SAAK;IACnE;;;;OAIG;IAC6B,OAAO,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAQ;IAEnE,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAS;IAgBvC,mEAAmE;IACnE,IAAI,cAAc,IAAI,kBAAkB,GAAG,IAAI,CAG9C;IAED,UAAmB,UAAU,CAAC,OAAO,EAAE,cAAc,QAQpD;IAED,UAAmB,OAAO,CAAC,OAAO,EAAE,cAAc,QAEjD;IAEQ,oBAAoB,SAK5B;IAED,0FAA0F;IAC1F,iBAAiB,SAKhB;IAED,sEAAsE;IACtE,oBAAoB,CAAC,QAAQ,EAAE,OAAO,QAQrC;IAED,OAAO,CAAC,OAAO;IA8Df,OAAO,CAAC,SAAS;IAuCjB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,iBAAiB;IA0BhB,MAAM,yCA6Bd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,oBAAoB,EAAE,iBAAiB,CAAC;KACzC;CACF"}
|
|
@@ -7,6 +7,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { LitElement, css, html, nothing } from "lit";
|
|
8
8
|
import { customElement, property, state } from "lit/decorators.js";
|
|
9
9
|
import { tokens } from "./tokens.js";
|
|
10
|
+
import { submitWithDefaultButton } from "./utils/form.js";
|
|
11
|
+
let instanceCount = 0;
|
|
10
12
|
/**
|
|
11
13
|
* Generic form-associated text input with a suggestion dropdown, for any
|
|
12
14
|
* "type to filter a list of `{key, value}` options" use case. Works as a
|
|
@@ -38,6 +40,8 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
38
40
|
this.placeholder = "";
|
|
39
41
|
/** Marks the input as required for native form validation. */
|
|
40
42
|
this.required = false;
|
|
43
|
+
/** Disables the input and closes its suggestion popup. */
|
|
44
|
+
this.disabled = false;
|
|
41
45
|
/** API endpoint queried in API mode. Ignored when `options` is set. */
|
|
42
46
|
this.endpoint = "";
|
|
43
47
|
/** Query string parameter name the current input text is sent under. */
|
|
@@ -55,10 +59,15 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
55
59
|
this._suggestions = [];
|
|
56
60
|
this._open = false;
|
|
57
61
|
this._activeIndex = -1;
|
|
62
|
+
this._formDisabled = false;
|
|
58
63
|
this.#internals = this.attachInternals();
|
|
59
64
|
this.#lastPicked = null;
|
|
60
65
|
this.#debounceTimer = null;
|
|
61
66
|
this.#abortController = null;
|
|
67
|
+
this.#isComposing = false;
|
|
68
|
+
this.#compositionJustEnded = false;
|
|
69
|
+
this.#compositionEndTimer = null;
|
|
70
|
+
this.#listboxId = `autocomplete-input-listbox-${++instanceCount}`;
|
|
62
71
|
}
|
|
63
72
|
static { this.formAssociated = true; }
|
|
64
73
|
static { this.styles = [
|
|
@@ -69,14 +78,34 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
69
78
|
position: relative;
|
|
70
79
|
}
|
|
71
80
|
input {
|
|
72
|
-
font-family: var(
|
|
81
|
+
font-family: var(
|
|
82
|
+
--ui-font,
|
|
83
|
+
ui-sans-serif,
|
|
84
|
+
system-ui,
|
|
85
|
+
sans-serif,
|
|
86
|
+
"Apple Color Emoji",
|
|
87
|
+
"Segoe UI Emoji",
|
|
88
|
+
"Segoe UI Symbol",
|
|
89
|
+
"Noto Color Emoji"
|
|
90
|
+
);
|
|
73
91
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
74
|
-
|
|
92
|
+
color: var(--ui-text, #0f172a);
|
|
93
|
+
background: var(--ui-surface, #ffffff);
|
|
94
|
+
padding: 0.5rem;
|
|
75
95
|
border: 1px solid var(--ui-border, #e2e8f0);
|
|
76
96
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
77
97
|
width: 100%;
|
|
78
98
|
box-sizing: border-box;
|
|
79
99
|
}
|
|
100
|
+
input:disabled {
|
|
101
|
+
cursor: not-allowed;
|
|
102
|
+
opacity: 0.6;
|
|
103
|
+
}
|
|
104
|
+
input:focus-visible {
|
|
105
|
+
outline: none;
|
|
106
|
+
border-color: var(--ui-primary, #4f46e5);
|
|
107
|
+
box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));
|
|
108
|
+
}
|
|
80
109
|
.suggestions {
|
|
81
110
|
position: absolute;
|
|
82
111
|
top: 100%;
|
|
@@ -85,16 +114,16 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
85
114
|
z-index: 10;
|
|
86
115
|
max-height: 40vh;
|
|
87
116
|
overflow-y: auto;
|
|
88
|
-
margin:
|
|
89
|
-
padding:
|
|
117
|
+
margin: 0.25rem 0 0;
|
|
118
|
+
padding: 0.25rem 0;
|
|
90
119
|
list-style: none;
|
|
91
|
-
background: var(--ui-surface, #
|
|
120
|
+
background: var(--ui-surface, #ffffff);
|
|
92
121
|
border: 1px solid var(--ui-border, #e2e8f0);
|
|
93
122
|
border-radius: var(--ui-radius-sm, 0.25rem);
|
|
94
123
|
box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));
|
|
95
124
|
}
|
|
96
125
|
.suggestion {
|
|
97
|
-
padding: 0.
|
|
126
|
+
padding: 0.5rem 0.75rem;
|
|
98
127
|
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
99
128
|
cursor: pointer;
|
|
100
129
|
}
|
|
@@ -102,18 +131,58 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
102
131
|
.suggestion:hover {
|
|
103
132
|
background: var(--ui-surface-muted, #f8fafc);
|
|
104
133
|
}
|
|
134
|
+
.no-suggestions {
|
|
135
|
+
padding: 0.5rem 0.75rem;
|
|
136
|
+
color: var(--ui-text-muted, #64748b);
|
|
137
|
+
font-size: var(--ui-font-size-sm, 0.75rem);
|
|
138
|
+
font-style: italic;
|
|
139
|
+
}
|
|
140
|
+
@media (forced-colors: active) {
|
|
141
|
+
input:focus-visible {
|
|
142
|
+
outline: 2px solid CanvasText;
|
|
143
|
+
outline-offset: 2px;
|
|
144
|
+
box-shadow: none;
|
|
145
|
+
}
|
|
146
|
+
input:disabled {
|
|
147
|
+
color: GrayText;
|
|
148
|
+
opacity: 1;
|
|
149
|
+
}
|
|
150
|
+
.suggestion.active,
|
|
151
|
+
.suggestion:hover {
|
|
152
|
+
color: HighlightText;
|
|
153
|
+
background: Highlight;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
105
156
|
`,
|
|
106
157
|
]; }
|
|
107
158
|
#internals;
|
|
108
159
|
#lastPicked;
|
|
109
160
|
#debounceTimer;
|
|
110
161
|
#abortController;
|
|
162
|
+
#isComposing;
|
|
163
|
+
#compositionJustEnded;
|
|
164
|
+
#compositionEndTimer;
|
|
165
|
+
#listboxId;
|
|
166
|
+
/** Whether the host or an ancestor fieldset currently disables the control. */
|
|
167
|
+
get #isDisabled() {
|
|
168
|
+
return this.disabled || this._formDisabled;
|
|
169
|
+
}
|
|
111
170
|
/** Last-picked option, or null once the input diverges from it. */
|
|
112
171
|
get selectedOption() {
|
|
113
172
|
if (this.#lastPicked && this.#lastPicked.value === this.value)
|
|
114
173
|
return this.#lastPicked;
|
|
115
174
|
return null;
|
|
116
175
|
}
|
|
176
|
+
willUpdate(changed) {
|
|
177
|
+
if (changed.has("disabled") && this.#isDisabled) {
|
|
178
|
+
if (this.#debounceTimer)
|
|
179
|
+
clearTimeout(this.#debounceTimer);
|
|
180
|
+
this.#resetComposition();
|
|
181
|
+
this.#abortController?.abort();
|
|
182
|
+
this._open = false;
|
|
183
|
+
this._activeIndex = -1;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
117
186
|
updated(changed) {
|
|
118
187
|
if (changed.has("value"))
|
|
119
188
|
this.#internals.setFormValue(this.value);
|
|
@@ -122,6 +191,7 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
122
191
|
super.disconnectedCallback();
|
|
123
192
|
if (this.#debounceTimer)
|
|
124
193
|
clearTimeout(this.#debounceTimer);
|
|
194
|
+
this.#resetComposition();
|
|
125
195
|
this.#abortController?.abort();
|
|
126
196
|
}
|
|
127
197
|
/** Resets to empty on `form.reset()`, per the form-associated custom element contract. */
|
|
@@ -131,17 +201,34 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
131
201
|
this._open = false;
|
|
132
202
|
this.#lastPicked = null;
|
|
133
203
|
}
|
|
204
|
+
/** Mirrors ancestor fieldset disabled state onto the native input. */
|
|
205
|
+
formDisabledCallback(disabled) {
|
|
206
|
+
this._formDisabled = disabled;
|
|
207
|
+
if (!disabled)
|
|
208
|
+
return;
|
|
209
|
+
if (this.#debounceTimer)
|
|
210
|
+
clearTimeout(this.#debounceTimer);
|
|
211
|
+
this.#resetComposition();
|
|
212
|
+
this.#abortController?.abort();
|
|
213
|
+
this._open = false;
|
|
214
|
+
this._activeIndex = -1;
|
|
215
|
+
}
|
|
134
216
|
onInput(e) {
|
|
217
|
+
if (this.#isDisabled)
|
|
218
|
+
return;
|
|
135
219
|
this.value = e.target.value;
|
|
136
220
|
this.#lastPicked = null;
|
|
137
221
|
this.#scheduleFetch(this.value);
|
|
138
222
|
}
|
|
139
223
|
#scheduleFetch(query) {
|
|
224
|
+
if (this.#isDisabled)
|
|
225
|
+
return;
|
|
140
226
|
if (this.#debounceTimer)
|
|
141
227
|
clearTimeout(this.#debounceTimer);
|
|
142
228
|
if (query.trim().length < this.minLength) {
|
|
143
229
|
this._suggestions = [];
|
|
144
230
|
this._open = false;
|
|
231
|
+
this._activeIndex = -1;
|
|
145
232
|
return;
|
|
146
233
|
}
|
|
147
234
|
if (this.options) {
|
|
@@ -159,7 +246,7 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
159
246
|
.filter((o) => o.value.toLowerCase().includes(needle))
|
|
160
247
|
.slice(0, 5);
|
|
161
248
|
this._activeIndex = -1;
|
|
162
|
-
this._open = this
|
|
249
|
+
this._open = !this.#isDisabled;
|
|
163
250
|
}
|
|
164
251
|
/** Fetches options for `query` from `endpoint`, cancelling any request still in flight. */
|
|
165
252
|
async #fetchOptions(query) {
|
|
@@ -170,9 +257,11 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
170
257
|
const res = await fetch(this.#buildUrl(query), { signal: controller.signal });
|
|
171
258
|
if (!res.ok)
|
|
172
259
|
throw new Error(`autocomplete request failed: ${res.status}`);
|
|
260
|
+
if (this.#isDisabled)
|
|
261
|
+
return;
|
|
173
262
|
this._suggestions = (await res.json()).slice(0, 5);
|
|
174
263
|
this._activeIndex = -1;
|
|
175
|
-
this._open =
|
|
264
|
+
this._open = true;
|
|
176
265
|
}
|
|
177
266
|
catch {
|
|
178
267
|
// Aborted (superseded by newer keystroke) or a network failure while
|
|
@@ -188,6 +277,11 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
188
277
|
return `${this.endpoint}?${params.toString()}`;
|
|
189
278
|
}
|
|
190
279
|
onKeydown(e) {
|
|
280
|
+
if (this.#isDisabled)
|
|
281
|
+
return;
|
|
282
|
+
if (e.isComposing || e.keyCode === 229 || this.#isComposing || this.#compositionJustEnded) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
191
285
|
if (e.key === "ArrowDown")
|
|
192
286
|
this.#moveActive(1, e);
|
|
193
287
|
else if (e.key === "ArrowUp")
|
|
@@ -205,12 +299,19 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
205
299
|
this._activeIndex = (this._activeIndex + delta + n) % n;
|
|
206
300
|
}
|
|
207
301
|
#handleEnter(e) {
|
|
208
|
-
if (!this._open)
|
|
209
|
-
return; // closed list: let the form submit normally
|
|
210
|
-
e.preventDefault();
|
|
211
302
|
const pick = this._suggestions[this._activeIndex] ?? this._suggestions[0];
|
|
212
|
-
if (pick)
|
|
303
|
+
if (this._open && pick) {
|
|
304
|
+
e.preventDefault();
|
|
213
305
|
this.#selectOption(pick);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (this.#internals.form) {
|
|
309
|
+
const form = this.#internals.form;
|
|
310
|
+
window.setTimeout(() => {
|
|
311
|
+
if (!e.defaultPrevented && form.isConnected)
|
|
312
|
+
submitWithDefaultButton(form);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
214
315
|
}
|
|
215
316
|
#handleEscape(e) {
|
|
216
317
|
if (!this._open)
|
|
@@ -221,6 +322,31 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
221
322
|
onBlur() {
|
|
222
323
|
this._open = false;
|
|
223
324
|
}
|
|
325
|
+
onCompositionStart() {
|
|
326
|
+
if (this.#compositionEndTimer)
|
|
327
|
+
clearTimeout(this.#compositionEndTimer);
|
|
328
|
+
this.#compositionEndTimer = null;
|
|
329
|
+
this.#compositionJustEnded = false;
|
|
330
|
+
this.#isComposing = true;
|
|
331
|
+
}
|
|
332
|
+
onCompositionEnd() {
|
|
333
|
+
this.#isComposing = false;
|
|
334
|
+
this.#compositionJustEnded = true;
|
|
335
|
+
if (this.#compositionEndTimer)
|
|
336
|
+
clearTimeout(this.#compositionEndTimer);
|
|
337
|
+
this.#compositionEndTimer = setTimeout(() => {
|
|
338
|
+
this.#compositionJustEnded = false;
|
|
339
|
+
this.#compositionEndTimer = null;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/** Clears composition state when the control can no longer receive keys. */
|
|
343
|
+
#resetComposition() {
|
|
344
|
+
if (this.#compositionEndTimer)
|
|
345
|
+
clearTimeout(this.#compositionEndTimer);
|
|
346
|
+
this.#compositionEndTimer = null;
|
|
347
|
+
this.#isComposing = false;
|
|
348
|
+
this.#compositionJustEnded = false;
|
|
349
|
+
}
|
|
224
350
|
#selectOption(o) {
|
|
225
351
|
this.value = o.value;
|
|
226
352
|
this.#lastPicked = o;
|
|
@@ -234,12 +360,13 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
234
360
|
this.#selectOption(o);
|
|
235
361
|
}
|
|
236
362
|
renderSuggestions() {
|
|
237
|
-
if (!this._open || this
|
|
363
|
+
if (!this._open || this.#isDisabled)
|
|
238
364
|
return nothing;
|
|
239
365
|
return html `
|
|
240
|
-
<ul class="suggestions" role="listbox">
|
|
366
|
+
<ul id=${this.#listboxId} class="suggestions" role="listbox" aria-label="Suggestions">
|
|
241
367
|
${this._suggestions.map((o, i) => html `
|
|
242
368
|
<li
|
|
369
|
+
id=${`${this.#listboxId}-option-${i}`}
|
|
243
370
|
role="option"
|
|
244
371
|
aria-selected=${i === this._activeIndex}
|
|
245
372
|
class="suggestion ${i === this._activeIndex ? "active" : ""}"
|
|
@@ -248,19 +375,36 @@ let AutocompleteInput = class AutocompleteInput extends LitElement {
|
|
|
248
375
|
${o.value}
|
|
249
376
|
</li>
|
|
250
377
|
`)}
|
|
378
|
+
${this._suggestions.length === 0
|
|
379
|
+
? html `<li class="no-suggestions" role="presentation">
|
|
380
|
+
<span role="status">No suggestions found</span>
|
|
381
|
+
</li>`
|
|
382
|
+
: nothing}
|
|
251
383
|
</ul>
|
|
252
384
|
`;
|
|
253
385
|
}
|
|
254
386
|
render() {
|
|
387
|
+
const activeDescendant = this._open && !this.#isDisabled && this._activeIndex >= 0
|
|
388
|
+
? `${this.#listboxId}-option-${this._activeIndex}`
|
|
389
|
+
: nothing;
|
|
390
|
+
const expanded = this._open && !this.#isDisabled;
|
|
255
391
|
return html `
|
|
256
|
-
<div
|
|
392
|
+
<div>
|
|
257
393
|
<input
|
|
258
394
|
type="text"
|
|
395
|
+
role="combobox"
|
|
396
|
+
aria-autocomplete="list"
|
|
397
|
+
aria-expanded=${expanded}
|
|
398
|
+
aria-controls=${this.#listboxId}
|
|
399
|
+
aria-activedescendant=${activeDescendant}
|
|
259
400
|
.value=${this.value}
|
|
260
401
|
placeholder=${this.placeholder}
|
|
261
402
|
?required=${this.required}
|
|
403
|
+
?disabled=${this.#isDisabled}
|
|
262
404
|
autocomplete="off"
|
|
263
405
|
@input=${this.onInput}
|
|
406
|
+
@compositionstart=${this.onCompositionStart}
|
|
407
|
+
@compositionend=${this.onCompositionEnd}
|
|
264
408
|
@keydown=${this.onKeydown}
|
|
265
409
|
@blur=${this.onBlur}
|
|
266
410
|
/>
|
|
@@ -278,6 +422,9 @@ __decorate([
|
|
|
278
422
|
__decorate([
|
|
279
423
|
property({ type: Boolean })
|
|
280
424
|
], AutocompleteInput.prototype, "required", void 0);
|
|
425
|
+
__decorate([
|
|
426
|
+
property({ type: Boolean, reflect: true })
|
|
427
|
+
], AutocompleteInput.prototype, "disabled", void 0);
|
|
281
428
|
__decorate([
|
|
282
429
|
property()
|
|
283
430
|
], AutocompleteInput.prototype, "endpoint", void 0);
|
|
@@ -302,6 +449,9 @@ __decorate([
|
|
|
302
449
|
__decorate([
|
|
303
450
|
state()
|
|
304
451
|
], AutocompleteInput.prototype, "_activeIndex", void 0);
|
|
452
|
+
__decorate([
|
|
453
|
+
state()
|
|
454
|
+
], AutocompleteInput.prototype, "_formDisabled", void 0);
|
|
305
455
|
AutocompleteInput = __decorate([
|
|
306
456
|
customElement("autocomplete-input")
|
|
307
457
|
], AutocompleteInput);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete-input.js","sourceRoot":"","sources":["../src/autocomplete-input.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/** A single autocomplete candidate: an opaque `key` plus its display `value`. */\nexport interface AutocompleteOption {\n key: string;\n value: string;\n}\n\n/**\n * Generic form-associated text input with a suggestion dropdown, for any\n * \"type to filter a list of `{key, value}` options\" use case. Works as a\n * drop-in replacement for a plain `<input>` inside a `<form>`: set `name` on\n * the element itself and consumers keep reading\n * `new FormData(form).get(name)` and calling `form.reset()` unchanged — the\n * submitted value is the picked option's `value`, while `key` is available\n * via the `option-select` event and the `selectedOption` getter for cases\n * that need the underlying id rather than the display text.\n *\n * Supports two suggestion sources:\n * - **API**: set `endpoint` to a URL that, given a `?<query-param>=<text>`\n * query string, responds with a JSON array of `{key, value}` objects.\n * Requests are debounced as the user types.\n * - **Local array**: set `options` to a fixed `AutocompleteOption[]` and the\n * component filters it client-side instead of making any network\n * request. Useful for small/fixed lists, offline use, or tests. Takes\n * priority over `endpoint` whenever it's non-null.\n *\n * @element autocomplete-input\n * @fires option-select - An option was picked; detail: AutocompleteOption.\n */\n@customElement(\"autocomplete-input\")\nexport class AutocompleteInput extends LitElement {\n static formAssociated = true;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n position: relative;\n }\n input {\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n font-size: var(--ui-font-size-sm, 0.75rem);\n padding: 0.35rem 0.5rem;\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n width: 100%;\n box-sizing: border-box;\n }\n .suggestions {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 10;\n max-height: 40vh;\n overflow-y: auto;\n margin: 2px 0 0;\n padding: 4px 0;\n list-style: none;\n background: var(--ui-surface, #fff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));\n }\n .suggestion {\n padding: 0.4rem 0.6rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n cursor: pointer;\n }\n .suggestion.active,\n .suggestion:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n `,\n ];\n\n /** Current input value; also the form-associated value submitted with the form. */\n @property() value = \"\";\n /** Placeholder text shown when the input is empty. */\n @property() placeholder = \"\";\n /** Marks the input as required for native form validation. */\n @property({ type: Boolean }) required = false;\n /** API endpoint queried in API mode. Ignored when `options` is set. */\n @property() endpoint = \"\";\n /** Query string parameter name the current input text is sent under. */\n @property({ attribute: \"query-param\" }) queryParam = \"q\";\n /** Debounce delay (ms) before firing a request/filter after typing. */\n @property({ type: Number }) debounce = 300;\n /** Minimum query length before suggestions are shown. */\n @property({ type: Number, attribute: \"min-length\" }) minLength = 3;\n /**\n * Fixed candidate list to filter locally instead of fetching from\n * `endpoint`. When set (non-null), no network request is ever made —\n * this takes priority over the API mode.\n */\n @property({ attribute: false }) options: AutocompleteOption[] | null = null;\n\n @state() private _suggestions: AutocompleteOption[] = [];\n @state() private _open = false;\n @state() private _activeIndex = -1;\n\n #internals = this.attachInternals();\n #lastPicked: AutocompleteOption | null = null;\n #debounceTimer: ReturnType<typeof setTimeout> | null = null;\n #abortController: AbortController | null = null;\n\n /** Last-picked option, or null once the input diverges from it. */\n get selectedOption(): AutocompleteOption | null {\n if (this.#lastPicked && this.#lastPicked.value === this.value) return this.#lastPicked;\n return null;\n }\n\n protected override updated(changed: PropertyValues) {\n if (changed.has(\"value\")) this.#internals.setFormValue(this.value);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n if (this.#debounceTimer) clearTimeout(this.#debounceTimer);\n this.#abortController?.abort();\n }\n\n /** Resets to empty on `form.reset()`, per the form-associated custom element contract. */\n formResetCallback() {\n this.value = \"\";\n this._suggestions = [];\n this._open = false;\n this.#lastPicked = null;\n }\n\n private onInput(e: InputEvent) {\n this.value = (e.target as HTMLInputElement).value;\n this.#lastPicked = null;\n this.#scheduleFetch(this.value);\n }\n\n #scheduleFetch(query: string) {\n if (this.#debounceTimer) clearTimeout(this.#debounceTimer);\n if (query.trim().length < this.minLength) {\n this._suggestions = [];\n this._open = false;\n return;\n }\n if (this.options) {\n // Local mode: filtering is synchronous and cheap, but keep the same\n // debounce as the API mode so the UX doesn't shift between sources.\n this.#debounceTimer = setTimeout(() => this.#filterLocalOptions(query), this.debounce);\n return;\n }\n this.#debounceTimer = setTimeout(() => void this.#fetchOptions(query), this.debounce);\n }\n\n /** Filters the locally-supplied `options` list for `query`, case-insensitively. */\n #filterLocalOptions(query: string) {\n const needle = query.trim().toLowerCase();\n this._suggestions = (this.options ?? [])\n .filter((o) => o.value.toLowerCase().includes(needle))\n .slice(0, 5);\n this._activeIndex = -1;\n this._open = this._suggestions.length > 0;\n }\n\n /** Fetches options for `query` from `endpoint`, cancelling any request still in flight. */\n async #fetchOptions(query: string) {\n this.#abortController?.abort();\n const controller = new AbortController();\n this.#abortController = controller;\n try {\n const res = await fetch(this.#buildUrl(query), { signal: controller.signal });\n if (!res.ok) throw new Error(`autocomplete request failed: ${res.status}`);\n this._suggestions = ((await res.json()) as AutocompleteOption[]).slice(0, 5);\n this._activeIndex = -1;\n this._open = this._suggestions.length > 0;\n } catch {\n // Aborted (superseded by newer keystroke) or a network failure while\n // typing — fail silently and just close the list.\n if (!controller.signal.aborted) {\n this._suggestions = [];\n this._open = false;\n }\n }\n }\n\n #buildUrl(query: string): string {\n const params = new URLSearchParams({ [this.queryParam]: query });\n return `${this.endpoint}?${params.toString()}`;\n }\n\n private onKeydown(e: KeyboardEvent) {\n if (e.key === \"ArrowDown\") this.#moveActive(1, e);\n else if (e.key === \"ArrowUp\") this.#moveActive(-1, e);\n else if (e.key === \"Enter\") this.#handleEnter(e);\n else if (e.key === \"Escape\") this.#handleEscape(e);\n }\n\n #moveActive(delta: number, e: KeyboardEvent) {\n if (!this._open || this._suggestions.length === 0) return;\n e.preventDefault();\n const n = this._suggestions.length;\n this._activeIndex = (this._activeIndex + delta + n) % n;\n }\n\n #handleEnter(e: KeyboardEvent) {\n if (!this._open) return; // closed list: let the form submit normally\n e.preventDefault();\n const pick = this._suggestions[this._activeIndex] ?? this._suggestions[0];\n if (pick) this.#selectOption(pick);\n }\n\n #handleEscape(e: KeyboardEvent) {\n if (!this._open) return;\n e.preventDefault();\n this._open = false;\n }\n\n private onBlur() {\n this._open = false;\n }\n\n #selectOption(o: AutocompleteOption) {\n this.value = o.value;\n this.#lastPicked = o;\n this._open = false;\n this._suggestions = [];\n this.dispatchEvent(new CustomEvent<AutocompleteOption>(\"option-select\", { detail: o, bubbles: true, composed: true }));\n }\n\n /** Selects on mousedown (not click) so it wins over the input's blur, which closes the list. */\n #onSuggestionMousedown(e: MouseEvent, o: AutocompleteOption) {\n e.preventDefault();\n this.#selectOption(o);\n }\n\n private renderSuggestions() {\n if (!this._open || this._suggestions.length === 0) return nothing;\n return html`\n <ul class=\"suggestions\" role=\"listbox\">\n ${this._suggestions.map(\n (o, i) => html`\n <li\n role=\"option\"\n aria-selected=${i === this._activeIndex}\n class=\"suggestion ${i === this._activeIndex ? \"active\" : \"\"}\"\n @mousedown=${(e: MouseEvent) => this.#onSuggestionMousedown(e, o)}\n >\n ${o.value}\n </li>\n `,\n )}\n </ul>\n `;\n }\n\n override render() {\n return html`\n <div role=\"combobox\" aria-expanded=${this._open} aria-haspopup=\"listbox\">\n <input\n type=\"text\"\n .value=${this.value}\n placeholder=${this.placeholder}\n ?required=${this.required}\n autocomplete=\"off\"\n @input=${this.onInput}\n @keydown=${this.onKeydown}\n @blur=${this.onBlur}\n />\n ${this.renderSuggestions()}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"autocomplete-input\": AutocompleteInput;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"autocomplete-input.js","sourceRoot":"","sources":["../src/autocomplete-input.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\nimport { submitWithDefaultButton } from \"./utils/form.js\";\n\nlet instanceCount = 0;\n\n/** A single autocomplete candidate: an opaque `key` plus its display `value`. */\nexport interface AutocompleteOption {\n key: string;\n value: string;\n}\n\n/**\n * Generic form-associated text input with a suggestion dropdown, for any\n * \"type to filter a list of `{key, value}` options\" use case. Works as a\n * drop-in replacement for a plain `<input>` inside a `<form>`: set `name` on\n * the element itself and consumers keep reading\n * `new FormData(form).get(name)` and calling `form.reset()` unchanged — the\n * submitted value is the picked option's `value`, while `key` is available\n * via the `option-select` event and the `selectedOption` getter for cases\n * that need the underlying id rather than the display text.\n *\n * Supports two suggestion sources:\n * - **API**: set `endpoint` to a URL that, given a `?<query-param>=<text>`\n * query string, responds with a JSON array of `{key, value}` objects.\n * Requests are debounced as the user types.\n * - **Local array**: set `options` to a fixed `AutocompleteOption[]` and the\n * component filters it client-side instead of making any network\n * request. Useful for small/fixed lists, offline use, or tests. Takes\n * priority over `endpoint` whenever it's non-null.\n *\n * @element autocomplete-input\n * @fires option-select - An option was picked; detail: AutocompleteOption.\n */\n@customElement(\"autocomplete-input\")\nexport class AutocompleteInput extends LitElement {\n static formAssociated = true;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: block;\n position: relative;\n }\n input {\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n font-size: var(--ui-font-size-sm, 0.75rem);\n color: var(--ui-text, #0f172a);\n background: var(--ui-surface, #ffffff);\n padding: 0.5rem;\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n width: 100%;\n box-sizing: border-box;\n }\n input:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n }\n input:focus-visible {\n outline: none;\n border-color: var(--ui-primary, #4f46e5);\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n }\n .suggestions {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 10;\n max-height: 40vh;\n overflow-y: auto;\n margin: 0.25rem 0 0;\n padding: 0.25rem 0;\n list-style: none;\n background: var(--ui-surface, #ffffff);\n border: 1px solid var(--ui-border, #e2e8f0);\n border-radius: var(--ui-radius-sm, 0.25rem);\n box-shadow: var(--ui-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));\n }\n .suggestion {\n padding: 0.5rem 0.75rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n cursor: pointer;\n }\n .suggestion.active,\n .suggestion:hover {\n background: var(--ui-surface-muted, #f8fafc);\n }\n .no-suggestions {\n padding: 0.5rem 0.75rem;\n color: var(--ui-text-muted, #64748b);\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-style: italic;\n }\n @media (forced-colors: active) {\n input:focus-visible {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n input:disabled {\n color: GrayText;\n opacity: 1;\n }\n .suggestion.active,\n .suggestion:hover {\n color: HighlightText;\n background: Highlight;\n }\n }\n `,\n ];\n\n /** Current input value; also the form-associated value submitted with the form. */\n @property() value = \"\";\n /** Placeholder text shown when the input is empty. */\n @property() placeholder = \"\";\n /** Marks the input as required for native form validation. */\n @property({ type: Boolean }) required = false;\n /** Disables the input and closes its suggestion popup. */\n @property({ type: Boolean, reflect: true }) disabled = false;\n /** API endpoint queried in API mode. Ignored when `options` is set. */\n @property() endpoint = \"\";\n /** Query string parameter name the current input text is sent under. */\n @property({ attribute: \"query-param\" }) queryParam = \"q\";\n /** Debounce delay (ms) before firing a request/filter after typing. */\n @property({ type: Number }) debounce = 300;\n /** Minimum query length before suggestions are shown. */\n @property({ type: Number, attribute: \"min-length\" }) minLength = 3;\n /**\n * Fixed candidate list to filter locally instead of fetching from\n * `endpoint`. When set (non-null), no network request is ever made —\n * this takes priority over the API mode.\n */\n @property({ attribute: false }) options: AutocompleteOption[] | null = null;\n\n @state() private _suggestions: AutocompleteOption[] = [];\n @state() private _open = false;\n @state() private _activeIndex = -1;\n @state() private _formDisabled = false;\n\n #internals = this.attachInternals();\n #lastPicked: AutocompleteOption | null = null;\n #debounceTimer: ReturnType<typeof setTimeout> | null = null;\n #abortController: AbortController | null = null;\n #isComposing = false;\n #compositionJustEnded = false;\n #compositionEndTimer: ReturnType<typeof setTimeout> | null = null;\n readonly #listboxId = `autocomplete-input-listbox-${++instanceCount}`;\n\n /** Whether the host or an ancestor fieldset currently disables the control. */\n get #isDisabled(): boolean {\n return this.disabled || this._formDisabled;\n }\n\n /** Last-picked option, or null once the input diverges from it. */\n get selectedOption(): AutocompleteOption | null {\n if (this.#lastPicked && this.#lastPicked.value === this.value) return this.#lastPicked;\n return null;\n }\n\n protected override willUpdate(changed: PropertyValues) {\n if (changed.has(\"disabled\") && this.#isDisabled) {\n if (this.#debounceTimer) clearTimeout(this.#debounceTimer);\n this.#resetComposition();\n this.#abortController?.abort();\n this._open = false;\n this._activeIndex = -1;\n }\n }\n\n protected override updated(changed: PropertyValues) {\n if (changed.has(\"value\")) this.#internals.setFormValue(this.value);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n if (this.#debounceTimer) clearTimeout(this.#debounceTimer);\n this.#resetComposition();\n this.#abortController?.abort();\n }\n\n /** Resets to empty on `form.reset()`, per the form-associated custom element contract. */\n formResetCallback() {\n this.value = \"\";\n this._suggestions = [];\n this._open = false;\n this.#lastPicked = null;\n }\n\n /** Mirrors ancestor fieldset disabled state onto the native input. */\n formDisabledCallback(disabled: boolean) {\n this._formDisabled = disabled;\n if (!disabled) return;\n if (this.#debounceTimer) clearTimeout(this.#debounceTimer);\n this.#resetComposition();\n this.#abortController?.abort();\n this._open = false;\n this._activeIndex = -1;\n }\n\n private onInput(e: InputEvent) {\n if (this.#isDisabled) return;\n this.value = (e.target as HTMLInputElement).value;\n this.#lastPicked = null;\n this.#scheduleFetch(this.value);\n }\n\n #scheduleFetch(query: string) {\n if (this.#isDisabled) return;\n if (this.#debounceTimer) clearTimeout(this.#debounceTimer);\n if (query.trim().length < this.minLength) {\n this._suggestions = [];\n this._open = false;\n this._activeIndex = -1;\n return;\n }\n if (this.options) {\n // Local mode: filtering is synchronous and cheap, but keep the same\n // debounce as the API mode so the UX doesn't shift between sources.\n this.#debounceTimer = setTimeout(() => this.#filterLocalOptions(query), this.debounce);\n return;\n }\n this.#debounceTimer = setTimeout(() => void this.#fetchOptions(query), this.debounce);\n }\n\n /** Filters the locally-supplied `options` list for `query`, case-insensitively. */\n #filterLocalOptions(query: string) {\n const needle = query.trim().toLowerCase();\n this._suggestions = (this.options ?? [])\n .filter((o) => o.value.toLowerCase().includes(needle))\n .slice(0, 5);\n this._activeIndex = -1;\n this._open = !this.#isDisabled;\n }\n\n /** Fetches options for `query` from `endpoint`, cancelling any request still in flight. */\n async #fetchOptions(query: string) {\n this.#abortController?.abort();\n const controller = new AbortController();\n this.#abortController = controller;\n try {\n const res = await fetch(this.#buildUrl(query), { signal: controller.signal });\n if (!res.ok) throw new Error(`autocomplete request failed: ${res.status}`);\n if (this.#isDisabled) return;\n this._suggestions = ((await res.json()) as AutocompleteOption[]).slice(0, 5);\n this._activeIndex = -1;\n this._open = true;\n } catch {\n // Aborted (superseded by newer keystroke) or a network failure while\n // typing — fail silently and just close the list.\n if (!controller.signal.aborted) {\n this._suggestions = [];\n this._open = false;\n }\n }\n }\n\n #buildUrl(query: string): string {\n const params = new URLSearchParams({ [this.queryParam]: query });\n return `${this.endpoint}?${params.toString()}`;\n }\n\n private onKeydown(e: KeyboardEvent) {\n if (this.#isDisabled) return;\n if (e.isComposing || e.keyCode === 229 || this.#isComposing || this.#compositionJustEnded) {\n return;\n }\n if (e.key === \"ArrowDown\") this.#moveActive(1, e);\n else if (e.key === \"ArrowUp\") this.#moveActive(-1, e);\n else if (e.key === \"Enter\") this.#handleEnter(e);\n else if (e.key === \"Escape\") this.#handleEscape(e);\n }\n\n #moveActive(delta: number, e: KeyboardEvent) {\n if (!this._open || this._suggestions.length === 0) return;\n e.preventDefault();\n const n = this._suggestions.length;\n this._activeIndex = (this._activeIndex + delta + n) % n;\n }\n\n #handleEnter(e: KeyboardEvent) {\n const pick = this._suggestions[this._activeIndex] ?? this._suggestions[0];\n if (this._open && pick) {\n e.preventDefault();\n this.#selectOption(pick);\n return;\n }\n if (this.#internals.form) {\n const form = this.#internals.form;\n window.setTimeout(() => {\n if (!e.defaultPrevented && form.isConnected) submitWithDefaultButton(form);\n });\n }\n }\n\n #handleEscape(e: KeyboardEvent) {\n if (!this._open) return;\n e.preventDefault();\n this._open = false;\n }\n\n private onBlur() {\n this._open = false;\n }\n\n private onCompositionStart(): void {\n if (this.#compositionEndTimer) clearTimeout(this.#compositionEndTimer);\n this.#compositionEndTimer = null;\n this.#compositionJustEnded = false;\n this.#isComposing = true;\n }\n\n private onCompositionEnd(): void {\n this.#isComposing = false;\n this.#compositionJustEnded = true;\n if (this.#compositionEndTimer) clearTimeout(this.#compositionEndTimer);\n this.#compositionEndTimer = setTimeout(() => {\n this.#compositionJustEnded = false;\n this.#compositionEndTimer = null;\n });\n }\n\n /** Clears composition state when the control can no longer receive keys. */\n #resetComposition(): void {\n if (this.#compositionEndTimer) clearTimeout(this.#compositionEndTimer);\n this.#compositionEndTimer = null;\n this.#isComposing = false;\n this.#compositionJustEnded = false;\n }\n\n #selectOption(o: AutocompleteOption) {\n this.value = o.value;\n this.#lastPicked = o;\n this._open = false;\n this._suggestions = [];\n this.dispatchEvent(new CustomEvent<AutocompleteOption>(\"option-select\", { detail: o, bubbles: true, composed: true }));\n }\n\n /** Selects on mousedown (not click) so it wins over the input's blur, which closes the list. */\n #onSuggestionMousedown(e: MouseEvent, o: AutocompleteOption) {\n e.preventDefault();\n this.#selectOption(o);\n }\n\n private renderSuggestions() {\n if (!this._open || this.#isDisabled) return nothing;\n return html`\n <ul id=${this.#listboxId} class=\"suggestions\" role=\"listbox\" aria-label=\"Suggestions\">\n ${this._suggestions.map(\n (o, i) => html`\n <li\n id=${`${this.#listboxId}-option-${i}`}\n role=\"option\"\n aria-selected=${i === this._activeIndex}\n class=\"suggestion ${i === this._activeIndex ? \"active\" : \"\"}\"\n @mousedown=${(e: MouseEvent) => this.#onSuggestionMousedown(e, o)}\n >\n ${o.value}\n </li>\n `,\n )}\n ${this._suggestions.length === 0\n ? html`<li class=\"no-suggestions\" role=\"presentation\">\n <span role=\"status\">No suggestions found</span>\n </li>`\n : nothing}\n </ul>\n `;\n }\n\n override render() {\n const activeDescendant =\n this._open && !this.#isDisabled && this._activeIndex >= 0\n ? `${this.#listboxId}-option-${this._activeIndex}`\n : nothing;\n const expanded = this._open && !this.#isDisabled;\n return html`\n <div>\n <input\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded=${expanded}\n aria-controls=${this.#listboxId}\n aria-activedescendant=${activeDescendant}\n .value=${this.value}\n placeholder=${this.placeholder}\n ?required=${this.required}\n ?disabled=${this.#isDisabled}\n autocomplete=\"off\"\n @input=${this.onInput}\n @compositionstart=${this.onCompositionStart}\n @compositionend=${this.onCompositionEnd}\n @keydown=${this.onKeydown}\n @blur=${this.onBlur}\n />\n ${this.renderSuggestions()}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"autocomplete-input\": AutocompleteInput;\n }\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
import type { StatusPillColor } from "./status-pill.js";
|
|
3
|
+
/**
|
|
4
|
+
* Declarative metadata for one calendar event, consumed by a parent
|
|
5
|
+
* `calendar-month` or `calendar-year`. Read-only/non-interactive; renders
|
|
6
|
+
* nothing itself.
|
|
7
|
+
*
|
|
8
|
+
* @element calendar-entry
|
|
9
|
+
* @slot title - Plain-text title shown instead of the `label` fallback.
|
|
10
|
+
* @slot detail - Repeatable plain-text details rendered inside the shared body spanning all remaining days.
|
|
11
|
+
* @slot footer - Plain-text ending note pinned to the bottom of the shared body.
|
|
12
|
+
*/
|
|
13
|
+
export declare class CalendarEntry extends LitElement {
|
|
14
|
+
/** Inclusive start date, `"YYYY-MM-DD"`. */
|
|
15
|
+
start: string;
|
|
16
|
+
/** Inclusive end date, `"YYYY-MM-DD"`. Falls back to `start` when unset (single-day entry). */
|
|
17
|
+
end: string;
|
|
18
|
+
/** Fallback title used when no `title` slot is provided. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Color variant, reusing `status-pill`'s palette. */
|
|
21
|
+
color: StatusPillColor;
|
|
22
|
+
/** Optional link target; the parent renders the entry as an `<a>` when set. */
|
|
23
|
+
href?: string;
|
|
24
|
+
static styles: import("lit").CSSResult[];
|
|
25
|
+
/** Keeps the metadata host hidden while exposing named text slots to the parent calendar. */
|
|
26
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
27
|
+
}
|
|
28
|
+
declare global {
|
|
29
|
+
interface HTMLElementTagNameMap {
|
|
30
|
+
"calendar-entry": CalendarEntry;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=calendar-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-entry.d.ts","sourceRoot":"","sources":["../src/calendar-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAE5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD;;;;;;;;;GASG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,4CAA4C;IACf,KAAK,SAAM;IAExC,+FAA+F;IAClE,GAAG,SAAM;IAEtC,4DAA4D;IAC/B,KAAK,SAAM;IAExC,sDAAsD;IACzB,KAAK,EAAE,eAAe,CAAa;IAEhE,+EAA+E;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IAE3C,OAAgB,MAAM,4BAOpB;IAEF,6FAA6F;IAC7F,UAAmB,MAAM,yCAExB;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,gBAAgB,EAAE,aAAa,CAAC;KACjC;CACF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, css, html } from "lit";
|
|
8
|
+
import { customElement, property } from "lit/decorators.js";
|
|
9
|
+
import { tokens } from "./tokens.js";
|
|
10
|
+
/**
|
|
11
|
+
* Declarative metadata for one calendar event, consumed by a parent
|
|
12
|
+
* `calendar-month` or `calendar-year`. Read-only/non-interactive; renders
|
|
13
|
+
* nothing itself.
|
|
14
|
+
*
|
|
15
|
+
* @element calendar-entry
|
|
16
|
+
* @slot title - Plain-text title shown instead of the `label` fallback.
|
|
17
|
+
* @slot detail - Repeatable plain-text details rendered inside the shared body spanning all remaining days.
|
|
18
|
+
* @slot footer - Plain-text ending note pinned to the bottom of the shared body.
|
|
19
|
+
*/
|
|
20
|
+
let CalendarEntry = class CalendarEntry extends LitElement {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
/** Inclusive start date, `"YYYY-MM-DD"`. */
|
|
24
|
+
this.start = "";
|
|
25
|
+
/** Inclusive end date, `"YYYY-MM-DD"`. Falls back to `start` when unset (single-day entry). */
|
|
26
|
+
this.end = "";
|
|
27
|
+
/** Fallback title used when no `title` slot is provided. */
|
|
28
|
+
this.label = "";
|
|
29
|
+
/** Color variant, reusing `status-pill`'s palette. */
|
|
30
|
+
this.color = "neutral";
|
|
31
|
+
}
|
|
32
|
+
static { this.styles = [
|
|
33
|
+
tokens,
|
|
34
|
+
css `
|
|
35
|
+
:host {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
38
|
+
`,
|
|
39
|
+
]; }
|
|
40
|
+
/** Keeps the metadata host hidden while exposing named text slots to the parent calendar. */
|
|
41
|
+
render() {
|
|
42
|
+
return html `<slot name="title"></slot><slot name="detail"></slot><slot name="footer"></slot>`;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
__decorate([
|
|
46
|
+
property({ reflect: true })
|
|
47
|
+
], CalendarEntry.prototype, "start", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
property({ reflect: true })
|
|
50
|
+
], CalendarEntry.prototype, "end", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
property({ reflect: true })
|
|
53
|
+
], CalendarEntry.prototype, "label", void 0);
|
|
54
|
+
__decorate([
|
|
55
|
+
property({ reflect: true })
|
|
56
|
+
], CalendarEntry.prototype, "color", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
property({ reflect: true })
|
|
59
|
+
], CalendarEntry.prototype, "href", void 0);
|
|
60
|
+
CalendarEntry = __decorate([
|
|
61
|
+
customElement("calendar-entry")
|
|
62
|
+
], CalendarEntry);
|
|
63
|
+
export { CalendarEntry };
|
|
64
|
+
//# sourceMappingURL=calendar-entry.js.map
|