diff --git a/README.md b/README.md
index 5160a45fb..7102c33a8 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ If that's not what you're trying to do, the [documentation website](https://shoe
### What are you using to build Shoelace?
-Components are built with [Shoemaker](https://github.com/shoelace-style/shoemaker), a lightweight utility that provides an elegant API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
+Components are built with [LitElement](https://lit-element.polymer-project.org/), a custom elements base class that provides an intuitive API and reactive data binding. The build is a custom script with bundling powered by [esbuild](https://esbuild.github.io/).
### Forking the Repo
diff --git a/docs/assets/plugins/metadata/metadata.js b/docs/assets/plugins/metadata/metadata.js
index 224a742ad..11b6d630f 100644
--- a/docs/assets/plugins/metadata/metadata.js
+++ b/docs/assets/plugins/metadata/metadata.js
@@ -56,6 +56,7 @@
Event
Description
+
Details
@@ -65,6 +66,7 @@
${escapeHtml(event.name)}
${escapeHtml(event.description)}
+
${escapeHtml(event.details)}
`
)
@@ -379,7 +381,7 @@
result += `
## Dependencies
- This component has the following dependencies so, if you're [cherry picking](/getting-started/installation#cherry-picking),
+ This component has the following dependencies so, if you're [cherry picking](/getting-started/installation#cherry-picking),
be sure to import these components in addition to <${tag}>.
${createDependenciesList(component.tag, metadata.components)}
diff --git a/docs/components/icon-button.md b/docs/components/icon-button.md
index 9afc9f87b..d363c0d80 100644
--- a/docs/components/icon-button.md
+++ b/docs/components/icon-button.md
@@ -8,8 +8,6 @@ For a full list of icons that come bundled with Shoelace, refer to the [icon com
```html preview
-
-
```
## Examples
diff --git a/docs/components/rating.md b/docs/components/rating.md
index b5624ec91..c848d244d 100644
--- a/docs/components/rating.md
+++ b/docs/components/rating.md
@@ -53,7 +53,12 @@ Use the `disable` attribute to disable the rating.
### Custom Icons
```html preview
-
+
+
+
```
### Value-based Icons
diff --git a/docs/getting-started/changelog.md b/docs/getting-started/changelog.md
index 9d220bbd6..153be7db2 100644
--- a/docs/getting-started/changelog.md
+++ b/docs/getting-started/changelog.md
@@ -6,16 +6,19 @@ Components with the Experimental badge
_During the beta period, these restrictions may be relaxed in the event of a mission-critical bug._ 🐛
-## Next
+## 2.0.0-beta.29
-- Added `vscode.html-custom-data.json` to the build to support IntelliSense
+**This release migrates component implementations from Shoemaker to LitElement.** Due to feedback from the community, Shoelace will rely on a more heavily tested library for component implementations. This gives you a more solid foundation and reduces my maintenance burden. Thank you for all your comments, concerns, and encouragement! Aside from that, everything else from beta.28 still applies plus the following.
+
+- 🚨 BREAKING: removed the `symbol` prop from `sl-rating` and reverted to `getSymbol` for optimal flexibility
+- Added `vscode.html-custom-data.json` to the build to support IntelliSense (see [the usage section](/getting-started/usage#code-completion) for details)
- Added a style to prevent FOUC before components are defined
- Moved chunk files into a separate folder
- Updated esbuild to 0.8.54
## 2.0.0-beta.28
-**This release includes a major under the hood overhaul of the library and how it's distributed.** Until now, Shoelace was developed with Stencil. This release moves to a lightweight tool called [Shoemaker](https://github.com/shoelace-style/shoemaker), a homegrown utility that provides declarative templating and data binding while reducing the boilerplate required for said features. The base class is open source and less than [200 lines of code](https://github.com/shoelace-style/shoemaker/blob/master/src/shoemaker.ts).
+**This release includes a major under the hood overhaul of the library and how it's distributed.** Until now, Shoelace was developed with Stencil. This release moves to a lightweight tool called Shoemaker, a homegrown utility that provides declarative templating and data binding while reducing the boilerplate required for said features.
This change in tooling addresses a number of longstanding bugs and limitations. It also gives us more control over the library and build process while streamlining development and maintenance. Instead of two different distributions, Shoelace now offers a single, standards-compliant collection of ES modules. This may affect how you install and use the library, so please refer to the [installation page](/getting-started/installation) for details.
diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md
index 6d66a74e5..bf82c73ae 100644
--- a/docs/getting-started/overview.md
+++ b/docs/getting-started/overview.md
@@ -113,7 +113,7 @@ Designing, developing, and supporting this library requires a lot of time, effor
Special thanks to the following projects and individuals that helped make Shoelace possible.
-- Components are built with [Shoemaker](https://github.com/shoelace-style/shoemaker)
+- Components are built with [LitElement](https://lit-element.polymer-project.org/)
- Documentation is powered by [Docsify](https://docsify.js.org/)
- CDN services are provided by [jsDelivr](https://www.jsdelivr.com/)
- The default theme is based on color palettes from [Tailwind](https://tailwindcss.com/)
diff --git a/package-lock.json b/package-lock.json
index 755fc5865..8229d373d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -118,11 +118,6 @@
"resolved": "https://registry.npmjs.org/@shoelace-style/animations/-/animations-1.1.0.tgz",
"integrity": "sha512-Be+cahtZyI2dPKRm8EZSx3YJQ+jLvEcn3xzRP7tM4tqBnvd/eW/64Xh0iOf0t2w5P8iJKfdBbpVNE9naCaOf2g=="
},
- "@shoelace-style/shoemaker": {
- "version": "1.0.0-beta.6",
- "resolved": "https://registry.npmjs.org/@shoelace-style/shoemaker/-/shoemaker-1.0.0-beta.6.tgz",
- "integrity": "sha512-uIogfUXZiczjZj0J5r6yEf0PFcop7aT72xqDK+tdhE68yAQ66Xx/UMNwZJnICozUAoqX0FdNd1gFH2EBJXukOw=="
- },
"@sideway/address": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.1.tgz",
@@ -2308,6 +2303,19 @@
"integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
"dev": true
},
+ "lit-element": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.4.0.tgz",
+ "integrity": "sha512-pBGLglxyhq/Prk2H91nA0KByq/hx/wssJBQFiYqXhGDvEnY31PRGYf1RglVzyLeRysu0IHm2K0P196uLLWmwFg==",
+ "requires": {
+ "lit-html": "^1.1.1"
+ }
+ },
+ "lit-html": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.3.0.tgz",
+ "integrity": "sha512-0Q1bwmaFH9O14vycPHw8C/IeHMk/uSDldVLIefu/kfbTBGIc44KGH6A8p1bDfxUfHdc8q6Ct7kQklWoHgr4t1Q=="
+ },
"localtunnel": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.1.tgz",
diff --git a/package.json b/package.json
index 924c7562e..4250c7907 100644
--- a/package.json
+++ b/package.json
@@ -37,8 +37,9 @@
"dependencies": {
"@popperjs/core": "^2.7.0",
"@shoelace-style/animations": "^1.1.0",
- "@shoelace-style/shoemaker": "^1.0.0-beta.6",
- "color": "^3.1.3"
+ "color": "^3.1.3",
+ "lit-element": "^2.4.0",
+ "lit-html": "^1.3.0"
},
"devDependencies": {
"@types/color": "^3.0.1",
diff --git a/scripts/make-metadata.cjs b/scripts/make-metadata.cjs
index f2002cf26..435fa2cc1 100644
--- a/scripts/make-metadata.cjs
+++ b/scripts/make-metadata.cjs
@@ -98,14 +98,12 @@ components.map(async component => {
const dependencies = tags.filter(item => item.tag === 'dependency');
const slots = tags.filter(item => item.tag === 'slot');
const parts = tags.filter(item => item.tag === 'part');
- const events = tags.filter(item => item.tag === 'emit');
api.since = tags.find(item => item.tag === 'since').text.trim();
api.status = tags.find(item => item.tag === 'status').text.trim();
api.dependencies = dependencies.map(tag => tag.text.trim());
api.slots = slots.map(tag => splitText(tag.text));
api.parts = parts.map(tag => splitText(tag.text));
- api.events = events.map(tag => splitText(tag.text));
} else {
console.error(chalk.yellow(`Missing comment block for ${component.name} - skipping metadata`));
}
@@ -113,6 +111,7 @@ components.map(async component => {
// Props
const props = component.children
.filter(child => child.kindString === 'Property' && !child.flags.isStatic)
+ .filter(child => child.type.name !== 'EventEmitter')
.filter(child => child.comment && child.comment.shortText); // only with comments
props.map(prop => {
@@ -127,6 +126,24 @@ components.map(async component => {
});
});
+ // Events
+ const events = component.children
+ .filter(child => child.kindString === 'Property' && !child.flags.isStatic)
+ .filter(child => child.type.name === 'EventEmitter')
+ .filter(child => child.comment && child.comment.shortText); // only with comments
+
+ events.map(event => {
+ const decorator = event.decorators.filter(dec => dec.name === 'event')[0];
+ const name = (decorator ? decorator.arguments.eventName : event.name).replace(/['"`]/g, '');
+ const details = event.type.typeArguments.map(arg => arg.name).join(', ');
+
+ api.events.push({
+ name,
+ description: event.comment.shortText,
+ details
+ });
+ });
+
// Methods
const methods = component.children
.filter(child => child.kindString === 'Method' && !child.flags.isStatic)
diff --git a/src/components/alert/alert.ts b/src/components/alert/alert.ts
index 3f4809c43..8f1462abe 100644
--- a/src/components/alert/alert.ts
+++ b/src/components/alert/alert.ts
@@ -1,4 +1,6 @@
-import { classMap, html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, internalProperty, property, unsafeCSS } from 'lit-element';
+import { classMap } from 'lit-html/directives/class-map';
+import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./alert.scss';
const toastStack = Object.assign(document.createElement('div'), { className: 'sl-toast-stack' });
@@ -16,37 +18,45 @@ const toastStack = Object.assign(document.createElement('div'), { className: 'sl
* @part icon - The container that wraps the alert icon.
* @part message - The alert message.
* @part close-button - The close button.
- *
- * @emit sl-show - Emitted when the alert opens. Calling `event.preventDefault()` will prevent it from being opened.
- * @emit sl-after-show - Emitted after the alert opens and all transitions are complete.
- * @emit sl-hide - Emitted when the alert closes. Calling `event.preventDefault()` will prevent it from being closed.
- * @emit sl-after-hide - Emitted after the alert closes and all transitions are complete.
*/
-export default class SlAlert extends Shoemaker {
- static tag = 'sl-alert';
- static props = ['isVisible', 'open', 'closable', 'type', 'duration'];
- static reflect = ['open', 'type'];
- static styles = styles;
+@customElement('sl-alert')
+export class SlAlert extends LitElement {
+ static styles = unsafeCSS(styles);
private autoHideTimeout: any;
- private isVisible = false;
+
+ @internalProperty() private isVisible = false;
/** Indicates whether or not the alert is open. You can use this in lieu of the show/hide methods. */
- open = false;
+ @property({ type: Boolean, reflect: true }) open = false;
/** Makes the alert closable. */
- closable = false;
+ @property({ type: Boolean, reflect: true }) closable = false;
/** The type of alert. */
- type: 'primary' | 'success' | 'info' | 'warning' | 'danger' = 'primary';
+ @property({ reflect: true }) type: 'primary' | 'success' | 'info' | 'warning' | 'danger' = 'primary';
/**
* The length of time, in milliseconds, the alert will show before closing itself. If the user interacts with
* the alert before it closes (e.g. moves the mouse over it), the timer will restart. Defaults to `Infinity`.
*/
- duration = Infinity;
+ @property({ type: Number }) duration = Infinity;
+
+ /** Emitted when the alert opens. Calling `event.preventDefault()` will prevent it from being opened. */
+ @event('sl-show') slShow: EventEmitter;
+
+ /** Emitted after the alert opens and all transitions are complete. */
+ @event('sl-after-show') slAfterShow: EventEmitter;
+
+ /** Emitted when the alert closes. Calling `event.preventDefault()` will prevent it from being closed. */
+ @event('sl-hide') slHide: EventEmitter;
+
+ /** Emitted after the alert closes and all transitions are complete. */
+ @event('sl-after-hide') slAfterHide: EventEmitter;
+
+ connectedCallback() {
+ super.connectedCallback();
- onConnect() {
// Show on init if open
if (this.open) {
this.show();
@@ -60,7 +70,7 @@ export default class SlAlert extends Shoemaker {
return;
}
- const slShow = this.emit('sl-show');
+ const slShow = this.slShow.emit();
if (slShow.defaultPrevented) {
this.open = false;
return;
@@ -81,7 +91,7 @@ export default class SlAlert extends Shoemaker {
return;
}
- const slHide = this.emit('sl-hide');
+ const slHide = this.slHide.emit();
if (slHide.defaultPrevented) {
this.open = true;
return;
@@ -143,15 +153,15 @@ export default class SlAlert extends Shoemaker {
// Ensure we only emit one event when the target element is no longer visible
if (event.propertyName === 'opacity' && target.classList.contains('alert')) {
this.isVisible = this.open;
- this.open ? this.emit('sl-after-show') : this.emit('sl-after-hide');
+ this.open ? this.slAfterShow.emit() : this.slAfterHide.emit();
}
}
- watchOpen() {
+ openChanged() {
this.open ? this.show() : this.hide();
}
- watchDuration() {
+ durationChanged() {
this.restartAutoHide();
}
@@ -174,21 +184,25 @@ export default class SlAlert extends Shoemaker {
aria-live="assertive"
aria-atomic="true"
aria-hidden=${this.open ? 'false' : 'true'}
- onmousemove=${this.handleMouseMove.bind(this)}
- ontransitionend=${this.handleTransitionEnd.bind(this)}
+ @mousemove=${this.handleMouseMove.bind(this)}
+ @transitionend=${this.handleTransitionEnd.bind(this)}
>
-
+
-
+
${this.closable
? html`
-
+
`
: ''}
diff --git a/src/components/animation/animation.ts b/src/components/animation/animation.ts
index 4d4e8cac8..898601641 100644
--- a/src/components/animation/animation.ts
+++ b/src/components/animation/animation.ts
@@ -1,4 +1,5 @@
-import { html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, property, queryAsync, unsafeCSS } from 'lit-element';
+import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./animation.scss';
import { animations } from './animations';
@@ -7,95 +8,134 @@ import { animations } from './animations';
* @status stable
*
* @slot - The element to animate. If multiple elements are to be animated, wrap them in a single container.
- *
- * @emit sl-cancel - Emitted when the animation is canceled.
- * @emit sl-finish - Emitted when the animation finishes.
- * @emit sl-start - Emitted when the animation starts or restarts.
*/
-export default class SlAnimation extends Shoemaker {
- static tag = 'sl-animation';
- static props = [
- 'name',
- 'delay',
- 'direction',
- 'duration',
- 'easing',
- 'endDelay',
- 'fill',
- 'iterations',
- 'iterationStart',
- 'keyframes',
- 'playbackRate',
- 'pause'
- ];
- static reflect = ['name', 'pause'];
- static styles = styles;
+@customElement('sl-animation')
+export class SlAnimation extends LitElement {
+ static styles = unsafeCSS(styles);
private animation: Animation;
- private defaultSlot: HTMLSlotElement;
private hasStarted = false;
+ @queryAsync('slot') defaultSlot: Promise;
+
/** The name of the built-in animation to use. For custom animations, use the `keyframes` prop. */
- name = 'none';
+ @property({ reflect: true }) name = 'none';
/** The number of milliseconds to delay the start of the animation. */
- delay = 0;
+ @property({ type: Number }) delay = 0;
/** Determines the direction of playback as well as the behavior when reaching the end of an iteration. */
- direction: PlaybackDirection = 'normal';
+ @property() direction: PlaybackDirection = 'normal';
/** The number of milliseconds each iteration of the animation takes to complete. */
- duration = 1000;
+ @property({ type: Number }) duration = 1000;
/**
* The easing function to use for the animation. This can be a Shoelace easing function or a custom easing function
* such as `cubic-bezier(0, 1, .76, 1.14)`.
*/
- easing = 'linear';
+ @property() easing = 'linear';
/** The number of milliseconds to delay after the active period of an animation sequence. */
- endDelay = 0;
+ @property({ type: Number }) endDelay = 0;
/** Sets how the animation applies styles to its target before and after its execution. */
- fill: FillMode = 'auto';
+ @property() fill: FillMode = 'auto';
/** The number of iterations to run before the animation completes. Defaults to `Infinity`, which loops. */
- iterations: number = Infinity;
+ @property({ type: Number }) iterations: number = Infinity;
/** The offset at which to start the animation, usually between 0 (start) and 1 (end). */
- iterationStart = 0;
+ @property({ attribute: 'iteration-start', type: Number }) iterationStart = 0;
/** The keyframes to use for the animation. If this is set, `name` will be ignored. */
- keyframes: Keyframe[];
+ @property() keyframes: Keyframe[];
/**
* Sets the animation's playback rate. The default is `1`, which plays the animation at a normal speed. Setting this
* to `2`, for example, will double the animation's speed. A negative value can be used to reverse the animation. This
* value can be changed without causing the animation to restart.
*/
- playbackRate = 1;
+ @property({ attribute: 'playback-rate', type: Number }) playbackRate = 1;
/** Pauses the animation. The animation will resume when this prop is removed. */
- pause = false;
+ @property({ type: Boolean }) pause = false;
- onReady() {
+ /** Emitted when the animation is canceled. */
+ @event('sl-cancel') slCancel: EventEmitter;
+
+ /** Emitted when the animation finishes. */
+ @event('sl-finish') slFinish: EventEmitter;
+
+ /** Emitted when the animation starts or restarts. */
+ @event('sl-start') slStart: EventEmitter;
+
+ connectedCallback() {
+ super.connectedCallback();
this.createAnimation();
}
- onDisconnect() {
+ disconnectedCallback() {
+ super.disconnectedCallback();
this.destroyAnimation();
}
+ update(changedProps: Map) {
+ super.update(changedProps);
+
+ if (changedProps.has('pause')) {
+ this.handlePauseChange();
+ }
+
+ if (changedProps.has('playbackRate')) {
+ this.handlePlaybackRateChange();
+ }
+
+ if (
+ [
+ 'name',
+ 'delay',
+ 'direction',
+ 'duration',
+ 'easing',
+ 'endDelay',
+ 'fill',
+ 'iterations',
+ 'iterationsStart',
+ 'keyframes'
+ ].find(prop => changedProps.has(prop))
+ ) {
+ this.createAnimation();
+ }
+ }
+
handleAnimationFinish() {
- this.emit('sl-finish');
+ this.slFinish.emit();
}
handleAnimationCancel() {
- this.emit('sl-cancel');
+ this.slCancel.emit();
+ }
+
+ handlePauseChange() {
+ if (this.animation) {
+ this.pause ? this.animation.pause() : this.animation.play();
+
+ if (!this.pause && !this.hasStarted) {
+ this.hasStarted = true;
+ this.slStart.emit();
+ }
+
+ return true;
+ } else {
+ return false;
+ }
}
handlePlaybackRateChange() {
- this.animation.playbackRate = this.playbackRate;
+ if (this.animation) {
+ this.animation.playbackRate = this.playbackRate;
+ }
}
handleSlotChange() {
@@ -103,13 +143,14 @@ export default class SlAnimation extends Shoemaker {
this.createAnimation();
}
- createAnimation() {
+ async createAnimation() {
const easing = animations.easings[this.easing] || this.easing;
const keyframes: Keyframe[] = this.keyframes ? this.keyframes : (animations as any)[this.name];
- const element = this.defaultSlot.assignedElements({ flatten: true })[0] as HTMLElement;
+ const slot = await this.defaultSlot;
+ const element = slot.assignedElements()[0] as HTMLElement;
if (!element) {
- return;
+ return false;
}
this.destroyAnimation();
@@ -131,8 +172,10 @@ export default class SlAnimation extends Shoemaker {
this.animation.pause();
} else {
this.hasStarted = true;
- this.emit('sl-start');
+ this.slStart.emit();
}
+
+ return true;
}
destroyAnimation() {
@@ -144,56 +187,6 @@ export default class SlAnimation extends Shoemaker {
}
}
- // Restart the animation when any of these properties change
- watchDelay() {
- this.createAnimation();
- }
-
- watchDirection() {
- this.createAnimation();
- }
-
- watchEasing() {
- this.createAnimation();
- }
-
- watchEndDelay() {
- this.createAnimation();
- }
-
- watchFill() {
- this.createAnimation();
- }
-
- watchIterations() {
- this.createAnimation();
- }
-
- watchIterationStart() {
- this.createAnimation();
- }
-
- watchKeyframes() {
- this.createAnimation();
- }
-
- watchName() {
- this.createAnimation();
- }
-
- watchPause() {
- this.pause ? this.animation.pause() : this.animation.play();
-
- if (!this.pause && !this.hasStarted) {
- this.hasStarted = true;
- this.emit('sl-start');
- }
- }
-
- watchPlaybackRate() {
- this.animation.playbackRate = this.playbackRate;
- }
-
/** Clears all KeyframeEffects caused by this animation and aborts its playback. */
cancel() {
try {
@@ -219,8 +212,6 @@ export default class SlAnimation extends Shoemaker {
}
render() {
- return html`
- (this.defaultSlot = el)} onslotchange=${this.handleSlotChange.bind(this)} />
- `;
+ return html` `;
}
}
diff --git a/src/components/avatar/avatar.ts b/src/components/avatar/avatar.ts
index 77b6686c7..8eaa2eb1f 100644
--- a/src/components/avatar/avatar.ts
+++ b/src/components/avatar/avatar.ts
@@ -1,4 +1,5 @@
-import { classMap, html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, internalProperty, property, unsafeCSS } from 'lit-element';
+import { classMap } from 'lit-html/directives/class-map';
import styles from 'sass:./avatar.scss';
/**
@@ -14,25 +15,23 @@ import styles from 'sass:./avatar.scss';
* @part initials - The container that wraps the avatar initials.
* @part image - The avatar image.
*/
-export default class SlAvatar extends Shoemaker {
- static tag = 'sl-avatar';
- static props = ['hasError', 'image', 'alt', 'initials', 'shape'];
- static reflect = ['shape'];
- static styles = styles;
+@customElement('sl-avatar')
+export class SlAvatar extends LitElement {
+ static styles = unsafeCSS(styles);
- private hasError = false;
+ @internalProperty() private hasError = false;
/** The image source to use for the avatar. */
- image = '';
+ @property({ reflect: true }) image: string;
/** Alternative text for the image. */
- alt = '';
+ @property({ reflect: true }) alt: string;
/** Initials to use as a fallback when no image is available (1-2 characters max recommended). */
- initials = '';
+ @property({ reflect: true }) initials: string;
/** The shape of the avatar. */
- shape: 'circle' | 'square' | 'rounded' = 'circle';
+ @property({ reflect: true }) shape: 'circle' | 'square' | 'rounded' = 'circle';
render() {
return html`
@@ -58,7 +57,7 @@ export default class SlAvatar extends Shoemaker {
`}
${this.image && !this.hasError
? html`
-
+ (this.hasError = true)}" />
`
: ''}
diff --git a/src/components/badge/badge.ts b/src/components/badge/badge.ts
index 3aff024f3..1f78cfa53 100644
--- a/src/components/badge/badge.ts
+++ b/src/components/badge/badge.ts
@@ -1,4 +1,5 @@
-import { classMap, html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, property, unsafeCSS } from 'lit-element';
+import { classMap } from 'lit-html/directives/class-map';
import styles from 'sass:./badge.scss';
/**
@@ -9,20 +10,18 @@ import styles from 'sass:./badge.scss';
*
* @part base - The base wrapper
*/
-export default class SlBadge extends Shoemaker {
- static tag = 'sl-badge';
- static props = ['type', 'pill', 'pulse'];
- static reflect = ['type', 'pill', 'pulse'];
- static styles = styles;
+@customElement('sl-badge')
+export class SlBadge extends LitElement {
+ static styles = unsafeCSS(styles);
/** The badge's type. */
- type: 'primary' | 'success' | 'info' | 'warning' | 'danger' = 'primary';
+ @property({ reflect: true }) type: 'primary' | 'success' | 'info' | 'warning' | 'danger' = 'primary';
/** Draws a pill-style badge with rounded edges. */
- pill = false;
+ @property({ type: Boolean, reflect: true }) pill = false;
/** Makes the badge pulsate to draw attention. */
- pulse = false;
+ @property({ type: Boolean, reflect: true }) pulse = false;
render() {
return html`
@@ -40,7 +39,7 @@ export default class SlBadge extends Shoemaker {
})}
role="status"
>
-
+
`;
}
diff --git a/src/components/button-group/button-group.scss b/src/components/button-group/button-group.scss
index bdcd9bf5b..b454a220a 100644
--- a/src/components/button-group/button-group.scss
+++ b/src/components/button-group/button-group.scss
@@ -10,10 +10,6 @@
position: relative;
}
-::slotted(.sl-hover) {
+::slotted(.sl-focus) {
z-index: 1;
}
-
-::slotted(.sl-focus) {
- z-index: 2;
-}
diff --git a/src/components/button-group/button-group.ts b/src/components/button-group/button-group.ts
index 72c0ea5a6..7bf6953b9 100644
--- a/src/components/button-group/button-group.ts
+++ b/src/components/button-group/button-group.ts
@@ -1,4 +1,4 @@
-import { html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, property, unsafeCSS } from 'lit-element';
import styles from 'sass:./button-group.scss';
/**
@@ -9,29 +9,12 @@ import styles from 'sass:./button-group.scss';
*
* @part base - The component's base wrapper.
*/
-
-export default class SlButtonGroup extends Shoemaker {
- static tag = 'sl-button-group';
- static props = ['label'];
- static styles = styles;
-
- private buttonGroup: HTMLElement;
+@customElement('sl-button-group')
+export class SlButtonGroup extends LitElement {
+ static styles = unsafeCSS(styles);
/** A label to use for the button group's `aria-label` attribute. */
- label = '';
-
- onReady() {
- this.handleFocus = this.handleFocus.bind(this);
- this.handleBlur = this.handleBlur.bind(this);
-
- this.buttonGroup.addEventListener('sl-focus', this.handleFocus);
- this.buttonGroup.addEventListener('sl-blur', this.handleBlur);
- }
-
- onDisconnect() {
- this.buttonGroup.removeEventListener('sl-focus', this.handleFocus);
- this.buttonGroup.removeEventListener('sl-blur', this.handleBlur);
- }
+ @property() label: string;
handleFocus(event: CustomEvent) {
const button = event.target as HTMLElement;
@@ -46,12 +29,13 @@ export default class SlButtonGroup extends Shoemaker {
render() {
return html`
diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts
index 623d98d4c..d13b08c7b 100644
--- a/src/components/dialog/dialog.ts
+++ b/src/components/dialog/dialog.ts
@@ -1,4 +1,6 @@
-import { classMap, html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, internalProperty, property, query, unsafeCSS } from 'lit-element';
+import { classMap } from 'lit-html/directives/class-map';
+import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./dialog.scss';
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
import { hasSlot } from '../../internal/slot';
@@ -27,47 +29,61 @@ let id = 0;
* @part close-button - The close button.
* @part body - The dialog body.
* @part footer - The dialog footer.
- *
- * @emit sl-show - Emitted when the dialog opens. Calling `event.preventDefault()` will prevent it from being opened.
- * @emit sl-after-show - Emitted after the dialog opens and all transitions are complete.
- * @emit sl-hide - Emitted when the dialog closes. Calling `event.preventDefault()` will prevent it from being closed.
- * @emit sl-after-hide - Emitted after the dialog closes and all transitions are complete.
- * @emit sl-initial-focus - Emitted when the dialog opens and the panel gains focus. Calling `event.preventDefault()`
- * will prevent focus and allow you to set it on a different element in the dialog, such as an input or button.
- * @emit sl-overlay-dismiss - Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the
- * dialog from closing.
*/
-export default class SlDialog extends Shoemaker {
- static tag = 'sl-dialog';
- static props = ['hasFooter', 'isVisible', 'open', 'label', 'noHeader'];
- static reflect = ['open'];
- static styles = styles;
+@customElement('sl-dialog')
+export class SlDialog extends LitElement {
+ static styles = unsafeCSS(styles);
+
+ @query('.dialog') dialog: HTMLElement;
+ @query('.dialog__panel') panel: HTMLElement;
private componentId = `dialog-${++id}`;
- private dialog: HTMLElement;
- private hasFooter = false;
- private isVisible = false;
private modal: Modal;
- private panel: HTMLElement;
private willShow = false;
private willHide = false;
+ @internalProperty() private hasFooter = false;
+ @internalProperty() private isVisible = false;
+
/** Indicates whether or not the dialog is open. You can use this in lieu of the show/hide methods. */
- open = false;
+ @property({ type: Boolean, reflect: true }) open = false;
/**
* The dialog's label as displayed in the header. You should always include a relevant label even when using
* `no-header`, as it is required for proper accessibility.
*/
- label = '';
+ @property({ reflect: true }) label = '';
/**
* Disables the header. This will also remove the default close button, so please ensure you provide an easy,
* accessible way for users to dismiss the dialog.
*/
- noHeader = false;
+ @property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
+
+ /** Emitted when the dialog opens. Calling `event.preventDefault()` will prevent it from being opened. */
+ @event('sl-show') slShow: EventEmitter;
+
+ /** Emitted after the dialog opens and all transitions are complete. */
+ @event('sl-after-show') slAfterShow: EventEmitter;
+
+ /** Emitted when the dialog closes. Calling `event.preventDefault()` will prevent it from being closed. */
+ @event('sl-hide') slHide: EventEmitter;
+
+ /** Emitted after the dialog closes and all transitions are complete. */
+ @event('sl-after-hide') slAfterHide: EventEmitter;
+
+ /**
+ * Emitted when the dialog opens and the panel gains focus. Calling `event.preventDefault()` will prevent focus and
+ * allow you to set it on a different element in the dialog, such as an input or button.
+ */
+ @event('sl-initial-focus') slInitialFocus: EventEmitter;
+
+ /** Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the dialog from closing. */
+ @event('sl-overlay-dismiss') slOverlayDismiss: EventEmitter;
+
+ connectedCallback() {
+ super.connectedCallback();
- onConnect() {
this.modal = new Modal(this, {
onfocusOut: () => this.panel.focus()
});
@@ -80,7 +96,8 @@ export default class SlDialog extends Shoemaker {
}
}
- onDisconnect() {
+ disconnectedCallback() {
+ super.disconnectedCallback();
unlockBodyScrolling(this);
}
@@ -90,7 +107,7 @@ export default class SlDialog extends Shoemaker {
return;
}
- const slShow = this.emit('sl-show');
+ const slShow = this.slShow.emit();
if (slShow.defaultPrevented) {
this.open = false;
return;
@@ -107,15 +124,15 @@ export default class SlDialog extends Shoemaker {
if (hasPreventScroll) {
// Wait for the next frame before setting initial focus so the dialog is technically visible
requestAnimationFrame(() => {
- const slInitialFocus = this.emit('sl-initial-focus');
+ const slInitialFocus = this.slInitialFocus.emit();
if (!slInitialFocus.defaultPrevented) {
this.panel.focus({ preventScroll: true });
}
});
} else {
// Once Safari supports { preventScroll: true } we can remove this nasty little hack, but until then we need to
- // wait for the transition to complete before setting focus, otherwise the panel may render in a buggy way its
- // out of view initially.
+ // wait for the transition to complete before setting focus, otherwise the panel may render in a buggy way
+ // that's out of view initially.
//
// Fiddle: https://jsfiddle.net/g6buoafq/1/
// Safari: https://bugs.webkit.org/show_bug.cgi?id=178583
@@ -123,7 +140,7 @@ export default class SlDialog extends Shoemaker {
this.dialog.addEventListener(
'transitionend',
() => {
- const slInitialFocus = this.emit('sl-initial-focus');
+ const slInitialFocus = this.slInitialFocus.emit();
if (!slInitialFocus.defaultPrevented) {
this.panel.focus();
}
@@ -140,7 +157,7 @@ export default class SlDialog extends Shoemaker {
return;
}
- const slHide = this.emit('sl-hide');
+ const slHide = this.slHide.emit();
if (slHide.defaultPrevented) {
this.open = true;
return;
@@ -164,8 +181,7 @@ export default class SlDialog extends Shoemaker {
}
handleOverlayClick() {
- const slOverlayDismiss = this.emit('sl-overlay-dismiss');
-
+ const slOverlayDismiss = this.slOverlayDismiss.emit();
if (!slOverlayDismiss.defaultPrevented) {
this.hide();
}
@@ -183,7 +199,7 @@ export default class SlDialog extends Shoemaker {
this.isVisible = this.open;
this.willShow = false;
this.willHide = false;
- this.open ? this.emit('sl-after-show') : this.emit('sl-after-hide');
+ this.open ? this.slAfterShow.emit() : this.slAfterHide.emit();
}
}
@@ -194,7 +210,6 @@ export default class SlDialog extends Shoemaker {
render() {
return html`
diff --git a/src/components/drawer/drawer.ts b/src/components/drawer/drawer.ts
index 3f4e8b856..369f7b45b 100644
--- a/src/components/drawer/drawer.ts
+++ b/src/components/drawer/drawer.ts
@@ -1,4 +1,6 @@
-import { classMap, html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, internalProperty, property, query, unsafeCSS } from 'lit-element';
+import { classMap } from 'lit-html/directives/class-map';
+import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./drawer.scss';
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
import { hasSlot } from '../../internal/slot';
@@ -27,56 +29,67 @@ let id = 0;
* @part close-button - The close button.
* @part body - The drawer body.
* @part footer - The drawer footer.
- *
- * @emit sl-show - Emitted when the drawer opens. Calling `event.preventDefault()` will prevent it from being opened.
- * @emit sl-after-show - Emitted after the drawer opens and all transitions are complete.
- * @emit sl-hide - Emitted when the drawer closes. Calling `event.preventDefault()` will prevent it from being closed.
- * @emit sl-after-hide - Emitted after the drawer closes and all transitions are complete.
- * @emit sl-initial-focus - Emitted when the drawer opens and the panel gains focus. Calling `event.preventDefault()`
- * will prevent focus and allow you to set it on a different element in the drawer, such as an input or button.
- * @emit sl-overlay-dismiss - Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the
- * drawer from closing.
*/
-export default class SlDrawer extends Shoemaker {
- static tag = 'sl-drawer';
- static props = ['hasFooter', 'isVisible', 'open', 'label', 'placement', 'contained', 'noHeader'];
- static reflect = ['open'];
- static styles = styles;
+@customElement('sl-drawer')
+export class SlDrawer extends LitElement {
+ static styles = unsafeCSS(styles);
+
+ @query('.drawer') drawer: HTMLElement;
+ @query('.drawer__panel') panel: HTMLElement;
private componentId = `drawer-${++id}`;
- private drawer: HTMLElement;
- private hasFooter = false;
- private isVisible = false;
private modal: Modal;
- private panel: HTMLElement;
private willShow = false;
private willHide = false;
+ @internalProperty() private hasFooter = false;
+ @internalProperty() private isVisible = false;
+
/** Indicates whether or not the drawer is open. You can use this in lieu of the show/hide methods. */
- open = false;
+ @property({ type: Boolean, reflect: true }) open = false;
/**
* The drawer's label as displayed in the header. You should always include a relevant label even when using
* `no-header`, as it is required for proper accessibility.
*/
- label = '';
+ @property({ reflect: true }) label = '';
/** The direction from which the drawer will open. */
- placement: 'top' | 'right' | 'bottom' | 'left' = 'right';
+ @property({ reflect: true }) placement: 'top' | 'right' | 'bottom' | 'left' = 'right';
/**
* By default, the drawer slides out of its containing block (usually the viewport). To make the drawer slide out of
* its parent element, set this prop and add `position: relative` to the parent.
*/
- contained = false;
+ @property({ type: Boolean, reflect: true }) contained = false;
/**
* Removes the header. This will also remove the default close button, so please ensure you provide an easy,
* accessible way for users to dismiss the drawer.
*/
- noHeader = false;
+ @property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
+
+ /** Emitted when the drawer opens. Calling `event.preventDefault()` will prevent it from being opened. */
+ @event('sl-show') slShow: EventEmitter;
+
+ /** Emitted after the drawer opens and all transitions are complete. */
+ @event('sl-after-show') slAfterShow: EventEmitter;
+
+ /** Emitted when the drawer closes. Calling `event.preventDefault()` will prevent it from being closed. */
+ @event('sl-hide') slHide: EventEmitter;
+
+ /** Emitted after the drawer closes and all transitions are complete. */
+ @event('sl-after-hide') slAfterHide: EventEmitter;
+
+ /** Emitted when the drawer opens and the panel gains focus. Calling `event.preventDefault()` will prevent focus and allow you to set it on a different element in the drawer, such as an input or button. */
+ @event('sl-initial-focus') slInitialFocus: EventEmitter;
+
+ /** Emitted when the overlay is clicked. Calling `event.preventDefault()` will prevent the drawer from closing. */
+ @event('sl-overlay-dismiss') slOverlayDismiss: EventEmitter;
+
+ connectedCallback() {
+ super.connectedCallback();
- onConnect() {
this.modal = new Modal(this, {
onfocusOut: () => (this.contained ? null : this.panel.focus())
});
@@ -89,7 +102,8 @@ export default class SlDrawer extends Shoemaker {
}
}
- onDisconnect() {
+ disconnectedCallback() {
+ super.disconnectedCallback();
unlockBodyScrolling(this);
}
@@ -99,7 +113,7 @@ export default class SlDrawer extends Shoemaker {
return;
}
- const slShow = this.emit('sl-show');
+ const slShow = this.slShow.emit();
if (slShow.defaultPrevented) {
this.open = false;
return;
@@ -119,7 +133,7 @@ export default class SlDrawer extends Shoemaker {
if (hasPreventScroll) {
// Wait for the next frame before setting initial focus so the drawer is technically visible
requestAnimationFrame(() => {
- const slInitialFocus = this.emit('sl-initial-focus');
+ const slInitialFocus = this.slInitialFocus.emit();
if (!slInitialFocus.defaultPrevented) {
this.panel.focus({ preventScroll: true });
}
@@ -135,7 +149,7 @@ export default class SlDrawer extends Shoemaker {
this.drawer.addEventListener(
'transitionend',
() => {
- const slInitialFocus = this.emit('sl-initial-focus');
+ const slInitialFocus = this.slInitialFocus.emit();
if (!slInitialFocus.defaultPrevented) {
this.panel.focus();
}
@@ -152,7 +166,7 @@ export default class SlDrawer extends Shoemaker {
return;
}
- const slHide = this.emit('sl-hide');
+ const slHide = this.slHide.emit();
if (slHide.defaultPrevented) {
this.open = true;
return;
@@ -176,8 +190,7 @@ export default class SlDrawer extends Shoemaker {
}
handleOverlayClick() {
- const slOverlayDismiss = this.emit('sl-overlay-dismiss');
-
+ const slOverlayDismiss = this.slOverlayDismiss.emit();
if (!slOverlayDismiss.defaultPrevented) {
this.hide();
}
@@ -195,14 +208,13 @@ export default class SlDrawer extends Shoemaker {
this.isVisible = this.open;
this.willShow = false;
this.willHide = false;
- this.open ? this.emit('sl-after-show') : this.emit('sl-after-hide');
+ this.open ? this.slAfterShow.emit() : this.slAfterHide.emit();
}
}
render() {
return html`
(this.panel = el)}
part="panel"
class="dropdown__panel"
role="menu"
diff --git a/src/components/form/form.ts b/src/components/form/form.ts
index 211d880e4..2e71962e5 100644
--- a/src/components/form/form.ts
+++ b/src/components/form/form.ts
@@ -1,4 +1,5 @@
-import { html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, property, query, unsafeCSS } from 'lit-element';
+import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./form.scss';
import {
SlButton,
@@ -26,25 +27,30 @@ interface FormControl {
* @slot - The form's content.
*
* @part base - The component's base wrapper.
- *
- * @emit sl-submit - Emitted when the form is submitted. This event will not be emitted if any form control inside of
- * it is in an invalid state, unless the form has the `novalidate` attribute. Note that there is never a need to prevent
- * this event, since it doen't send a GET or POST request like native forms. To "prevent" submission, use a conditional
- * around the XHR request you use to submit the form's data with. Event details will contain:
- * `{ formData: FormData; formControls: HTMLElement[] }`
*/
-export default class SlForm extends Shoemaker {
- static tag = 'sl-form';
- static props = ['novalidate'];
- static styles = styles;
+@customElement('sl-form')
+export class SlForm extends LitElement {
+ static styles = unsafeCSS(styles);
+
+ @query('.form') form: HTMLElement;
- private form: HTMLElement;
private formControls: FormControl[];
/** Prevent the form from validating inputs before submitting. */
- novalidate = false;
+ @property({ type: Boolean, reflect: true }) novalidate = false;
+
+ /**
+ * @emit sl-submit - Emitted when the form is submitted. This event will not be emitted if any form control inside of
+ * it is in an invalid state, unless the form has the `novalidate` attribute. Note that there is never a need to prevent
+ * this event, since it doen't send a GET or POST request like native forms. To "prevent" submission, use a conditional
+ * around the XHR request you use to submit the form's data with. Event details will contain:
+ * `{ formData: FormData; formControls: HTMLElement[] }`
+ */
+ @event('sl-submit') slSubmit: EventEmitter<{ formData: FormData; formControls: HTMLElement[] }>;
+
+ connectedCallback() {
+ super.connectedCallback();
- onConnect() {
this.formControls = [
{
tag: 'button',
@@ -183,9 +189,6 @@ export default class SlForm extends Shoemaker {
el.name && !el.disabled ? formData.append(el.name, el.value) : null
}
];
-
- this.handleClick = this.handleClick.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
}
/** Serializes all form controls elements and returns a `FormData` object. */
@@ -230,7 +233,7 @@ export default class SlForm extends Shoemaker {
}
}
- this.emit('sl-submit', { detail: { formData, formControls } });
+ this.slSubmit.emit({ detail: { formData, formControls } });
return true;
}
@@ -271,15 +274,8 @@ export default class SlForm extends Shoemaker {
render() {
return html`
-
`;
diff --git a/src/components/tab-panel/tab-panel.ts b/src/components/tab-panel/tab-panel.ts
index e20bbf519..fb2301562 100644
--- a/src/components/tab-panel/tab-panel.ts
+++ b/src/components/tab-panel/tab-panel.ts
@@ -1,4 +1,4 @@
-import { html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, property, unsafeCSS } from 'lit-element';
import styles from 'sass:./tab-panel.scss';
let id = 0;
@@ -11,21 +11,19 @@ let id = 0;
*
* @part base - The component's base wrapper.
*/
-export default class SlTabPanel extends Shoemaker {
- static tag = 'sl-tab-panel';
- static props = ['name', 'active'];
- static reflect = ['name', 'active'];
- static styles = styles;
+@customElement('sl-tab-panel')
+export class SlTabPanel extends LitElement {
+ static styles = unsafeCSS(styles);
private componentId = `tab-panel-${++id}`;
/** The tab panel's name. */
- name = '';
+ @property() name = '';
/** When true, the tab panel will be shown. */
- active = false;
+ @property({ type: Boolean, reflect: true }) active = false;
- onReady() {
+ firstUpdated() {
this.id = this.id || this.componentId;
}
@@ -40,7 +38,7 @@ export default class SlTabPanel extends Shoemaker {
aria-selected=${this.active ? 'true' : 'false'}
aria-hidden=${this.active ? 'false' : 'true'}
>
-
+
`;
}
diff --git a/src/components/tab/tab.ts b/src/components/tab/tab.ts
index a6c4392a4..31f171dbb 100644
--- a/src/components/tab/tab.ts
+++ b/src/components/tab/tab.ts
@@ -1,4 +1,6 @@
-import { classMap, html, Shoemaker } from '@shoelace-style/shoemaker';
+import { LitElement, customElement, html, property, query, unsafeCSS } from 'lit-element';
+import { classMap } from 'lit-html/directives/class-map';
+import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./tab.scss';
let id = 0;
@@ -13,29 +15,29 @@ let id = 0;
*
* @part base - The component's base wrapper.
* @part close-button - The close button, which is the icon button's base wrapper.
- *
- * @emit sl-close - Emitted when the tab is closable and the close button is activated.
*/
-export default class SlTab extends Shoemaker {
- static tag = 'sl-tab';
- static props = ['panel', 'active', 'closable', 'disabled'];
- static reflect = ['panel', 'active', 'closable', 'disabled'];
- static styles = styles;
+@customElement('sl-tab')
+export class SlTab extends LitElement {
+ static styles = unsafeCSS(styles);
+
+ @query('.tab') tab: HTMLElement;
private componentId = `tab-${++id}`;
- private tab: HTMLElement;
/** The name of the tab panel the tab will control. The panel must be located in the same tab group. */
- panel = '';
+ @property() panel = '';
/** Draws the tab in an active state. */
- active = false;
+ @property({ type: Boolean, reflect: true }) active = false;
/** Makes the tab closable and shows a close icon. */
- closable = false;
+ @property({ type: Boolean, reflect: true }) closable = false;
/** Draws the tab in a disabled state. */
- disabled = false;
+ @property({ type: Boolean, reflect: true }) disabled = false;
+
+ /** Emitted when the tab is closable and the close button is activated. */
+ @event('sl-close') slClose: EventEmitter;
/** Sets focus to the tab. */
setFocus(options?: FocusOptions) {
@@ -48,7 +50,7 @@ export default class SlTab extends Shoemaker {
}
handleCloseClick() {
- this.emit('sl-close');
+ this.slClose.emit();
}
render() {
@@ -58,7 +60,6 @@ export default class SlTab extends Shoemaker {
return html`
(props.onLabelClick ? props.onLabelClick(event) : null)}
+ @click=${(event: MouseEvent) => (props.onLabelClick ? props.onLabelClick(event) : null)}
>
${props.label}
diff --git a/src/shoelace.ts b/src/shoelace.ts
index a3af21886..2f5b845d0 100644
--- a/src/shoelace.ts
+++ b/src/shoelace.ts
@@ -1,46 +1,46 @@
export * from './utilities';
-export { default as SlAlert } from './components/alert/alert';
-export { default as SlAnimation } from './components/animation/animation';
-export { default as SlAvatar } from './components/avatar/avatar';
-export { default as SlBadge } from './components/badge/badge';
-export { default as SlButton } from './components/button/button';
-export { default as SlButtonGroup } from './components/button-group/button-group';
-export { default as SlCard } from './components/card/card';
-export { default as SlCheckbox } from './components/checkbox/checkbox';
-export { default as SlColorPicker } from './components/color-picker/color-picker';
-export { default as SlDetails } from './components/details/details';
-export { default as SlDialog } from './components/dialog/dialog';
-export { default as SlDrawer } from './components/drawer/drawer';
-export { default as SlDropdown } from './components/dropdown/dropdown';
-export { default as SlForm } from './components/form/form';
-export { default as SlFormatBytes } from './components/format-bytes/format-bytes';
-export { default as SlFormatDate } from './components/format-date/format-date';
-export { default as SlFormatNumber } from './components/format-number/format-number';
-export { default as SlIcon } from './components/icon/icon';
-export { default as SlIconButton } from './components/icon-button/icon-button';
-export { default as SlImageComparer } from './components/image-comparer/image-comparer';
-export { default as SlInclude } from './components/include/include';
-export { default as SlInput } from './components/input/input';
-export { default as SlMenu } from './components/menu/menu';
-export { default as SlMenuDivider } from './components/menu-divider/menu-divider';
-export { default as SlMenuItem } from './components/menu-item/menu-item';
-export { default as SlMenuLabel } from './components/menu-label/menu-label';
-export { default as SlProgressBar } from './components/progress-bar/progress-bar';
-export { default as SlProgressRing } from './components/progress-ring/progress-ring';
-export { default as SlRadio } from './components/radio/radio';
-export { default as SlRange } from './components/range/range';
-export { default as SlRating } from './components/rating/rating';
-export { default as SlRelativeTime } from './components/relative-time/relative-time';
-export { default as SlResizeObserver } from './components/resize-observer/resize-observer';
-export { default as SlResponsiveEmbed } from './components/responsive-embed/responsive-embed';
-export { default as SlSelect } from './components/select/select';
-export { default as SlSkeleton } from './components/skeleton/skeleton';
-export { default as SlSpinner } from './components/spinner/spinner';
-export { default as SlSwitch } from './components/switch/switch';
-export { default as SlTab } from './components/tab/tab';
-export { default as SlTabGroup } from './components/tab-group/tab-group';
-export { default as SlTabPanel } from './components/tab-panel/tab-panel';
-export { default as SlTag } from './components/tag/tag';
-export { default as SlTextarea } from './components/textarea/textarea';
-export { default as SlTooltip } from './components/tooltip/tooltip';
+export { SlAlert } from './components/alert/alert';
+export { SlAnimation } from './components/animation/animation';
+export { SlAvatar } from './components/avatar/avatar';
+export { SlBadge } from './components/badge/badge';
+export { SlButton } from './components/button/button';
+export { SlButtonGroup } from './components/button-group/button-group';
+export { SlCard } from './components/card/card';
+export { SlCheckbox } from './components/checkbox/checkbox';
+export { SlColorPicker } from './components/color-picker/color-picker';
+export { SlDetails } from './components/details/details';
+export { SlDialog } from './components/dialog/dialog';
+export { SlDrawer } from './components/drawer/drawer';
+export { SlDropdown } from './components/dropdown/dropdown';
+export { SlForm } from './components/form/form';
+export { SlFormatBytes } from './components/format-bytes/format-bytes';
+export { SlFormatDate } from './components/format-date/format-date';
+export { SlFormatNumber } from './components/format-number/format-number';
+export { SlIcon } from './components/icon/icon';
+export { SlIconButton } from './components/icon-button/icon-button';
+export { SlImageComparer } from './components/image-comparer/image-comparer';
+export { SlInclude } from './components/include/include';
+export { SlInput } from './components/input/input';
+export { SlMenu } from './components/menu/menu';
+export { SlMenuDivider } from './components/menu-divider/menu-divider';
+export { SlMenuItem } from './components/menu-item/menu-item';
+export { SlMenuLabel } from './components/menu-label/menu-label';
+export { SlProgressBar } from './components/progress-bar/progress-bar';
+export { SlProgressRing } from './components/progress-ring/progress-ring';
+export { SlRadio } from './components/radio/radio';
+export { SlRange } from './components/range/range';
+export { SlRating } from './components/rating/rating';
+export { SlRelativeTime } from './components/relative-time/relative-time';
+export { SlResizeObserver } from './components/resize-observer/resize-observer';
+export { SlResponsiveEmbed } from './components/responsive-embed/responsive-embed';
+export { SlSelect } from './components/select/select';
+export { SlSkeleton } from './components/skeleton/skeleton';
+export { SlSpinner } from './components/spinner/spinner';
+export { SlSwitch } from './components/switch/switch';
+export { SlTab } from './components/tab/tab';
+export { SlTabGroup } from './components/tab-group/tab-group';
+export { SlTabPanel } from './components/tab-panel/tab-panel';
+export { SlTag } from './components/tag/tag';
+export { SlTextarea } from './components/textarea/textarea';
+export { SlTooltip } from './components/tooltip/tooltip';
diff --git a/tsconfig.json b/tsconfig.json
index 92c0bf200..695b8f200 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -64,7 +64,7 @@
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
- // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
+ "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */