From d0b9488c1c50ea340963f15fdb25158b4da6942f Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Thu, 24 Jun 2021 18:24:54 -0400 Subject: [PATCH] switch to custom elements manifest analyzer --- README.md | 4 +- custom-elements-manifest.config.js | 74 +++ docs/assets/plugins/metadata/metadata.js | 129 +++-- docs/assets/styles/docs-dark.css | 4 - docs/assets/styles/docs.css | 11 +- docs/getting-started/overview.md | 3 +- docs/resources/changelog.md | 9 + docs/resources/contributing.md | 6 +- package-lock.json | 479 ++++-------------- package.json | 5 +- scripts/build.js | 20 +- scripts/make-icons.js | 10 +- scripts/make-metadata.js | 291 +---------- scripts/make-vscode-data.js | 86 ++++ .../plop/templates/component/component.hbs | 20 +- src/components/alert/alert.ts | 47 +- src/components/animation/animation.ts | 26 +- src/components/avatar/avatar.ts | 12 +- src/components/badge/badge.ts | 4 +- src/components/button-group/button-group.ts | 4 +- src/components/button/button.ts | 39 +- src/components/card/card.ts | 26 +- src/components/checkbox/checkbox.ts | 34 +- src/components/color-picker/color-picker.ts | 46 +- src/components/details/details.ts | 46 +- src/components/dialog/dialog.ts | 92 ++-- src/components/drawer/drawer.ts | 101 ++-- src/components/dropdown/dropdown.ts | 42 +- src/components/form/form.ts | 21 +- src/components/icon-button/icon-button.ts | 2 +- src/components/icon/icon.ts | 22 +- .../image-comparer/image-comparer.ts | 29 +- src/components/include/include.ts | 18 +- src/components/input/input.ts | 76 ++- src/components/menu-divider/menu-divider.ts | 2 +- src/components/menu-item/menu-item.ts | 16 +- src/components/menu-label/menu-label.ts | 4 +- src/components/menu/menu.ts | 15 +- src/components/progress-bar/progress-bar.ts | 16 +- src/components/progress-ring/progress-ring.ts | 12 +- src/components/qr-code/qr-code.ts | 4 +- src/components/radio-group/radio-group.ts | 8 +- src/components/radio/radio.ts | 32 +- src/components/range/range.ts | 32 +- src/components/rating/rating.ts | 20 +- src/components/relative-time/relative-time.ts | 2 +- .../resize-observer/resize-observer.ts | 9 +- .../responsive-media/responsive-media.ts | 2 +- src/components/select/select.ts | 54 +- src/components/skeleton/skeleton.ts | 10 +- src/components/spinner/spinner.ts | 8 +- src/components/switch/switch.ts | 38 +- src/components/tab-group/tab-group.ts | 34 +- src/components/tab-panel/tab-panel.ts | 4 +- src/components/tab/tab.ts | 17 +- src/components/tag/tag.ts | 17 +- src/components/textarea/textarea.ts | 48 +- src/components/tooltip/tooltip.ts | 45 +- src/internal/decorators.ts | 103 ---- src/internal/event.ts | 20 + src/internal/watch.ts | 40 ++ 61 files changed, 980 insertions(+), 1470 deletions(-) create mode 100644 custom-elements-manifest.config.js create mode 100644 scripts/make-vscode-data.js delete mode 100644 src/internal/decorators.ts create mode 100644 src/internal/watch.ts diff --git a/README.md b/README.md index ecd87abd..95f64cd9 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,7 @@ Once you've cloned the repo, run the following command. npm start ``` -This will spin up the Shoelace dev server. After the initial build, a browser will open automatically. - -There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically. The exception is component metadata used by the docs, which is generated by TypeDoc. This tool takes a few seconds to run so, to prevent long reload delays, it only runs once at startup. +This will spin up the Shoelace dev server. After the initial build, a browser will open automatically. There is currently no hot module reloading (HMR), as browser's don't provide a way to reregister custom elements, but most changes to the source will reload the browser automatically. The documentation is powered by Docsify, which uses raw markdown files to generate pages. As such, no static files are built for the docs. diff --git a/custom-elements-manifest.config.js b/custom-elements-manifest.config.js new file mode 100644 index 00000000..692e1e42 --- /dev/null +++ b/custom-elements-manifest.config.js @@ -0,0 +1,74 @@ +import fs from 'fs'; +import commentParser from 'comment-parser'; + +const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8')); +const { name, description, version, author, homepage, license } = packageData; + +export default { + globs: ['src/components/**/*.ts'], + exclude: ['**/*.test.ts'], + dev: true, + watch: true, + plugins: [ + // Append package data + { + name: 'shoelace-package-data', + packageLinkPhase({ customElementsManifest, context }) { + customElementsManifest.package = { name, description, version, author, homepage, license }; + } + }, + // Parse custom jsDoc tags + { + name: 'shoelace-custom-tags', + analyzePhase({ ts, node, moduleDoc, context }) { + switch (node.kind) { + case ts.SyntaxKind.ClassDeclaration: + const hasDefaultModifier = node?.modifiers?.some(mod => ts.SyntaxKind.DefaultKeyword === mod.kind); + const className = hasDefaultModifier ? 'default' : node?.name?.getText(); + const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className); + const customTags = ['animation', 'dependency', 'since', 'status']; + let customComments = '/**'; + + node.jsDoc?.forEach(jsDoc => { + jsDoc?.tags?.forEach(tag => { + const tagName = tag.tagName.getText(); + + if (customTags.includes(tagName)) { + customComments += `\n * @${tagName} ${tag.comment}`; + } + }); + }); + + const parsed = commentParser.parse(customComments + '\n */'); + parsed[0].tags?.map(t => { + switch (t.tag) { + case 'since': + case 'status': + classDoc[t.tag] = t.name; + break; + + case 'dependency': + if (!Array.isArray(classDoc['dependencies'])) { + classDoc['dependencies'] = []; + } + classDoc['dependencies'].push(t.name); + break; + + // All other tags + default: + if (!Array.isArray(classDoc[t.tag])) { + classDoc[t.tag] = []; + } + + classDoc[t.tag].push({ + name: t.name, + description: t.description, + type: t.type || undefined + }); + } + }); + } + } + } + ] +}; diff --git a/docs/assets/plugins/metadata/metadata.js b/docs/assets/plugins/metadata/metadata.js index cdf7c375..63c61f63 100644 --- a/docs/assets/plugins/metadata/metadata.js +++ b/docs/assets/plugins/metadata/metadata.js @@ -7,6 +7,7 @@ Property + Attribute Description Type Default @@ -17,23 +18,11 @@ .map(prop => { return ` - - ${escapeHtml(prop.name)} - ${ - prop.attribute && prop.name !== prop.attribute - ? ` -
- - - ${escapeHtml(prop.attribute)} - - ` - : '' - } - + ${escapeHtml(prop.name)} + ${prop.attribute ? `${escapeHtml(prop.attribute)}` : '-'} ${escapeHtml(prop.description)} - ${escapeHtml(prop.type)} - ${escapeHtml(prop.defaultValue)} + ${prop.type?.text ? `${escapeHtml(prop.type?.text || '')}` : '-'} + ${prop.default ? `${escapeHtml(prop.default)}` : '-'} `; }) @@ -51,7 +40,7 @@ Event Description - Details + Event Detail @@ -59,9 +48,9 @@ .map( event => ` - ${escapeHtml(event.name)} + ${escapeHtml(event.name)} ${escapeHtml(event.description)} - ${escapeHtml(event.details)} + ${event.type?.text ? `${escapeHtml(event.type?.text)}` : '-'} ` ) @@ -87,19 +76,17 @@ .map( method => ` - ${escapeHtml(method.name)} + ${escapeHtml(method.name)} ${escapeHtml(method.description)} ${ - method.params.length + method.parameters?.length ? ` - ${escapeHtml( - method.params - .map(param => `${param.name}${param.isOptional ? '?' : ''}: ${param.type}`) - .join(', ') + ${escapeHtml( + method.parameters.map(param => `${param.name}: ${param.type.text}`).join(', ') )} ` - : '' + : '-' } @@ -126,7 +113,7 @@ .map( slot => ` - ${slot.name ? escapeHtml(slot.name) : '(default)'} + ${escapeHtml(slot.name)} ${escapeHtml(slot.description)} ` @@ -178,7 +165,7 @@ .map( part => ` - ${escapeHtml(part.name)} + ${escapeHtml(part.name)} ${escapeHtml(part.description)} ` @@ -204,7 +191,7 @@ .map( animation => ` - ${escapeHtml(animation.name)} + ${escapeHtml(animation.name)} ${escapeHtml(animation.description)} ` @@ -227,7 +214,7 @@ return []; } - component.dependencies.map(tag => { + component.dependencies?.map(tag => { if (!dependencies.includes(tag)) { dependencies.push(tag); } @@ -255,6 +242,32 @@ .replace(/`(.*?)`/g, '$1'); } + function getAllComponents(metadata) { + const allComponents = []; + + metadata.modules.map(module => { + module.exports.find(ex => { + if (ex.kind === 'custom-element-definition') { + allComponents.push(getComponent(metadata, ex.name)); + } + }); + }); + + return allComponents; + } + + function getComponent(metadata, tagName) { + const module = metadata.modules.find(module => { + return module.exports.find(ex => { + return ex.kind === 'custom-element-definition' && ex.name === tagName; + }); + }); + const component = module?.declarations.find(dec => dec.name === 'default'); + const tag = module.exports.filter(ex => ex.kind === 'custom-element-definition' && ex.name === tagName)[0]?.name; + + return Object.assign({ tag }, component); + } + function getMetadata() { return new Promise((resolve, reject) => { // Simple caching to prevent multiple XHR requests @@ -262,7 +275,7 @@ return resolve(metadataStore); } - fetch('/dist/metadata.json') + fetch('/dist/custom-elements.json') .then(res => res.json()) .then(data => { metadataStore = data; @@ -284,7 +297,7 @@ // Add version const version = document.createElement('div'); version.classList.add('sidebar-version'); - version.textContent = metadata.version; + version.textContent = metadata.package.version; target.appendChild(version); // Add repo buttons @@ -309,11 +322,11 @@ const metadata = await getMetadata(); // Replace %VERSION% placeholders - content = content.replace(/%VERSION%/g, metadata.version); + content = content.replace(/%VERSION%/g, metadata.package.version); // Handle [component-header] tags content = content.replace(/\[component-header:([a-z-]+)\]/g, (match, tag) => { - const component = metadata.components.filter(data => data.tag === tag)[0]; + const component = getComponent(metadata, tag); let result = ''; if (!component) { @@ -350,7 +363,7 @@ // Handle [component-metadata] tags content = content.replace(/\[component-metadata:([a-z-]+)\]/g, (match, tag) => { - const component = metadata.components.filter(data => data.tag === tag)[0]; + const component = getComponent(metadata, tag); let result = ''; if (!component) { @@ -358,49 +371,67 @@ return next(content); } - if (component.props.length) { + // Remove members that don't have a description + const members = component.members?.filter(member => member.description); + const props = members?.filter(prop => { + // Look for a corresponding attribute + const attribute = component.attributes?.find(attr => attr.fieldName === prop.name); + if (attribute) { + prop.attribute = attribute.name; + } + + return prop.kind === 'field' && prop.privacy !== 'private'; + }); + + // .map(prop => { + // const attribute = component.attributes?.find(attr => attr.fieldName === prop); + // prop.attribute = attribute; + // }); + const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private'); + + if (props?.length) { result += ` ## Properties - ${createPropsTable(component.props)} + ${createPropsTable(props)} `; } - if (component.events.length) { + if (component.events?.length) { result += ` ## Events ${createEventsTable(component.events)} `; } - if (component.methods.length) { + if (methods?.length) { result += ` - ## Methods - ${createMethodsTable(component.methods)} + ## Methods + ${createMethodsTable(methods)} `; } - if (component.slots.length) { + if (component.slots?.length) { result += ` ## Slots ${createSlotsTable(component.slots)} `; } - if (component.cssCustomProperties.length) { + if (component.cssProperties?.length) { result += ` ## CSS Custom Properties - ${createCustomPropertiesTable(component.cssCustomProperties)} + ${createCustomPropertiesTable(component.cssProperties)} `; } - if (component.parts.length) { + if (component.cssParts?.length) { result += ` ## CSS Parts - ${createPartsTable(component.parts)} + ${createPartsTable(component.cssParts)} `; } - if (component.animations.length) { + if (component.animations?.length) { result += ` ## Animations ${createAnimationsTable(component.animations)} @@ -409,14 +440,14 @@ `; } - if (component.dependencies.length) { + if (component.dependencies?.length) { result += ` ## Dependencies 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)} + ${createDependenciesList(component.tag, getAllComponents(metadata))} `; } diff --git a/docs/assets/styles/docs-dark.css b/docs/assets/styles/docs-dark.css index cf570b47..d280e22c 100644 --- a/docs/assets/styles/docs-dark.css +++ b/docs/assets/styles/docs-dark.css @@ -85,10 +85,6 @@ border-bottom-color: var(--sl-color-gray-800); } -.sl-theme-dark .markdown-section table .attribute-tooltip { - border-bottom-color: var(--sl-color-gray-700); -} - /* Tips & warnings */ .sl-theme-dark .markdown-section p.tip, .sl-theme-dark .markdown-section p.warn { diff --git a/docs/assets/styles/docs.css b/docs/assets/styles/docs.css index e8c81115..5ebc2ca6 100644 --- a/docs/assets/styles/docs.css +++ b/docs/assets/styles/docs.css @@ -433,6 +433,7 @@ strong { /* Tables */ .markdown-section table { + display: table; margin-bottom: 1.5rem; } @@ -446,7 +447,7 @@ strong { .markdown-section th { border: none; - font-weight: inherit; + font-weight: var(--sl-font-weight-semibold); text-align: left; } @@ -457,16 +458,10 @@ strong { border-right: none; } -.markdown-section td code { +.markdown-section table .nowrap { white-space: nowrap; } -.markdown-section table .attribute-tooltip { - background: none; - border-bottom: dashed 1px var(--sl-color-gray-200); - cursor: help; -} - /* Iframes */ .markdown-section iframe { border: none; diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md index 08345bc5..2629c820 100644 --- a/docs/getting-started/overview.md +++ b/docs/getting-started/overview.md @@ -113,9 +113,10 @@ Designing, developing, and supporting this library requires a lot of time, effor ## Attribution -Special thanks to the following projects and individuals that helped make Shoelace possible. +Special thanks to the following projects and individuals that help make Shoelace possible. - Components are built with [LitElement](https://lit-element.polymer-project.org/) +- Component metadata is generated by the [Custom Elements Manifest Analyzer](https://github.com/open-wc/custom-elements-manifest) - 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/docs/resources/changelog.md b/docs/resources/changelog.md index 4c6b8e0e..ee0cd995 100644 --- a/docs/resources/changelog.md +++ b/docs/resources/changelog.md @@ -8,10 +8,19 @@ _During the beta period, these restrictions may be relaxed in the event of a mis ## Next +This release changes the way component metadata is generated. Previously, TypeDoc was used to generate a very large file with type data for all components. The data was then parsed and converted to an easier-to-consume file called `metadata.json`. Alas, TypeDoc is expensive to run and the metadata format wasn't standard. + +Thanks to an amazing effort by [Pascal Schilp](https://twitter.com/passle_), the world has a simpler, faster way to gather metadata using the [Custom Elements Manifest Analyzer](https://github.com/open-wc/custom-elements-manifest). Not only is this tool faster, but the data follows the evolving `custom-elements.json` format. This is exciting because a standard format for custom elements opens the door for many potential uses, including documentation generation, framework adapters, IDE integrations, third-party uses, and more. [Check out Pascal's great article](https://dev.to/open-wc/introducing-custom-elements-manifest-gkk) for more info about `custom-elements.json` and the new analyzer. + +The docs have been updated to use `custom-elements.json`. If you're relying on the old `metadata.json` file for any purpose, this will be a breaking change for you. + - 🚨 BREAKING: removed the `sl-overlay-click` event from `sl-dialog` and `sl-drawer` (use `sl-request-close` instead) [#471](https://github.com/shoelace-style/shoelace/discussions/471) +- 🚨 BREAKING: removed `metadata.json` (use `custom-elements.json` instead) +- Added `custom-elements.json` for component metadata - Added `sl-request-close` event to `sl-dialog` and `sl-drawer` - Added `dialog.denyClose` and `drawer.denyClose` animations - Fixed a bug in `sl-color-picker` where setting `value` immediately wouldn't trigger an update +- Updated the docs to use the new `custom-elements.json` for component metadata ## 2.0.0-beta.44 diff --git a/docs/resources/contributing.md b/docs/resources/contributing.md index 0b6b03a0..434fa116 100644 --- a/docs/resources/contributing.md +++ b/docs/resources/contributing.md @@ -104,7 +104,7 @@ This will generate a source file, a stylesheet, and a docs page for you. When yo Component development occurs _within_ the local docs site. I've found that offering common variations _in the docs_ is more beneficial for users than segmenting demos and code examples into separate tools such as Storybook. This encourages more thorough documentation, streamlines development for maintainers, and simplifies how the project is built. It also reduces installation and startup times significantly. -There is currently no hot module reloading (HMR), as browsers don't provide a way to unregister custom elements. However, most changes to the source will reload the browser automatically. The exception is component metadata, which is generated by TypeDoc. TypeDoc takes a few seconds to run so, to prevent long reload delays, it only runs once at startup. +There is currently no hot module reloading (HMR), as browsers don't provide a way to unregister custom elements. However, most changes to the source will reload the browser automatically. For more information about running and building the project locally, refer to `README.md` in the project's root. @@ -224,8 +224,8 @@ Then use the following syntax for comments so they appear in the generated docs. ```js /** - * @customProperty --color: The component's text color. - * @customProperty --background-color: The component's background color. + * @cssproperty --color: The component's text color. + * @cssproperty --background-color: The component's background color. */ @customElement('sl-example') export default class SlExample { diff --git a/package-lock.json b/package-lock.json index c1978efb..b8552b67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "2.0.0-beta.44", "license": "MIT", "dependencies": { + "@custom-elements-manifest/analyzer": "^0.3.4", "@popperjs/core": "^2.7.0", "@shoelace-style/animations": "^1.1.0", "color": "^3.1.3", @@ -28,6 +29,7 @@ "browser-sync": "^2.26.14", "chalk": "^4.1.0", "command-line-args": "^5.1.1", + "comment-parser": "^1.1.5", "concurrently": "^5.3.0", "del": "^6.0.0", "download": "^8.0.0", @@ -43,7 +45,6 @@ "sass": "^1.32.7", "sinon": "^11.1.1", "tslib": "^2.2.0", - "typedoc": "^0.20.28", "typescript": "^4.2.4", "wait-on": "^5.2.1" }, @@ -138,6 +139,25 @@ "node": ">=6.9.0" } }, + "node_modules/@custom-elements-manifest/analyzer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@custom-elements-manifest/analyzer/-/analyzer-0.3.4.tgz", + "integrity": "sha512-wgLe4p2PYsDWxgrustrr4dUcg0qkfTjydo9lQJ5eAFX+Xz0TSaNnfOx87UZ/qdbYlDHIw8fm2pXxx0sgtJ5ApQ==", + "dependencies": { + "@web/config-loader": "^0.1.3", + "chokidar": "^3.5.2", + "command-line-args": "^5.1.1", + "comment-parser": "^1.1.5", + "custom-elements-manifest": "^1.0.0", + "debounce": "^1.2.1", + "globby": "^11.0.1", + "typescript": "^4.3.2" + }, + "bin": { + "cem": "index.js", + "custom-elements-manifest": "index.js" + } + }, "node_modules/@hapi/hoek": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", @@ -821,7 +841,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.1.3.tgz", "integrity": "sha512-XVKH79pk4d3EHRhofete8eAnqto1e8mCRAqPV00KLNFzCWSe8sWmLnqKCqkPNARC6nksMaGrATnA5sPDRllMpQ==", - "dev": true, "dependencies": { "semver": "^7.3.4" }, @@ -833,7 +852,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -845,7 +863,6 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -856,12 +873,6 @@ "node": ">=10" } }, - "node_modules/@web/config-loader/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@web/dev-server": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/@web/dev-server/-/dev-server-0.1.17.tgz", @@ -954,12 +965,6 @@ "node": ">=10" } }, - "node_modules/@web/dev-server-core/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@web/dev-server-esbuild": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@web/dev-server-esbuild/-/dev-server-esbuild-0.2.12.tgz", @@ -1323,10 +1328,9 @@ "dev": true }, "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1396,7 +1400,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, "engines": { "node": ">=6" } @@ -1632,7 +1635,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -2085,24 +2087,23 @@ } }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/chownr": { @@ -2420,20 +2421,10 @@ "simple-swizzle": "^0.2.2" } }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/command-line-args": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", - "dev": true, "dependencies": { "array-back": "^3.0.1", "find-replace": "^3.0.0", @@ -2530,6 +2521,14 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/comment-parser": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.5.tgz", + "integrity": "sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -2989,6 +2988,11 @@ "node": ">=8.0.0" } }, + "node_modules/custom-elements-manifest": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/custom-elements-manifest/-/custom-elements-manifest-1.0.0.tgz", + "integrity": "sha512-j59k0ExGCKA8T6Mzaq+7axc+KVHwpEphEERU7VZ99260npu/p/9kd+Db+I3cGKxHkM5y6q5gnlXn00mzRQkX2A==" + }, "node_modules/date-fns": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.17.0.tgz", @@ -3001,8 +3005,7 @@ "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "dev": true + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" }, "node_modules/debug": { "version": "3.1.0", @@ -4244,7 +4247,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, "dependencies": { "array-back": "^3.0.1" }, @@ -4574,7 +4576,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "optional": true, "os": [ "darwin" @@ -5353,7 +5354,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6281,8 +6281,7 @@ "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, "node_modules/lodash.get": { "version": "4.4.2", @@ -6465,21 +6464,6 @@ "node": ">=0.10.0" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -6535,18 +6519,6 @@ "node": ">=0.10.0" } }, - "node_modules/marked": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", - "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", - "dev": true, - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">= 8.16.2" - } - }, "node_modules/marky": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.2.tgz", @@ -7190,7 +7162,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7459,15 +7430,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "dependencies": { - "lru-cache": "^5.1.1" - } - }, "node_modules/only": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", @@ -8460,10 +8422,9 @@ "dev": true }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dependencies": { "picomatch": "^2.2.1" }, @@ -9150,33 +9111,6 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, - "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shiki": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.3.tgz", - "integrity": "sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA==", - "dev": true, - "dependencies": { - "onigasm": "^2.2.5", - "vscode-textmate": "^5.2.0" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -10307,82 +10241,10 @@ "node": ">= 0.6" } }, - "node_modules/typedoc": { - "version": "0.20.36", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.20.36.tgz", - "integrity": "sha512-qFU+DWMV/hifQ9ZAlTjdFO9wbUIHuUBpNXzv68ZyURAP9pInjZiO4+jCPeAzHVcaBCHER9WL/+YzzTt6ZlN/Nw==", - "dev": true, - "dependencies": { - "colors": "^1.4.0", - "fs-extra": "^9.1.0", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "lunr": "^2.3.9", - "marked": "^2.0.3", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shelljs": "^0.8.4", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 10.8.0" - }, - "peerDependencies": { - "typescript": "3.9.x || 4.0.x || 4.1.x || 4.2.x" - } - }, - "node_modules/typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/typedoc/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typedoc/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "node_modules/typedoc/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", - "dev": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", + "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10395,7 +10257,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true, "engines": { "node": ">=8" } @@ -10694,12 +10555,6 @@ "node": ">= 0.8" } }, - "node_modules/vscode-textmate": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.4.0.tgz", - "integrity": "sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w==", - "dev": true - }, "node_modules/wait-on": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.2.1.tgz", @@ -11009,10 +10864,9 @@ } }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "1.10.0", @@ -11439,6 +11293,21 @@ "regenerator-runtime": "^0.13.4" } }, + "@custom-elements-manifest/analyzer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@custom-elements-manifest/analyzer/-/analyzer-0.3.4.tgz", + "integrity": "sha512-wgLe4p2PYsDWxgrustrr4dUcg0qkfTjydo9lQJ5eAFX+Xz0TSaNnfOx87UZ/qdbYlDHIw8fm2pXxx0sgtJ5ApQ==", + "requires": { + "@web/config-loader": "^0.1.3", + "chokidar": "^3.5.2", + "command-line-args": "^5.1.1", + "comment-parser": "^1.1.5", + "custom-elements-manifest": "^1.0.0", + "debounce": "^1.2.1", + "globby": "^11.0.1", + "typescript": "^4.3.2" + } + }, "@hapi/hoek": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", @@ -12102,7 +11971,6 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@web/config-loader/-/config-loader-0.1.3.tgz", "integrity": "sha512-XVKH79pk4d3EHRhofete8eAnqto1e8mCRAqPV00KLNFzCWSe8sWmLnqKCqkPNARC6nksMaGrATnA5sPDRllMpQ==", - "dev": true, "requires": { "semver": "^7.3.4" }, @@ -12111,7 +11979,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -12120,16 +11987,9 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, @@ -12210,12 +12070,6 @@ "requires": { "yallist": "^4.0.0" } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, @@ -12492,10 +12346,9 @@ "dev": true }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -12548,8 +12401,7 @@ "array-back": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==" }, "array-differ": { "version": "1.0.0", @@ -12726,8 +12578,7 @@ "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, "bl": { "version": "1.2.3", @@ -13109,19 +12960,18 @@ "dev": true }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "chownr": { @@ -13387,17 +13237,10 @@ "simple-swizzle": "^0.2.2" } }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, "command-line-args": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", - "dev": true, "requires": { "array-back": "^3.0.1", "find-replace": "^3.0.0", @@ -13472,6 +13315,11 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "comment-parser": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.5.tgz", + "integrity": "sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA==" + }, "compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -13852,6 +13700,11 @@ "source-map": "^0.6.1" } }, + "custom-elements-manifest": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/custom-elements-manifest/-/custom-elements-manifest-1.0.0.tgz", + "integrity": "sha512-j59k0ExGCKA8T6Mzaq+7axc+KVHwpEphEERU7VZ99260npu/p/9kd+Db+I3cGKxHkM5y6q5gnlXn00mzRQkX2A==" + }, "date-fns": { "version": "2.17.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.17.0.tgz", @@ -13861,8 +13714,7 @@ "debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "dev": true + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" }, "debug": { "version": "3.1.0", @@ -14875,7 +14727,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, "requires": { "array-back": "^3.0.1" } @@ -15147,7 +14998,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "optional": true }, "function-bind": { @@ -15756,7 +15606,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -16469,8 +16318,7 @@ "lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" }, "lodash.get": { "version": "4.4.2", @@ -16615,21 +16463,6 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -16672,12 +16505,6 @@ "object-visit": "^1.0.0" } }, - "marked": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", - "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==", - "dev": true - }, "marky": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.2.tgz", @@ -17209,8 +17036,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "normalize-url": { "version": "2.0.1", @@ -17416,15 +17242,6 @@ "mimic-fn": "^2.1.0" } }, - "onigasm": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/onigasm/-/onigasm-2.2.5.tgz", - "integrity": "sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==", - "dev": true, - "requires": { - "lru-cache": "^5.1.1" - } - }, "only": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", @@ -18216,10 +18033,9 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } @@ -18786,27 +18602,6 @@ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, - "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shiki": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.9.3.tgz", - "integrity": "sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA==", - "dev": true, - "requires": { - "onigasm": "^2.2.5", - "vscode-textmate": "^5.2.0" - } - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -19749,72 +19544,15 @@ "mime-types": "~2.1.24" } }, - "typedoc": { - "version": "0.20.36", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.20.36.tgz", - "integrity": "sha512-qFU+DWMV/hifQ9ZAlTjdFO9wbUIHuUBpNXzv68ZyURAP9pInjZiO4+jCPeAzHVcaBCHER9WL/+YzzTt6ZlN/Nw==", - "dev": true, - "requires": { - "colors": "^1.4.0", - "fs-extra": "^9.1.0", - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "lunr": "^2.3.9", - "marked": "^2.0.3", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "shelljs": "^0.8.4", - "shiki": "^0.9.3", - "typedoc-default-themes": "^0.12.10" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } - } - }, - "typedoc-default-themes": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz", - "integrity": "sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==", - "dev": true - }, "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", - "dev": true + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", + "integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==" }, "typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==" }, "ua-parser-js": { "version": "0.7.24", @@ -20045,12 +19783,6 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, - "vscode-textmate": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.4.0.tgz", - "integrity": "sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w==", - "dev": true - }, "wait-on": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.2.1.tgz", @@ -20291,10 +20023,9 @@ "dev": true }, "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.0", diff --git a/package.json b/package.json index 8c4b77d3..b308f6c0 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,11 @@ "license": "MIT", "main": "dist/shoelace.js", "module": "dist/shoelace.js", + "customElements": "dist/custom-elements.json", "type": "module", "types": "dist/shoelace.d.ts", "files": [ + "custom-elements.json", "dist" ], "keywords": [ @@ -38,6 +40,7 @@ "test:watch": "web-test-runner \"src/**/*.test.ts\" --node-resolve --puppeteer --watch" }, "dependencies": { + "@custom-elements-manifest/analyzer": "^0.3.4", "@popperjs/core": "^2.7.0", "@shoelace-style/animations": "^1.1.0", "color": "^3.1.3", @@ -57,6 +60,7 @@ "browser-sync": "^2.26.14", "chalk": "^4.1.0", "command-line-args": "^5.1.1", + "comment-parser": "^1.1.5", "concurrently": "^5.3.0", "del": "^6.0.0", "download": "^8.0.0", @@ -72,7 +76,6 @@ "sass": "^1.32.7", "sinon": "^11.1.1", "tslib": "^2.2.0", - "typedoc": "^0.20.28", "typescript": "^4.2.4", "wait-on": "^5.2.1" }, diff --git a/scripts/build.js b/scripts/build.js index 05cb228a..d0c078a2 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -5,15 +5,17 @@ import browserSync from 'browser-sync'; import chalk from 'chalk'; import commandLineArgs from 'command-line-args'; import copy from 'recursive-copy'; +import crypto from 'crypto'; import del from 'del'; import esbuild from 'esbuild'; -import { execSync } from 'child_process'; +import fs from 'fs'; import getPort from 'get-port'; import glob from 'globby'; import inlineImportPlugin from 'esbuild-plugin-inline-import'; import path from 'path'; import sass from 'sass'; import sassPlugin from 'esbuild-plugin-sass'; +import { execSync } from 'child_process'; const build = esbuild.build; const bs = browserSync.create(); @@ -23,6 +25,7 @@ del.sync('./dist'); if (!dev) execSync('tsc', { stdio: 'inherit' }); // for type declarations execSync('node scripts/make-metadata.js', { stdio: 'inherit' }); +execSync('node scripts/make-vscode-data.js', { stdio: 'inherit' }); execSync('node scripts/make-icons.js', { stdio: 'inherit' }); (async () => { @@ -83,21 +86,21 @@ execSync('node scripts/make-icons.js', { stdio: 'inherit' }); process.exit(1); }); - // Create the docs distribution by copying dist into docs/dist. This is what powers the website. It can't exist in dev - // because it will conflict with browser sync's routing to the actual dist dir. + // Create the docs distribution by copying dist into the docs folder. This is what powers the website. It doesn't need + // to exist in dev because Browser Sync routes it virtually. await del('./docs/dist'); if (!dev) { - await copy('./dist', './docs/dist'); + await Promise.all([copy('./dist', './docs/dist')]); } - console.log(chalk.green('The build has finished! 📦')); + console.log(chalk.green('The build has finished! 📦\n')); if (dev) { const port = await getPort({ port: getPort.makeRange(4000, 4999) }); - console.log(chalk.cyan(`\nLaunching the Shoelace dev server at http://localhost:${port}! 🥾\n`)); + console.log(chalk.cyan(`Launching the Shoelace dev server at http://localhost:${port}! 🥾\n`)); // Launch browser sync bs.init({ @@ -118,11 +121,10 @@ execSync('node scripts/make-icons.js', { stdio: 'inherit' }); // Rebuild and reload when source files change bs.watch(['src/**/!(*.test).*']).on('change', async filename => { console.log(`Source file changed - ${filename}`); - - // NOTE: we don't run TypeDoc on every change because it's quite heavy, so changes to the docs won't be included - // until the next time the build script runs. buildResult + // Rebuild and reload .rebuild() + .then(() => execSync('node scripts/make-metadata.js', { stdio: 'inherit' })) .then(() => bs.reload()) .catch(err => console.error(chalk.red(err))); }); diff --git a/scripts/make-icons.js b/scripts/make-icons.js index 2cdeb4a9..b7af4c83 100644 --- a/scripts/make-icons.js +++ b/scripts/make-icons.js @@ -25,15 +25,15 @@ let numIcons = 0; try { await stat(`${srcPath}/LICENSE.md`); - console.log(chalk.cyan('Generating icons from cache')); + console.log('Generating icons from cache'); } catch { // Download the source from GitHub (since not everything is published to NPM) - console.log(chalk.cyan(`Downloading and extracting Bootstrap Icons ${version} 📦`)); + console.log(`Downloading and extracting Bootstrap Icons ${version} 📦`); await download(url, './.cache/icons', { extract: true }); } // Copy icons - console.log(chalk.cyan(`Copying icons and license`)); + console.log(`Copying icons and license`); await del([iconDir]); await mkdirp(iconDir); await Promise.all([ @@ -43,7 +43,7 @@ let numIcons = 0; ]); // Generate metadata - console.log(chalk.cyan(`Generating icon metadata`)); + console.log(`Generating icon metadata`); const files = await glob(`${srcPath}/docs/content/icons/**/*.md`); const metadata = await Promise.map(files, async file => { @@ -61,7 +61,7 @@ let numIcons = 0; await writeFile(path.join(iconDir, 'icons.json'), JSON.stringify(metadata, null, 2), 'utf8'); - console.log(chalk.green(`Successfully processed ${numIcons} icons ✨\n`)); + console.log(chalk.cyan(`Successfully processed ${numIcons} icons ✨\n`)); } catch (err) { console.error(err); } diff --git a/scripts/make-metadata.js b/scripts/make-metadata.js index 8235ec59..c5ca02ab 100644 --- a/scripts/make-metadata.js +++ b/scripts/make-metadata.js @@ -1,290 +1,13 @@ // -// This script runs TypeDoc and uses its output to generate metadata files used by the docs +// This script runs the Custom Elements Manifest analyzer to generate custom-elements.json // import chalk from 'chalk'; -import { execSync } from 'child_process'; -import fs from 'fs'; import mkdirp from 'mkdirp'; -import path from 'path'; +import { execSync } from 'child_process'; -const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8')); +mkdirp.sync('./dist'); -function getTagName(className) { - return className.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`).replace(/^-/, ''); -} - -// Takes a prop or param and returns type info as a string and, if applicable, an array of possible values -function getTypeInfo(item) { - const values = []; - let type = item.type.name || ''; - - if (item.type.type === 'union') { - const types = item.type.types.map(t => { - if (t.type === 'literal' || t.type === 'reference') { - values.push(t.value); - type = `'${item.type.types.map(t => t.value).join(`' | '`)}'`; - } - - if (t.type === 'intrinsic') { - values.push(t.name); - type = item.type.types.map(t => t.name).join(' | '); - } - }); - } - - if (item.type.type === 'reflection' && item.type.declaration?.children) { - const args = item.type.declaration.children.map(prop => { - const name = prop.name; - const type = prop.type.name; - const isOptional = prop.flags.isOptional === true; - return `${name}${isOptional ? '?' : ''}: ${type}`; - }); - - // Display as an object - type += `{ ${args.join(', ')} }`; - } - - return { - type, - values: values.length ? values : undefined - }; -} - -// Splits a string of tag text into a { name, description } object -function splitText(text) { - const shouldSplit = text.indexOf(' - ') > -1; - let name = ''; - let description = ''; - - if (shouldSplit) { - const split = text.split(' - '); - name = split[0].trim(); - description = split.slice(1).join(' - ').replace(/^- /, ''); - } else { - description = text.trim().replace(/^-\s/, ''); - } - - return { name, description }; -} - -// Run typedoc -console.log(chalk.cyan('Generating type data with TypeDoc')); -mkdirp.sync('./.cache'); -execSync( - 'typedoc --json .cache/typedoc.json --entryPoints src/shoelace.ts --exclude "**/*+(index|.spec|.e2e).ts" --excludeExternals --excludeProtected --excludeInternal', - { stdio: 'inherit' } -); - -const data = JSON.parse(fs.readFileSync('.cache/typedoc.json', 'utf8')); -const modules = data.children; -const components = modules.filter(module => module.kindString === 'Class'); -const metadata = { - name: packageData.name, - description: packageData.description, - version: packageData.version, - author: packageData.author, - homepage: packageData.homepage, - license: packageData.license, - components: [] -}; - -components.map(async component => { - const api = { - className: component.name, - tag: getTagName(component.name), - file: component.sources[0].fileName, - since: '', - status: '', - props: [], - methods: [], - events: [], - slots: [], - cssCustomProperties: [], - parts: [], - dependencies: [] - }; - - // Metadata - if (component.comment) { - const tags = component.comment.tags; - 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 customProperties = tags.filter(item => item.tag === 'customproperty'); - const animations = tags.filter(item => item.tag === 'animation'); - - 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.cssCustomProperties = customProperties.map(tag => splitText(tag.text)); - api.animations = animations.map(tag => splitText(tag.text)); - } else { - console.error(chalk.yellow(`Missing comment block for ${component.name} - skipping metadata`)); - } - - // 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 => { - const { type, values } = getTypeInfo(prop); - let attribute; - - // Look for an attribute in the @property decorator - if (Array.isArray(prop.decorators)) { - const decorator = prop.decorators.find(d => d.name === 'property'); - if (decorator) { - try { - // We trust TypeDoc <3 - const options = eval(`(${decorator.arguments.options})`); - - // If an attribute is specified, it will always be a string - if (options && typeof options.attribute === 'string') { - attribute = options.attribute; - } - } catch (err) { - console.log(err); - } - } - } - - api.props.push({ - name: prop.name, - attribute: attribute, - description: prop.comment.shortText, - type, - values, - defaultValue: prop.defaultValue - }); - }); - - // 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, ''); - - // TODO: This logic is used to gather event details in a developer-friendly format. It could be improved as it may - // not cover all types yet. The output is used to populate the Events table of each component in the docs. - const params = event.type.typeArguments.map(param => { - if (param.type === 'intrinsic') { - return param.name; - } - - if (param.type === 'literal') { - return param.value; - } - - if (param.type === 'reflection') { - return ( - '{ ' + - param.declaration.children - .map(child => { - // Component exports aren't named, so they appear as "default" in the type data. However, we can use the - // id to link them to the right class. - if (child.type.name === 'default') { - const component = components.find(component => component.id === child.type.id); - if (component) { - child.type.name = component.name; - } else { - child.type.name = 'unknown'; - } - } - - if (child.type.type === 'intrinsic' || child.type.type === 'reference') { - return `${child.name}: ${child.type.name}`; - } else if (child.name) { - if (child.type.type === 'array') { - return `${child.name}: ${child.type.elementType.name}[]`; - } else { - return `${child.name}: ${child.type.elementType.name} (${child.type.type})`; - } - } else { - return child.type.type; - } - }) - .join(', ') + - ' }' - ); - } - - return ''; - }); - - const details = params.join(', '); - - api.events.push({ - name, - description: event.comment.shortText, - details - }); - }); - - // Methods - const methods = component.children - .filter(child => child.kindString === 'Method' && !child.flags.isStatic) - .filter(child => child.signatures[0].comment && child.signatures[0].comment.shortText); // only with comments - - methods.map(method => { - const signature = method.signatures[0]; - const params = Array.isArray(signature.parameters) - ? signature.parameters.map(param => { - const { type, values } = getTypeInfo(param); - return { - name: param.name, - type, - values, - isOptional: param.flags?.isOptional, - defaultValue: param.defaultValue - }; - }) - : []; - - api.methods.push({ - name: method.name, - description: signature.comment.shortText, - params - }); - }); - - metadata.components.push(api); -}); - -// Generate metadata.json -(async () => { - const filename = path.join('./dist/metadata.json'); - const json = JSON.stringify(metadata, null, 2); - - await mkdirp(path.dirname(filename)); - fs.writeFileSync(filename, json, 'utf8'); -})(); - -// Generate vscode.html-custom-data.json (for IntelliSense) -(async () => { - const filename = path.join('./dist/vscode.html-custom-data.json'); - const customData = { - tags: metadata.components.map(component => ({ - name: component.tag, - description: component.description, - attributes: component.props.map(prop => ({ - name: prop.name, - description: prop.description, - values: prop.values ? prop.values.map(value => ({ name: value })) : undefined - })) - })) - }; - const json = JSON.stringify(customData, null, 2); - - await mkdirp(path.dirname(filename)); - fs.writeFileSync(filename, json, 'utf8'); -})(); - -console.log(chalk.green(`Successfully generated metadata 🏷\n`)); +// Run the analyzer +console.log('Generating component metadata'); +execSync('cem analyze --litelement --outdir dist', { stdio: 'inherit' }); +console.log(chalk.cyan(`Successfully generated metadata 🏷\n`)); diff --git a/scripts/make-vscode-data.js b/scripts/make-vscode-data.js new file mode 100644 index 00000000..3903db70 --- /dev/null +++ b/scripts/make-vscode-data.js @@ -0,0 +1,86 @@ +// +// This script generates vscode.html-custom-data.json (for IntelliSense). +// +// You must generate dist/custom-elements.json before running this script. +// +import chalk from 'chalk'; +import fs from 'fs'; +import mkdirp from 'mkdirp'; + +const metadata = JSON.parse(fs.readFileSync('./dist/custom-elements.json', 'utf8')); + +function getAllComponents() { + const allComponents = []; + const getComponent = tagName => { + const module = metadata.modules.find(module => { + return module.exports.find(ex => { + return ex.kind === 'custom-element-definition' && ex.name === tagName; + }); + }); + + const component = module?.declarations.find(dec => dec.name === 'default'); + const tag = module.exports.filter(ex => ex.kind === 'custom-element-definition' && ex.name === tagName)[0]?.name; + + return Object.assign({ tag }, component); + }; + + metadata.modules.map(module => { + module.exports.find(ex => { + if (ex.kind === 'custom-element-definition') { + if (typeof ex.name === 'string') { + allComponents.push(getComponent(ex.name)); + } + } + }); + }); + + return allComponents; +} + +console.log('Generating IntelliSense data for VS Code'); + +const components = getAllComponents(); +const vscode = { tags: [] }; + +components.map(component => { + const name = component.tag; + const attributes = component.attributes?.map(attr => { + const type = attr.type?.text; + let values = []; + + if (type) { + type.split('|').map(val => { + val = val.trim(); + + // Only accept values that are strings and numbers + const isString = val.startsWith(`'`); + const isNumber = Number(val).toString() === val; + + if (isString) { + // Remove quotes + val = val.replace(/^'/, '').replace(/'$/, ''); + } + + if (isNumber || isString) { + values.push({ name: val }); + } + }); + } + + if (values.length === 0) { + values = undefined; + } + + return { + name: attr.name, + description: attr.description, + values + }; + }); + + vscode.tags.push({ name, attributes }); +}); + +fs.writeFileSync('./dist/vscode.html-custom-data.json', JSON.stringify(vscode, null, 2), 'utf8'); + +console.log(chalk.cyan(`Successfully generated IntelliSense data for VS Code 🔮\n`)); diff --git a/scripts/plop/templates/component/component.hbs b/scripts/plop/templates/component/component.hbs index be1d8284..d9fc8e4b 100644 --- a/scripts/plop/templates/component/component.hbs +++ b/scripts/plop/templates/component/component.hbs @@ -1,6 +1,7 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { event, EventEmitter } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import styles from 'sass:./{{ tagWithoutPrefix tag }}.scss'; /** @@ -9,10 +10,14 @@ import styles from 'sass:./{{ tagWithoutPrefix tag }}.scss'; * * @dependency sl-example * - * @slot - The default slot. - * @slot example - An example slot. + * @event sl-event-name Emitted as an example. * - * @part base - The component's base wrapper. + * @slot default The default slot. + * @slot example An example slot. + * + * @csspart base The component's base wrapper. + * + * @cssproperty --example An example CSS custom property. */ @customElement('{{ tag }}') export default class {{ properCase tag }} extends LitElement { @@ -21,8 +26,11 @@ export default class {{ properCase tag }} extends LitElement { /** An example property. */ @property() prop = 'example'; - /** An example event. */ - @event('sl-event') slEvent: EventEmitter; + @watch('someProp') + doSomething() { + // Example event + emit(this, 'sl-event-name'); + } render() { return html` `; diff --git a/src/components/alert/alert.ts b/src/components/alert/alert.ts index 1a00f101..eefb35dc 100644 --- a/src/components/alert/alert.ts +++ b/src/components/alert/alert.ts @@ -2,7 +2,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { animateTo, stopAnimations } from '../../internal/animate'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { waitForEvent } from '../../internal/event'; import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry'; import styles from 'sass:./alert.scss'; @@ -15,19 +16,25 @@ const toastStack = Object.assign(document.createElement('div'), { className: 'sl * * @dependency sl-icon-button * - * @slot - The alert's content. - * @slot icon - An icon to show in the alert. + * @slot default The alert's content. + * @slot icon An icon to show in the alert. * - * @part base - The component's base wrapper. - * @part icon - The container that wraps the alert icon. - * @part message - The alert message. - * @part close-button - The close button. + * @event sl-show Emitted when the alert opens. + * @event sl-after-show Emitted after the alert opens and all transitions are complete. + * @event sl-hide Emitted when the alert closes. + * @event sl-after-hide Emitted after the alert closes and all transitions are complete. * - * @customProperty --box-shadow - The alert's box shadow. + * @csspart base The component's base wrapper. + * @csspart icon The container that wraps the alert icon. + * @csspart message The alert message. + * @csspart close-button The close button. * - * @animation alert.show - The animation to use when showing the alert. - * @animation alert.hide - The animation to use when hiding the alert. + * @cssproperty --box-shadow The alert's box shadow. + * + * @animation alert.show The animation to use when showing the alert. + * @animation alert.hide The animation to use when hiding the alert. */ + @customElement('sl-alert') export default class SlAlert extends LitElement { static styles = unsafeCSS(styles); @@ -51,18 +58,6 @@ export default class SlAlert extends LitElement { */ @property({ type: Number }) duration = Infinity; - /** Emitted when the alert opens. */ - @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. */ - @event('sl-hide') slHide: EventEmitter; - - /** Emitted after the alert closes and all transitions are complete. */ - @event('sl-after-hide') slAfterHide: EventEmitter; - firstUpdated() { this.base.hidden = !this.open; } @@ -141,7 +136,7 @@ export default class SlAlert extends LitElement { async handleOpenChange() { if (this.open) { // Show - this.slShow.emit(); + emit(this, 'sl-show'); if (this.duration < Infinity) { this.restartAutoHide(); @@ -152,10 +147,10 @@ export default class SlAlert extends LitElement { const { keyframes, options } = getAnimation(this, 'alert.show'); await animateTo(this.base, keyframes, options); - this.slAfterShow.emit(); + emit(this, 'sl-after-show'); } else { // Hide - this.slHide.emit(); + emit(this, 'sl-hide'); clearTimeout(this.autoHideTimeout); @@ -164,7 +159,7 @@ export default class SlAlert extends LitElement { await animateTo(this.base, keyframes, options); this.base.hidden = true; - this.slAfterHide.emit(); + emit(this, 'sl-after-hide'); } } diff --git a/src/components/animation/animation.ts b/src/components/animation/animation.ts index cf64c116..f0b2e952 100644 --- a/src/components/animation/animation.ts +++ b/src/components/animation/animation.ts @@ -1,6 +1,7 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, queryAsync } from 'lit/decorators.js'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { animations } from './animations'; import styles from 'sass:./animation.scss'; @@ -8,7 +9,11 @@ import styles from 'sass:./animation.scss'; * @since 2.0 * @status stable * - * @slot - The element to animate. If multiple elements are to be animated, wrap them in a single container. + * @event sl-cancel Emitted when the animation is canceled. + * @event sl-finish Emitted when the animation finishes. + * @event sl-start Emitted when the animation starts or restarts. + * + * @slot default The element to animate. If multiple elements are to be animated, wrap them in a single container. */ @customElement('sl-animation') export default class SlAnimation extends LitElement { @@ -62,15 +67,6 @@ export default class SlAnimation extends LitElement { /** Pauses the animation. The animation will resume when this prop is removed. */ @property({ type: Boolean }) pause = false; - /** 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(); @@ -102,11 +98,11 @@ export default class SlAnimation extends LitElement { } handleAnimationFinish() { - this.slFinish.emit(); + emit(this, 'sl-finish'); } handleAnimationCancel() { - this.slCancel.emit(); + emit(this, 'sl-cancel'); } @watch('pause') @@ -116,7 +112,7 @@ export default class SlAnimation extends LitElement { if (!this.pause && !this.hasStarted) { this.hasStarted = true; - this.slStart.emit(); + emit(this, 'sl-start'); } return true; @@ -166,7 +162,7 @@ export default class SlAnimation extends LitElement { this.animation.pause(); } else { this.hasStarted = true; - this.slStart.emit(); + emit(this, 'sl-start'); } return true; diff --git a/src/components/avatar/avatar.ts b/src/components/avatar/avatar.ts index e3e2ed6d..5029e5c4 100644 --- a/src/components/avatar/avatar.ts +++ b/src/components/avatar/avatar.ts @@ -9,14 +9,14 @@ import styles from 'sass:./avatar.scss'; * * @dependency sl-icon * - * @slot icon - The default icon to use when no image or initials are present. + * @slot icon The default icon to use when no image or initials are present. * - * @part base - The component's base wrapper. - * @part icon - The container that wraps the avatar icon. - * @part initials - The container that wraps the avatar initials. - * @part image - The avatar image. + * @csspart base The component's base wrapper. + * @csspart icon The container that wraps the avatar icon. + * @csspart initials The container that wraps the avatar initials. + * @csspart image The avatar image. * - * @customProperty --size - The size of the avatar. + * @cssproperty --size The size of the avatar. */ @customElement('sl-avatar') export default class SlAvatar extends LitElement { diff --git a/src/components/badge/badge.ts b/src/components/badge/badge.ts index 0514c4f2..776aa3f0 100644 --- a/src/components/badge/badge.ts +++ b/src/components/badge/badge.ts @@ -7,9 +7,9 @@ import styles from 'sass:./badge.scss'; * @since 2.0 * @status stable * - * @slot - The badge's content. + * @slot default The badge's content. * - * @part base - The base wrapper + * @csspart base The base wrapper */ @customElement('sl-badge') export default class SlBadge extends LitElement { diff --git a/src/components/button-group/button-group.ts b/src/components/button-group/button-group.ts index 25b4b55c..29fc4e70 100644 --- a/src/components/button-group/button-group.ts +++ b/src/components/button-group/button-group.ts @@ -6,9 +6,9 @@ import styles from 'sass:./button-group.scss'; * @since 2.0 * @status stable * - * @slot - One or more `` elements to display in the button group. + * @slot default One or more `` elements to display in the button group. * - * @part base - The component's base wrapper. + * @csspart base The component's base wrapper. */ @customElement('sl-button-group') export default class SlButtonGroup extends LitElement { diff --git a/src/components/button/button.ts b/src/components/button/button.ts index 5585c66b..6f1ad76d 100644 --- a/src/components/button/button.ts +++ b/src/components/button/button.ts @@ -2,7 +2,7 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; -import { event, EventEmitter } from '../../internal/decorators'; +import { emit } from '../../internal/event'; import { hasSlot } from '../../internal/slot'; import styles from 'sass:./button.scss'; @@ -12,15 +12,18 @@ import styles from 'sass:./button.scss'; * * @dependency sl-spinner * - * @slot - The button's label. - * @slot prefix - Used to prepend an icon or similar element to the button. - * @slot suffix - Used to append an icon or similar element to the button. + * @event sl-blur Emitted when the button loses focus. + * @event sl-focus Emitted when the button gains focus. * - * @part base - The component's base wrapper. - * @part prefix - The prefix container. - * @part label - The button's label. - * @part suffix - The suffix container. - * @part caret - The button's caret. + * @slot default The button's label. + * @slot prefix Used to prepend an icon or similar element to the button. + * @slot suffix Used to append an icon or similar element to the button. + * + * @csspart base The component's base wrapper. + * @csspart prefix The prefix container. + * @csspart label The button's label. + * @csspart suffix The suffix container. + * @csspart caret The button's caret. */ @customElement('sl-button') export default class SlButton extends LitElement { @@ -73,12 +76,6 @@ export default class SlButton extends LitElement { /** Tells the browser to download the linked file as this filename. Only used when `href` is set. */ @property() download: string; - /** Emitted when the button loses focus. */ - @event('sl-blur') slBlur: EventEmitter; - - /** Emitted when the button gains focus. */ - @event('sl-focus') slFocus: EventEmitter; - connectedCallback() { super.connectedCallback(); this.handleSlotChange(); @@ -99,23 +96,23 @@ export default class SlButton extends LitElement { this.button.blur(); } - handleSlotChange() { + private handleSlotChange() { this.hasLabel = hasSlot(this); this.hasPrefix = hasSlot(this, 'prefix'); this.hasSuffix = hasSlot(this, 'suffix'); } - handleBlur() { + private handleBlur() { this.hasFocus = false; - this.slBlur.emit(); + emit(this, 'sl-blur'); } - handleFocus() { + private handleFocus() { this.hasFocus = true; - this.slFocus.emit(); + emit(this, 'sl-focus'); } - handleClick(event: MouseEvent) { + private handleClick(event: MouseEvent) { if (this.disabled || this.loading) { event.preventDefault(); event.stopPropagation(); diff --git a/src/components/card/card.ts b/src/components/card/card.ts index d5433aee..a7b7707c 100644 --- a/src/components/card/card.ts +++ b/src/components/card/card.ts @@ -8,21 +8,21 @@ import styles from 'sass:./card.scss'; * @since 2.0 * @status stable * - * @slot - The card's body. - * @slot header - The card's header. - * @slot footer - The card's footer. - * @slot image - The card's image. + * @slot default The card's body. + * @slot header The card's header. + * @slot footer The card's footer. + * @slot image The card's image. * - * @part base - The component's base wrapper. - * @part image - The card's image, if present. - * @part header - The card's header, if present. - * @part body - The card's body. - * @part footer - The card's footer, if present. + * @csspart base The component's base wrapper. + * @csspart image The card's image, if present. + * @csspart header The card's header, if present. + * @csspart body The card's body. + * @csspart footer The card's footer, if present. * - * @customProperty --border-color - The card's border color, including borders that occur inside the card. - * @customProperty --border-radius - The border radius for card edges. - * @customProperty --border-width - The width of card borders. - * @customProperty --padding - The padding to use for card sections.* + * @cssproperty --border-color The card's border color, including borders that occur inside the card. + * @cssproperty --border-radius The border radius for card edges. + * @cssproperty --border-width The width of card borders. + * @cssproperty --padding The padding to use for card sections.* */ @customElement('sl-card') export default class SlCard extends LitElement { diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts index 565f5ade..a67340dd 100644 --- a/src/components/checkbox/checkbox.ts +++ b/src/components/checkbox/checkbox.ts @@ -2,7 +2,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import styles from 'sass:./checkbox.scss'; let id = 0; @@ -11,13 +12,17 @@ let id = 0; * @since 2.0 * @status stable * - * @slot - The checkbox's label. + * @slot default The checkbox's label. * - * @part base - The component's base wrapper. - * @part control - The checkbox control. - * @part checked-icon - The container the wraps the checked icon. - * @part indeterminate-icon - The container that wraps the indeterminate icon. - * @part label - The checkbox label. + * @event sl-blur Emitted when the control loses focus. + * @event sl-change Emitted when the control's checked state changes. + * @event sl-focus Emitted when the control gains focus. + * + * @csspart base The component's base wrapper. + * @csspart control The checkbox control. + * @csspart checked-icon The container the wraps the checked icon. + * @csspart indeterminate-icon The container that wraps the indeterminate icon. + * @csspart label The checkbox label. */ @customElement('sl-checkbox') export default class SlCheckbox extends LitElement { @@ -51,15 +56,6 @@ export default class SlCheckbox extends LitElement { /** This will be true when the control is in an invalid state. Validity is determined by the `required` prop. */ @property({ type: Boolean, reflect: true }) invalid = false; - /** Emitted when the control loses focus. */ - @event('sl-blur') slBlur: EventEmitter; - - /** Emitted when the control's checked state changes. */ - @event('sl-change') slChange: EventEmitter; - - /** Emitted when the control gains focus. */ - @event('sl-focus') slFocus: EventEmitter; - firstUpdated() { this.invalid = !this.input.checkValidity(); } @@ -97,12 +93,12 @@ export default class SlCheckbox extends LitElement { handleBlur() { this.hasFocus = false; - this.slBlur.emit(); + emit(this, 'sl-blur'); } handleFocus() { this.hasFocus = true; - this.slFocus.emit(); + emit(this, 'sl-focus'); } handleLabelMouseDown(event: MouseEvent) { @@ -115,7 +111,7 @@ export default class SlCheckbox extends LitElement { @watch('indeterminate', { waitUntilFirstUpdate: true }) handleStateChange() { this.invalid = !this.input.checkValidity(); - this.slChange.emit(); + emit(this, 'sl-change'); } render() { diff --git a/src/components/color-picker/color-picker.ts b/src/components/color-picker/color-picker.ts index 69d01de8..09c42706 100644 --- a/src/components/color-picker/color-picker.ts +++ b/src/components/color-picker/color-picker.ts @@ -3,7 +3,8 @@ import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; import { styleMap } from 'lit-html/directives/style-map'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { clamp } from '../../internal/math'; import type SlDropdown from '../dropdown/dropdown'; import type SlInput from '../input/input'; @@ -19,25 +20,27 @@ import styles from 'sass:./color-picker.scss'; * @dependency sl-icon * @dependency sl-input * - * @part base - The component's base wrapper. - * @part trigger - The color picker's dropdown trigger. - * @part swatches - The container that holds swatches. - * @part swatch - Each individual swatch. - * @part grid - The color grid. - * @part grid-handle - The color grid's handle. - * @part hue-slider - The hue slider. - * @part opacity-slider - The opacity slider. - * @part slider - Hue and opacity sliders. - * @part slider-handle - Hue and opacity slider handles. - * @part preview - The preview color. - * @part input - The text input. - * @part format-button - The toggle format button's base. + * @event sl-change Emitted when the color picker's value changes. * - * @customProperty --grid-width - The width of the color grid. - * @customProperty --grid-height - The height of the color grid. - * @customProperty --grid-handle-size - The size of the color grid's handle. - * @customProperty --slider-height - The height of the hue and alpha sliders. - * @customProperty --slider-handle-size - The diameter of the slider's handle. + * @csspart base The component's base wrapper + * @csspart trigger The color picker's dropdown trigger. + * @csspart swatches The container that holds swatches. + * @csspart swatch Each individual swatch. + * @csspart grid The color grid. + * @csspart grid-handle The color grid's handle. + * @csspart hue-slider The hue slider. + * @csspart opacity-slider The opacity slider. + * @csspart slider Hue and opacity sliders. + * @csspart slider-handle Hue and opacity slider handles. + * @csspart preview The preview color. + * @csspart input The text input. + * @csspart format-button The toggle format button's base. + * + * @cssproperty --grid-width The width of the color grid. + * @cssproperty --grid-height The height of the color grid. + * @cssproperty --grid-handle-size The size of the color grid's handle. + * @cssproperty --slider-height The height of the hue and alpha sliders. + * @cssproperty --slider-handle-size The diameter of the slider's handle. */ @customElement('sl-color-picker') export default class SlColorPicker extends LitElement { @@ -122,9 +125,6 @@ export default class SlColorPicker extends LitElement { '#fff' ]; - /** Emitted when the color picker's value changes. */ - @event('sl-change') slChange: EventEmitter; - connectedCallback() { super.connectedCallback(); @@ -579,7 +579,7 @@ export default class SlColorPicker extends LitElement { } if (this.value !== this.lastValueEmitted) { - this.slChange.emit(); + emit(this, 'sl-change'); this.lastValueEmitted = this.value; } } diff --git a/src/components/details/details.ts b/src/components/details/details.ts index 9d328331..611ea31e 100644 --- a/src/components/details/details.ts +++ b/src/components/details/details.ts @@ -2,7 +2,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { animateTo, stopAnimations, shimKeyframesHeightAuto } from '../../internal/animate'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { waitForEvent } from '../../internal/event'; import { focusVisible } from '../../internal/focus-visible'; import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry'; @@ -16,17 +17,22 @@ let id = 0; * * @dependency sl-icon * - * @slot - The details' content. - * @slot summary - The details' summary. Alternatively, you can use the summary prop. + * @slot default The details' content. + * @slot summary The details' summary. Alternatively, you can use the summary prop. * - * @part base - The component's base wrapper. - * @part header - The summary header. - * @part summary - The details summary. - * @part summary-icon - The expand/collapse summary icon. - * @part content - The details content. + * @event sl-show Emitted when the details opens. + * @event sl-after-show Emitted after the details opens and all transitions are complete. + * @event sl-hide Emitted when the details closes. + * @event sl-after-hide Emitted after the details closes and all transitions are complete. * - * @animation details.show - The animation to use when showing details. You can use `height: auto` with this animation. - * @animation details.hide - The animation to use when hiding details. You can use `height: auto` with this animation. + * @csspart base The component's base wrapper. + * @csspart header The summary header. + * @csspart summary The details summary. + * @csspart summary-icon The expand/collapse summary icon. + * @csspart content The details content. + * + * @animation details.show The animation to use when showing details. You can use `height: auto` with this animation. + * @animation details.hide The animation to use when hiding details. You can use `height: auto` with this animation. */ @customElement('sl-details') export default class SlDetails extends LitElement { @@ -47,18 +53,6 @@ export default class SlDetails extends LitElement { /** Disables the details so it can't be toggled. */ @property({ type: Boolean, reflect: true }) disabled = false; - /** Emitted when the details opens. */ - @event('sl-show') slShow: EventEmitter; - - /** Emitted after the details opens and all transitions are complete. */ - @event('sl-after-show') slAfterShow: EventEmitter; - - /** Emitted when the details closes. */ - @event('sl-hide') slHide: EventEmitter; - - /** Emitted after the details closes and all transitions are complete. */ - @event('sl-after-hide') slAfterHide: EventEmitter; - connectedCallback() { super.connectedCallback(); this.updateComplete.then(() => focusVisible.observe(this.details)); @@ -122,7 +116,7 @@ export default class SlDetails extends LitElement { async handleOpenChange() { if (this.open) { // Show - this.slShow.emit(); + emit(this, 'sl-show'); await stopAnimations(this); this.body.hidden = false; @@ -131,10 +125,10 @@ export default class SlDetails extends LitElement { await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options); this.body.style.height = 'auto'; - this.slAfterShow.emit(); + emit(this, 'sl-after-show'); } else { // Hide - this.slHide.emit(); + emit(this, 'sl-hide'); await stopAnimations(this); @@ -143,7 +137,7 @@ export default class SlDetails extends LitElement { this.body.hidden = true; this.body.style.height = 'auto'; - this.slAfterHide.emit(); + emit(this, 'sl-after-hide'); } } diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 20740b40..618b1d63 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -3,7 +3,8 @@ import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; import { animateTo, stopAnimations } from '../../internal/animate'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { waitForEvent } from '../../internal/event'; import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll'; import { hasSlot } from '../../internal/slot'; @@ -22,29 +23,39 @@ let id = 0; * * @dependency sl-icon-button * - * @slot - The dialog's content. - * @slot label - The dialog's label. Alternatively, you can use the label prop. - * @slot footer - The dialog's footer, usually one or more buttons representing various options. + * @slot default The dialog's content. + * @slot label The dialog's label. Alternatively, you can use the label prop. + * @slot footer The dialog's footer, usually one or more buttons representing various options. * - * @part base - The component's base wrapper. - * @part overlay - The overlay. - * @part panel - The dialog panel (where the dialog and its content is rendered). - * @part header - The dialog header. - * @part title - The dialog title. - * @part close-button - The close button. - * @part body - The dialog body. - * @part footer - The dialog footer. + * @event sl-show Emitted when the dialog opens. + * @event sl-after-show Emitted after the dialog opens and all transitions are complete. + * @event sl-hide Emitted when the dialog closes. + * @event sl-after-hide Emitted after the dialog closes and all transitions are complete. + * @event 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. + * @event sl-request-close Emitted when the user attempts to close the dialog by clicking the close button, clicking the + * overlay, or pressing the escape key. Calling `event.preventDefault()` will prevent the dialog from closing. Avoid + * using this unless closing the dialog will result in destructive behavior such as data loss. * - * @customProperty --width - The preferred width of the dialog. Note that the dialog will shrink to accommodate smaller screens. - * @customProperty --header-spacing - The amount of padding to use for the header. - * @customProperty --body-spacing - The amount of padding to use for the body. - * @customProperty --footer-spacing - The amount of padding to use for the footer. + * @csspart base The component's base wrapper. + * @csspart overlay The overlay. + * @csspart panel The dialog panel (where the dialog and its content is rendered). + * @csspart header The dialog header. + * @csspart title The dialog title. + * @csspart close-button The close button. + * @csspart body The dialog body. + * @csspart footer The dialog footer. * - * @animation dialog.show - The animation to use when showing the dialog. - * @animation dialog.hide - The animation to use when hiding the dialog. - * @animation dialog.denyClose - The animation to use when a request to close the dialog is denied. - * @animation dialog.overlay.show - The animation to use when showing the dialog's overlay. - * @animation dialog.overlay.hide - The animation to use when hiding the dialog's overlay. + * @cssproperty --width The preferred width of the dialog. Note that the dialog will shrink to accommodate smaller screens. + * @cssproperty --header-spacing The amount of padding to use for the header. + * @cssproperty --body-spacing The amount of padding to use for the body. + * @cssproperty --footer-spacing The amount of padding to use for the footer. + * + * @animation dialog.show The animation to use when showing the dialog. + * @animation dialog.hide The animation to use when hiding the dialog. + * @animation dialog.denyClose The animation to use when a request to close the dialog is denied. + * @animation dialog.overlay.show The animation to use when showing the dialog's overlay. + * @animation dialog.overlay.hide The animation to use when hiding the dialog's overlay. */ @customElement('sl-dialog') export default class SlDialog extends LitElement { @@ -75,31 +86,6 @@ export default class SlDialog extends LitElement { */ @property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false; - /** Emitted when the dialog opens. */ - @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. */ - @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 user attempts to close the dialog by clicking the close button, clicking the overlay, or pressing - * the escape key. Calling `event.preventDefault()` will prevent the dialog from closing. Avoid using this unless - * closing the dialog will result in destructive behavior such as data loss. - */ - @event('sl-request-close') slRequestClose: EventEmitter; - connectedCallback() { super.connectedCallback(); @@ -137,7 +123,7 @@ export default class SlDialog extends LitElement { } private requestClose() { - const slRequestClose = this.slRequestClose.emit({ cancelable: true }); + const slRequestClose = emit(this, 'sl-request-close', { cancelable: true }); if (slRequestClose.defaultPrevented) { const animation = getAnimation(this, 'dialog.denyClose'); animateTo(this.panel, animation.keyframes, animation.options); @@ -158,7 +144,7 @@ export default class SlDialog extends LitElement { async handleOpenChange() { if (this.open) { // Show - this.slShow.emit(); + emit(this, 'sl-show'); this.originalTrigger = document.activeElement as HTMLElement; this.modal.activate(); @@ -169,7 +155,7 @@ export default class SlDialog extends LitElement { // Browsers that support el.focus({ preventScroll }) can set initial focus immediately if (hasPreventScroll) { - const slInitialFocus = this.slInitialFocus.emit({ cancelable: true }); + const slInitialFocus = emit(this, 'sl-initial-focus', { cancelable: true }); if (!slInitialFocus.defaultPrevented) { this.panel.focus({ preventScroll: true }); } @@ -185,16 +171,16 @@ export default class SlDialog extends LitElement { // Browsers that don't support el.focus({ preventScroll }) have to wait for the animation to finish before initial // focus to prevent scrolling issues. See: https://caniuse.com/mdn-api_htmlelement_focus_preventscroll_option if (!hasPreventScroll) { - const slInitialFocus = this.slInitialFocus.emit({ cancelable: true }); + const slInitialFocus = emit(this, 'sl-initial-focus', { cancelable: true }); if (!slInitialFocus.defaultPrevented) { this.panel.focus({ preventScroll: true }); } } - this.slAfterShow.emit(); + emit(this, 'sl-after-show'); } else { // Hide - this.slHide.emit(); + emit(this, 'sl-hide'); this.modal.deactivate(); await Promise.all([stopAnimations(this.dialog), stopAnimations(this.overlay)]); @@ -214,7 +200,7 @@ export default class SlDialog extends LitElement { setTimeout(() => trigger.focus()); } - this.slAfterHide.emit(); + emit(this, 'sl-after-hide'); } } diff --git a/src/components/drawer/drawer.ts b/src/components/drawer/drawer.ts index 5d77a12c..dcb8c9ff 100644 --- a/src/components/drawer/drawer.ts +++ b/src/components/drawer/drawer.ts @@ -3,7 +3,8 @@ import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; import { animateTo, stopAnimations } from '../../internal/animate'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { waitForEvent } from '../../internal/event'; import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll'; import { hasSlot } from '../../internal/slot'; @@ -23,36 +24,46 @@ let id = 0; * * @dependency sl-icon-button * - * @slot - The drawer's content. - * @slot label - The drawer's label. Alternatively, you can use the label prop. - * @slot footer - The drawer's footer, usually one or more buttons representing various options. + * @slot default The drawer's content. + * @slot label The drawer's label. Alternatively, you can use the label prop. + * @slot footer The drawer's footer, usually one or more buttons representing various options. * - * @part base - The component's base wrapper. - * @part overlay - The overlay. - * @part panel - The drawer panel (where the drawer and its content is rendered). - * @part header - The drawer header. - * @part title - The drawer title. - * @part close-button - The close button. - * @part body - The drawer body. - * @part footer - The drawer footer. + * @event sl-show Emitted when the drawer opens. + * @event sl-after-show Emitted after the drawer opens and all transitions are complete. + * @event sl-hide Emitted when the drawer closes. + * @event sl-after-hide Emitted after the drawer closes and all transitions are complete. + * @event 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. + * @event sl-request-close Emitted when the user attempts to close the drawer by clicking the close button, clicking the + * overlay, or pressing the escape key. Calling `event.preventDefault()` will prevent the drawer from closing. Avoid + * using this unless closing the drawer will result in destructive behavior such as data loss. * - * @customProperty --size - The preferred size of the drawer. This will be applied to the drawer's width or height + * @csspart base The component's base wrapper. + * @csspart overlay The overlay. + * @csspart panel The drawer panel (where the drawer and its content is rendered). + * @csspart header The drawer header. + * @csspart title The drawer title. + * @csspart close-button The close button. + * @csspart body The drawer body. + * @csspart footer The drawer footer. + * + * @cssproperty --size The preferred size of the drawer. This will be applied to the drawer's width or height * depending on its `placement`. Note that the drawer will shrink to accommodate smaller screens. - * @customProperty --header-spacing - The amount of padding to use for the header. - * @customProperty --body-spacing - The amount of padding to use for the body. - * @customProperty --footer-spacing - The amount of padding to use for the footer. + * @cssproperty --header-spacing The amount of padding to use for the header. + * @cssproperty --body-spacing The amount of padding to use for the body. + * @cssproperty --footer-spacing The amount of padding to use for the footer. * - * @animation drawer.showTop - The animation to use when showing a drawer with `top` placement. - * @animation drawer.showEnd - The animation to use when showing a drawer with `end` placement. - * @animation drawer.showBottom - The animation to use when showing a drawer with `bottom` placement. - * @animation drawer.showStart - The animation to use when showing a drawer with `start` placement. - * @animation drawer.hideTop - The animation to use when hiding a drawer with `top` placement. - * @animation drawer.hideEnd - The animation to use when hiding a drawer with `end` placement. - * @animation drawer.hideBottom - The animation to use when hiding a drawer with `bottom` placement. - * @animation drawer.hideStart - The animation to use when hiding a drawer with `start` placement. - * @animation drawer.denyClose - The animation to use when a request to close the drawer is denied. - * @animation drawer.overlay.show - The animation to use when showing the drawer's overlay. - * @animation drawer.overlay.hide - The animation to use when hiding the drawer's overlay. + * @animation drawer.showTop The animation to use when showing a drawer with `top` placement. + * @animation drawer.showEnd The animation to use when showing a drawer with `end` placement. + * @animation drawer.showBottom The animation to use when showing a drawer with `bottom` placement. + * @animation drawer.showStart The animation to use when showing a drawer with `start` placement. + * @animation drawer.hideTop The animation to use when hiding a drawer with `top` placement. + * @animation drawer.hideEnd The animation to use when hiding a drawer with `end` placement. + * @animation drawer.hideBottom The animation to use when hiding a drawer with `bottom` placement. + * @animation drawer.hideStart The animation to use when hiding a drawer with `start` placement. + * @animation drawer.denyClose The animation to use when a request to close the drawer is denied. + * @animation drawer.overlay.show The animation to use when showing the drawer's overlay. + * @animation drawer.overlay.hide The animation to use when hiding the drawer's overlay. */ @customElement('sl-drawer') export default class SlDrawer extends LitElement { @@ -92,28 +103,6 @@ export default class SlDrawer extends LitElement { */ @property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false; - /** Emitted when the drawer opens. */ - @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. */ - @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 user attempts to close the drawer by clicking the close button, clicking the overlay, or pressing - * the escape key. Calling `event.preventDefault()` will prevent the drawer from closing. Avoid using this unless - * closing the drawer will result in destructive behavior such as data loss. - */ - @event('sl-request-close') slRequestClose: EventEmitter; - connectedCallback() { super.connectedCallback(); @@ -151,7 +140,7 @@ export default class SlDrawer extends LitElement { } private requestClose() { - const slRequestClose = this.slRequestClose.emit({ cancelable: true }); + const slRequestClose = emit(this, 'sl-request-close', { cancelable: true }); if (slRequestClose.defaultPrevented) { const animation = getAnimation(this, 'drawer.denyClose'); animateTo(this.panel, animation.keyframes, animation.options); @@ -172,7 +161,7 @@ export default class SlDrawer extends LitElement { async handleOpenChange() { if (this.open) { // Show - this.slShow.emit(); + emit(this, 'sl-show'); this.originalTrigger = document.activeElement as HTMLElement; // Lock body scrolling only if the drawer isn't contained @@ -186,7 +175,7 @@ export default class SlDrawer extends LitElement { // Browsers that support el.focus({ preventScroll }) can set initial focus immediately if (hasPreventScroll) { - const slInitialFocus = this.slInitialFocus.emit({ cancelable: true }); + const slInitialFocus = emit(this, 'sl-initial-focus', { cancelable: true }); if (!slInitialFocus.defaultPrevented) { this.panel.focus({ preventScroll: true }); } @@ -202,16 +191,16 @@ export default class SlDrawer extends LitElement { // Browsers that don't support el.focus({ preventScroll }) have to wait for the animation to finish before initial // focus to prevent scrolling issues. See: https://caniuse.com/mdn-api_htmlelement_focus_preventscroll_option if (!hasPreventScroll) { - const slInitialFocus = this.slInitialFocus.emit({ cancelable: true }); + const slInitialFocus = emit(this, 'sl-initial-focus', { cancelable: true }); if (!slInitialFocus.defaultPrevented) { this.panel.focus({ preventScroll: true }); } } - this.slAfterShow.emit(); + emit(this, 'sl-after-show'); } else { // Hide - this.slHide.emit(); + emit(this, 'sl-hide'); this.modal.deactivate(); unlockBodyScrolling(this); @@ -231,7 +220,7 @@ export default class SlDrawer extends LitElement { setTimeout(() => trigger.focus()); } - this.slAfterHide.emit(); + emit(this, 'sl-after-hide'); } } diff --git a/src/components/dropdown/dropdown.ts b/src/components/dropdown/dropdown.ts index 29b5c1d7..a1309de5 100644 --- a/src/components/dropdown/dropdown.ts +++ b/src/components/dropdown/dropdown.ts @@ -3,7 +3,8 @@ import { customElement, property, query } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { Instance as PopperInstance, createPopper } from '@popperjs/core/dist/esm'; import { animateTo, stopAnimations } from '../../internal/animate'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { waitForEvent } from '../../internal/event'; import { scrollIntoView } from '../../internal/scroll'; import { getNearestTabbableElement } from '../../internal/tabbable'; @@ -18,15 +19,20 @@ let id = 0; * @since 2.0 * @status stable * - * @slot trigger - The dropdown's trigger, usually a `` element. - * @slot - The dropdown's content. + * @slot trigger The dropdown's trigger, usually a `` element. + * @slot default The dropdown's content. * - * @part base - The component's base wrapper. - * @part trigger - The container that wraps the trigger. - * @part panel - The panel that gets shown when the dropdown is open. + * @event sl-show Emitted when the dropdown opens. + * @event sl-after-show Emitted after the dropdown opens and all animations are complete. + * @event sl-hide Emitted when the dropdown closes. + * @event sl-after-hide Emitted after the dropdown closes and all animations are complete.* * - * @animation dropdown.show - The animation to use when showing the dropdown. - * @animation dropdown.hide - The animation to use when hiding the dropdown. + * @csspart base The component's base wrapper. + * @csspart trigger The container that wraps the trigger. + * @csspart panel The panel that gets shown when the dropdown is open. + * + * @animation dropdown.show The animation to use when showing the dropdown. + * @animation dropdown.hide The animation to use when hiding the dropdown. */ @customElement('sl-dropdown') export default class SlDropdown extends LitElement { @@ -81,18 +87,6 @@ export default class SlDropdown extends LitElement { */ @property({ type: Boolean }) hoist = false; - /** Emitted when the dropdown opens. */ - @event('sl-show') slShow: EventEmitter; - - /** Emitted after the dropdown opens and all animations are complete. */ - @event('sl-after-show') slAfterShow: EventEmitter; - - /** Emitted when the dropdown closes. */ - @event('sl-hide') slHide: EventEmitter; - - /** Emitted after the dropdown closes and all animations are complete. */ - @event('sl-after-hide') slAfterHide: EventEmitter; - connectedCallback() { super.connectedCallback(); this.handleMenuItemActivate = this.handleMenuItemActivate.bind(this); @@ -369,7 +363,7 @@ export default class SlDropdown extends LitElement { if (this.open) { // Show - this.slShow.emit(); + emit(this, 'sl-show'); this.panel.addEventListener('sl-activate', this.handleMenuItemActivate); this.panel.addEventListener('sl-select', this.handlePanelSelect); document.addEventListener('keydown', this.handleDocumentKeyDown); @@ -381,10 +375,10 @@ export default class SlDropdown extends LitElement { const { keyframes, options } = getAnimation(this, 'dropdown.show'); await animateTo(this.panel, keyframes, options); - this.slAfterShow.emit(); + emit(this, 'sl-after-show'); } else { // Hide - this.slHide.emit(); + emit(this, 'sl-hide'); this.panel.removeEventListener('sl-activate', this.handleMenuItemActivate); this.panel.removeEventListener('sl-select', this.handlePanelSelect); document.removeEventListener('keydown', this.handleDocumentKeyDown); @@ -395,7 +389,7 @@ export default class SlDropdown extends LitElement { await animateTo(this.panel, keyframes, options); this.panel.hidden = true; - this.slAfterHide.emit(); + emit(this, 'sl-after-hide'); } } diff --git a/src/components/form/form.ts b/src/components/form/form.ts index 44982209..d709eadb 100644 --- a/src/components/form/form.ts +++ b/src/components/form/form.ts @@ -1,6 +1,6 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; -import { event, EventEmitter } from '../../internal/decorators'; +import { emit } from '../../internal/event'; import type SlButton from '../button/button'; import type SlCheckbox from '../checkbox/checkbox'; import type SlColorPicker from '../color-picker/color-picker'; @@ -23,9 +23,14 @@ interface FormControl { * @since 2.0 * @status stable * - * @slot - The form's content. + * @slot default The form's content. * - * @part base - The component's base wrapper. + * @event {{ formData: FormData, formControls: [] }} 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. + * + * @csspart base The component's base wrapper. */ @customElement('sl-form') export default class SlForm extends LitElement { @@ -38,14 +43,6 @@ export default class SlForm extends LitElement { /** Prevent the form from validating inputs before submitting. */ @property({ type: Boolean, reflect: true }) novalidate = false; - /** - * 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('sl-submit') slSubmit: EventEmitter<{ formData: FormData; formControls: HTMLElement[] }>; - connectedCallback() { super.connectedCallback(); @@ -231,7 +228,7 @@ export default class SlForm extends LitElement { } } - this.slSubmit.emit({ detail: { formData, formControls } }); + emit(this, 'sl-submit', { detail: { formData, formControls } }); return true; } diff --git a/src/components/icon-button/icon-button.ts b/src/components/icon-button/icon-button.ts index 5b51e90f..bd7a02e3 100644 --- a/src/components/icon-button/icon-button.ts +++ b/src/components/icon-button/icon-button.ts @@ -11,7 +11,7 @@ import styles from 'sass:./icon-button.scss'; * * @dependency sl-icon * - * @part base - The component's base wrapper. + * @csspart base The component's base wrapper. */ @customElement('sl-icon-button') export default class SlIconButton extends LitElement { diff --git a/src/components/icon/icon.ts b/src/components/icon/icon.ts index 849541ef..8ade7d6f 100644 --- a/src/components/icon/icon.ts +++ b/src/components/icon/icon.ts @@ -1,7 +1,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { unsafeSVG } from 'lit-html/directives/unsafe-svg'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { getIconLibrary, watchIcon, unwatchIcon } from './library'; import { requestIcon } from './request'; import styles from 'sass:./icon.scss'; @@ -12,7 +13,10 @@ const parser = new DOMParser(); * @since 2.0 * @status stable * - * @part base - The component's base wrapper. + * @event sl-load Emitted when the icon has loaded. + * @event {{ status: number }} sl-error Emitted when the included file fails to load due to an error. + * + * @csspart base The component's base wrapper. */ @customElement('sl-icon') export default class SlIcon extends LitElement { @@ -32,12 +36,6 @@ export default class SlIcon extends LitElement { /** The name of a registered custom icon library. */ @property() library = 'default'; - /** Emitted when the icon has loaded. */ - @event('sl-load') slLoad: EventEmitter; - - /** Emitted when the icon failed to load. */ - @event('sl-error') slError: EventEmitter<{ status: number }>; - connectedCallback() { super.connectedCallback(); watchIcon(this); @@ -102,17 +100,17 @@ export default class SlIcon extends LitElement { } this.svg = svgEl.outerHTML; - this.slLoad.emit(); + emit(this, 'sl-load'); } else { this.svg = ''; - this.slError.emit({ detail: { status: file.status } }); + emit(this, 'sl-error', { detail: { status: file.status } }); } } else { this.svg = ''; - this.slError.emit({ detail: { status: file.status } }); + emit(this, 'sl-error', { detail: { status: file.status } }); } } catch { - this.slError.emit({ detail: { status: -1 } }); + emit(this, 'sl-error', { detail: { status: -1 } }); } } else if (this.svg) { // If we can't resolve a URL and an icon was previously set, remove it diff --git a/src/components/image-comparer/image-comparer.ts b/src/components/image-comparer/image-comparer.ts index 679e277d..a1f700f8 100644 --- a/src/components/image-comparer/image-comparer.ts +++ b/src/components/image-comparer/image-comparer.ts @@ -1,8 +1,9 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; import { styleMap } from 'lit-html/directives/style-map'; -import { event, EventEmitter, watch } from '../../internal/decorators'; import { clamp } from '../../internal/math'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import styles from 'sass:./image-comparer.scss'; /** @@ -11,18 +12,20 @@ import styles from 'sass:./image-comparer.scss'; * * @dependency sl-icon * - * @slot before - The before image, an `` or `` element. - * @slot after - The after image, an `` or `` element. - * @slot handle-icon - The icon used inside the handle. + * @slot before The before image, an `` or `` element. + * @slot after The after image, an `` or `` element. + * @slot handle-icon The icon used inside the handle. * - * @part base - The component's base wrapper. - * @part before - The container that holds the "before" image. - * @part after - The container that holds the "after" image. - * @part divider - The divider that separates the images. - * @part handle - The handle that the user drags to expose the after image. + * @event sl-change Emitted when the position changes. * - * @customProperty --divider-width - The width of the dividing line. - * @customProperty --handle-size - The size of the compare handle. + * @csspart base The component's base wrapper. + * @csspart before The container that holds the "before" image. + * @csspart after The container that holds the "after" image. + * @csspart divider The divider that separates the images. + * @csspart handle The handle that the user drags to expose the after image. + * + * @cssproperty --divider-width The width of the dividing line. + * @cssproperty --handle-size The size of the compare handle. */ @customElement('sl-image-comparer') export default class SlImageComparer extends LitElement { @@ -34,8 +37,6 @@ export default class SlImageComparer extends LitElement { /** The position of the divider as a percentage. */ @property({ type: Number, reflect: true }) position = 50; - @event('sl-change') slChange: EventEmitter; - handleDrag(event: any) { const { width } = this.base.getBoundingClientRect(); @@ -94,7 +95,7 @@ export default class SlImageComparer extends LitElement { @watch('position', { waitUntilFirstUpdate: true }) handlePositionChange() { - this.slChange.emit(); + emit(this, 'sl-change'); } render() { diff --git a/src/components/include/include.ts b/src/components/include/include.ts index f0edd6e8..33951643 100644 --- a/src/components/include/include.ts +++ b/src/components/include/include.ts @@ -1,12 +1,16 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { requestInclude } from './request'; import styles from 'sass:./include.scss'; /** * @since 2.0 * @status stable + * + * @event sl-load Emitted when the included file is loaded. + * @event {{ status: number }} sl-error Emitted when the included file fails to load due to an error. */ @customElement('sl-include') export default class SlInclude extends LitElement { @@ -24,12 +28,6 @@ export default class SlInclude extends LitElement { */ @property({ attribute: 'allow-scripts', type: Boolean }) allowScripts = false; - /** Emitted when the included file is loaded. */ - @event('sl-load') slLoad: EventEmitter; - - /** Emitted when the included file fails to load due to an error. */ - @event('sl-error') slError: EventEmitter<{ status: number }>; - executeScript(script: HTMLScriptElement) { // Create a copy of the script and swap it out so the browser executes it const newScript = document.createElement('script'); @@ -54,7 +52,7 @@ export default class SlInclude extends LitElement { } if (!file.ok) { - this.slError.emit({ detail: { status: file.status } }); + emit(this, 'sl-error', { detail: { status: file.status } }); return; } @@ -64,9 +62,9 @@ export default class SlInclude extends LitElement { [...this.querySelectorAll('script')].map(script => this.executeScript(script)); } - this.slLoad.emit(); + emit(this, 'sl-load'); } catch { - this.slError.emit({ detail: { status: -1 } }); + emit(this, 'sl-error', { detail: { status: -1 } }); } } diff --git a/src/components/input/input.ts b/src/components/input/input.ts index 42333a40..731bf4ad 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -2,7 +2,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined'; import { classMap } from 'lit-html/directives/class-map'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { getLabelledBy, renderFormControl } from '../../internal/form-control'; import { hasSlot } from '../../internal/slot'; import styles from 'sass:./input.scss'; @@ -15,25 +16,31 @@ let id = 0; * * @dependency sl-icon * - * @slot label - The input's label. Alternatively, you can use the label prop. - * @slot prefix - Used to prepend an icon or similar element to the input. - * @slot suffix - Used to append an icon or similar element to the input. - * @slot clear-icon - An icon to use in lieu of the default clear icon. - * @slot show-password-icon - An icon to use in lieu of the default show password icon. - * @slot hide-password-icon - An icon to use in lieu of the default hide password icon. - * @slot help-text - Help text that describes how to use the input. Alternatively, you can use the help-text prop. + * @slot label The input's label. Alternatively, you can use the label prop. + * @slot prefix Used to prepend an icon or similar element to the input. + * @slot suffix Used to append an icon or similar element to the input. + * @slot clear-icon An icon to use in lieu of the default clear icon. + * @slot show-password-icon An icon to use in lieu of the default show password icon. + * @slot hide-password-icon An icon to use in lieu of the default hide password icon. + * @slot help-text Help text that describes how to use the input. Alternatively, you can use the help-text prop. * - * @part base - The component's base wrapper. - * @part form-control - The form control that wraps the label, input, and help-text. - * @part label - The input label. - * @part input - The input control. - * @part prefix - The input prefix container. - * @part clear-button - The clear button. - * @part password-toggle-button - The password toggle button. - * @part suffix - The input suffix container. - * @part help-text - The input help text. + * @event sl-change Emitted when the control's value changes. + * @event sl-clear Emitted when the clear button is activated. + * @event sl-input Emitted when the control receives input. + * @event sl-focus Emitted when the control gains focus. + * @event sl-blur Emitted when the control loses focus. * - * @customProperty --focus-ring - The focus ring style to use when the control receives focus, a `box-shadow` property. + * @csspart base The component's base wrapper. + * @csspart form-control The form control that wraps the label, input, and help-text. + * @csspart label The input label. + * @csspart input The input control. + * @csspart prefix The input prefix container. + * @csspart clear-button The clear button. + * @csspart password-toggle-button The password toggle button. + * @csspart suffix The input suffix container. + * @csspart help-text The input help text. + * + * @cssproperty --focus-ring The focus ring style to use when the control receives focus, a `box-shadow` property. */ @customElement('sl-input') export default class SlInput extends LitElement { @@ -131,21 +138,6 @@ export default class SlInput extends LitElement { /** The input's inputmode attribute. */ @property() inputmode: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url'; - /** Emitted when the control's value changes. */ - @event('sl-change') slChange: EventEmitter; - - /** Emitted when the clear button is activated. */ - @event('sl-clear') slClear: EventEmitter; - - /** Emitted when the control receives input. */ - @event('sl-input') slInput: EventEmitter; - - /** Emitted when the control gains focus. */ - @event('sl-focus') slFocus: EventEmitter; - - /** Emitted when the control loses focus. */ - @event('sl-blur') slBlur: EventEmitter; - connectedCallback() { super.connectedCallback(); this.handleSlotChange = this.handleSlotChange.bind(this); @@ -196,8 +188,8 @@ export default class SlInput extends LitElement { if (this.value !== this.input.value) { this.value = this.input.value; - this.slInput.emit(); - this.slChange.emit(); + emit(this, 'sl-input'); + emit(this, 'sl-change'); } } @@ -214,12 +206,12 @@ export default class SlInput extends LitElement { handleChange() { this.value = this.input.value; - this.slChange.emit(); + emit(this, 'sl-change'); } handleInput() { this.value = this.input.value; - this.slInput.emit(); + emit(this, 'sl-input'); } handleInvalid() { @@ -228,19 +220,19 @@ export default class SlInput extends LitElement { handleBlur() { this.hasFocus = false; - this.slBlur.emit(); + emit(this, 'sl-blur'); } handleFocus() { this.hasFocus = true; - this.slFocus.emit(); + emit(this, 'sl-focus'); } handleClearClick(event: MouseEvent) { this.value = ''; - this.slClear.emit(); - this.slInput.emit(); - this.slChange.emit(); + emit(this, 'sl-clear'); + emit(this, 'sl-input'); + emit(this, 'sl-change'); this.input.focus(); event.stopPropagation(); diff --git a/src/components/menu-divider/menu-divider.ts b/src/components/menu-divider/menu-divider.ts index 29afb60e..db243f41 100644 --- a/src/components/menu-divider/menu-divider.ts +++ b/src/components/menu-divider/menu-divider.ts @@ -8,7 +8,7 @@ import styles from 'sass:./menu-divider.scss'; * * @dependency sl-menu * - * @part base - The component's base wrapper. + * @csspart base The component's base wrapper. */ @customElement('sl-menu-divider') export default class SlMenuDivider extends LitElement { diff --git a/src/components/menu-item/menu-item.ts b/src/components/menu-item/menu-item.ts index 06b4b69d..f16fbe57 100644 --- a/src/components/menu-item/menu-item.ts +++ b/src/components/menu-item/menu-item.ts @@ -10,15 +10,15 @@ import styles from 'sass:./menu-item.scss'; * * @dependency sl-icon * - * @slot - The menu item's label. - * @slot prefix - Used to prepend an icon or similar element to the menu item. - * @slot suffix - Used to append an icon or similar element to the menu item. + * @slot default The menu item's label. + * @slot prefix Used to prepend an icon or similar element to the menu item. + * @slot suffix Used to append an icon or similar element to the menu item. * - * @part base - The component's base wrapper. - * @part checked-icon - The container that wraps the checked icon. - * @part prefix - The prefix container. - * @part label - The menu item label. - * @part suffix - The suffix container. + * @csspart base The component's base wrapper. + * @csspart checked-icon The container that wraps the checked icon. + * @csspart prefix The prefix container. + * @csspart label The menu item label. + * @csspart suffix The suffix container. */ @customElement('sl-menu-item') export default class SlMenuItem extends LitElement { diff --git a/src/components/menu-label/menu-label.ts b/src/components/menu-label/menu-label.ts index 05a28270..081e8990 100644 --- a/src/components/menu-label/menu-label.ts +++ b/src/components/menu-label/menu-label.ts @@ -8,9 +8,9 @@ import styles from 'sass:./menu-label.scss'; * * @dependency sl-menu * - * @slot - The menu label's content. + * @slot default The menu label's content. * - * @part base - The component's base wrapper. + * @csspart base The component's base wrapper. */ @customElement('sl-menu-label') export default class SlMenuLabel extends LitElement { diff --git a/src/components/menu/menu.ts b/src/components/menu/menu.ts index 70ee5299..f3e32161 100644 --- a/src/components/menu/menu.ts +++ b/src/components/menu/menu.ts @@ -1,6 +1,6 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, query } from 'lit/decorators.js'; -import { event, EventEmitter } from '../../internal/decorators'; +import { emit } from '../../internal/event'; import { getTextContent } from '../../internal/slot'; import type SlMenuItem from '../menu-item/menu-item'; import styles from 'sass:./menu.scss'; @@ -9,9 +9,11 @@ import styles from 'sass:./menu.scss'; * @since 2.0 * @status stable * - * @slot - The menu's content, including menu items, menu dividers, and menu labels. + * @slot default The menu's content, including menu items, menu dividers, and menu labels. * - * @part base - The component's base wrapper. + * @event {{ item: SlMenuItem }} sl-select Emitted when a menu item is selected. + * + * @csspart base The component's base wrapper. */ @customElement('sl-menu') export default class SlMenu extends LitElement { @@ -24,9 +26,6 @@ export default class SlMenu extends LitElement { private typeToSelectString = ''; private typeToSelectTimeout: any; - /** Emitted when a menu item is selected. */ - @event('sl-select') slSelect: EventEmitter<{ item: SlMenuItem }>; - /** * Initiates type-to-select logic, which automatically selects an option based on what the user is currently typing. * The key passed will be appended to the internal query and the selection will be updated. After a brief period, the @@ -66,7 +65,7 @@ export default class SlMenu extends LitElement { const item = target.closest('sl-menu-item') as SlMenuItem; if (item && !item.disabled) { - this.slSelect.emit({ detail: { item } }); + emit(this, 'sl-select', { detail: { item } }); } } @@ -77,7 +76,7 @@ export default class SlMenu extends LitElement { event.preventDefault(); if (item) { - this.slSelect.emit({ detail: { item } }); + emit(this, 'sl-select', { detail: { item } }); } } diff --git a/src/components/progress-bar/progress-bar.ts b/src/components/progress-bar/progress-bar.ts index 8b838aad..6ac49a13 100644 --- a/src/components/progress-bar/progress-bar.ts +++ b/src/components/progress-bar/progress-bar.ts @@ -8,16 +8,16 @@ import styles from 'sass:./progress-bar.scss'; * @since 2.0 * @status stable * - * @slot - A label to show inside the indicator. + * @slot default A label to show inside the indicator. * - * @part base - The component's base wrapper. - * @part indicator - The progress bar indicator. - * @part label - The progress bar label. + * @csspart base The component's base wrapper. + * @csspart indicator The progress bar indicator. + * @csspart label The progress bar label. * - * @customProperty --height - The progress bar's height. - * @customProperty --track-color - The track color. - * @customProperty --indicator-color - The indicator color. - * @customProperty --label-color - The label color. + * @cssproperty --height The progress bar's height. + * @cssproperty --track-color The track color. + * @cssproperty --indicator-color The indicator color. + * @cssproperty --label-color The label color. */ @customElement('sl-progress-bar') export default class SlProgressBar extends LitElement { diff --git a/src/components/progress-ring/progress-ring.ts b/src/components/progress-ring/progress-ring.ts index 18c82225..acaebd52 100644 --- a/src/components/progress-ring/progress-ring.ts +++ b/src/components/progress-ring/progress-ring.ts @@ -1,19 +1,19 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; -import { watch } from '../../internal/decorators'; +import { watch } from '../../internal/watch'; import styles from 'sass:./progress-ring.scss'; /** * @since 2.0 * @status stable * - * @slot - A label to show inside the ring. + * @slot default A label to show inside the ring. * - * @part base - The component's base wrapper. - * @part label - The progress ring label. + * @csspart base The component's base wrapper. + * @csspart label The progress ring label. * - * @customProperty --track-color - The track color. - * @customProperty --indicator-color - The indicator color. + * @cssproperty --track-color The track color. + * @cssproperty --indicator-color The indicator color. */ @customElement('sl-progress-ring') export default class SlProgressRing extends LitElement { diff --git a/src/components/qr-code/qr-code.ts b/src/components/qr-code/qr-code.ts index fbc8b746..07f0c0ad 100644 --- a/src/components/qr-code/qr-code.ts +++ b/src/components/qr-code/qr-code.ts @@ -1,7 +1,7 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query } from 'lit/decorators.js'; import { styleMap } from 'lit-html/directives/style-map'; -import { watch } from '../../internal/decorators'; +import { watch } from '../../internal/watch'; import QrCreator from 'qr-creator'; import styles from 'sass:./qr-code.scss'; @@ -9,7 +9,7 @@ import styles from 'sass:./qr-code.scss'; * @since 2.0 * @status experimental * - * @part base - The component's base wrapper. + * @csspart base The component's base wrapper. */ @customElement('sl-qr-code') export default class SlQrCode extends LitElement { diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index 2a38a150..777a5669 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -7,11 +7,11 @@ import styles from 'sass:./radio-group.scss'; * @since 2.0 * @status stable * - * @slot - The default slot where radio controls are placed. - * @slot label - The radio group label. Required for proper accessibility. Alternatively, you can use the label prop. + * @slot default The default slot where radio controls are placed. + * @slot label The radio group label. Required for proper accessibility. Alternatively, you can use the label prop. * - * @part base - The component's base wrapper. - * @part label - The radio group label. + * @csspart base The component's base wrapper. + * @csspart label The radio group label. */ @customElement('sl-radio-group') export default class SlRadioGroup extends LitElement { diff --git a/src/components/radio/radio.ts b/src/components/radio/radio.ts index 438e39bb..7bfc274d 100644 --- a/src/components/radio/radio.ts +++ b/src/components/radio/radio.ts @@ -2,7 +2,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import styles from 'sass:./radio.scss'; let id = 0; @@ -11,12 +12,16 @@ let id = 0; * @since 2.0 * @status stable * - * @slot - The radio's label. + * @slot default The radio's label. * - * @part base - The component's base wrapper. - * @part control - The radio control. - * @part checked-icon - The container the wraps the checked icon. - * @part label - The radio label. + * @event sl-blur Emitted when the control loses focus. + * @event sl-change Emitted when the control's checked state changes. + * @event sl-focus Emitted when the control gains focus. + * + * @csspart base The component's base wrapper. + * @csspart control The radio control. + * @csspart checked-icon The container the wraps the checked icon. + * @csspart label The radio label. */ @customElement('sl-radio') export default class SlRadio extends LitElement { @@ -47,15 +52,6 @@ export default class SlRadio extends LitElement { */ @property({ type: Boolean, reflect: true }) invalid = false; - /** Emitted when the control loses focus. */ - @event('sl-blur') slBlur: EventEmitter; - - /** Emitted when the control's checked state changes. */ - @event('sl-change') slChange: EventEmitter; - - /** Emitted when the control gains focus. */ - @event('sl-focus') slFocus: EventEmitter; - /** Simulates a click on the radio. */ click() { this.input.click(); @@ -102,7 +98,7 @@ export default class SlRadio extends LitElement { if (this.checked) { this.getSiblingRadios().map(radio => (radio.checked = false)); } - this.slChange.emit(); + emit(this, 'sl-change'); } handleClick() { @@ -111,12 +107,12 @@ export default class SlRadio extends LitElement { handleBlur() { this.hasFocus = false; - this.slBlur.emit(); + emit(this, 'sl-blur'); } handleFocus() { this.hasFocus = true; - this.slFocus.emit(); + emit(this, 'sl-focus'); } handleKeyDown(event: KeyboardEvent) { diff --git a/src/components/range/range.ts b/src/components/range/range.ts index a4da1015..b9e23997 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -2,7 +2,8 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { ifDefined } from 'lit-html/directives/if-defined'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { getLabelledBy, renderFormControl } from '../../internal/form-control'; import { hasSlot } from '../../internal/slot'; import styles from 'sass:./range.scss'; @@ -13,12 +14,16 @@ let id = 0; * @since 2.0 * @status stable * - * @slot label - The input's label. Alternatively, you can use the label prop. - * @slot help-text - Help text that describes how to use the input. Alternatively, you can use the help-text prop. + * @slot label The input's label. Alternatively, you can use the label prop. + * @slot help-text Help text that describes how to use the input. Alternatively, you can use the help-text prop. * - * @part base - The component's base wrapper. - * @part input - The native range input. - * @part tooltip - The range tooltip. + * @event sl-change Emitted when the control's value changes. + * @event sl-blur Emitted when the control loses focus. + * @event sl-focus Emitted when the control gains focus. * + * + * @csspart base The component's base wrapper. + * @csspart input The native range input. + * @csspart tooltip The range tooltip. */ @customElement('sl-range') export default class SlRange extends LitElement { @@ -73,15 +78,6 @@ export default class SlRange extends LitElement { /** A function used to format the tooltip's value. */ @property() tooltipFormatter = (value: number) => value.toString(); - /** Emitted when the control's value changes. */ - @event('sl-change') slChange: EventEmitter; - - /** Emitted when the control loses focus. */ - @event('sl-blur') slBlur: EventEmitter; - - /** Emitted when the control gains focus. */ - @event('sl-focus') slFocus: EventEmitter; - connectedCallback() { super.connectedCallback(); this.handleSlotChange = this.handleSlotChange.bind(this); @@ -124,7 +120,7 @@ export default class SlRange extends LitElement { handleInput() { this.value = Number(this.input.value); - this.slChange.emit(); + emit(this, 'sl-change'); requestAnimationFrame(() => this.syncTooltip()); } @@ -132,13 +128,13 @@ export default class SlRange extends LitElement { handleBlur() { this.hasFocus = false; this.hasTooltip = false; - this.slBlur.emit(); + emit(this, 'sl-blur'); } handleFocus() { this.hasFocus = true; this.hasTooltip = true; - this.slFocus.emit(); + emit(this, 'sl-focus'); } @watch('label') diff --git a/src/components/rating/rating.ts b/src/components/rating/rating.ts index 22e9cd9a..09c7a69f 100644 --- a/src/components/rating/rating.ts +++ b/src/components/rating/rating.ts @@ -3,7 +3,8 @@ import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit-html/directives/class-map'; import { styleMap } from 'lit-html/directives/style-map'; import { unsafeHTML } from 'lit-html/directives/unsafe-html'; -import { event, EventEmitter, watch } from '../../internal/decorators'; +import { emit } from '../../internal/event'; +import { watch } from '../../internal/watch'; import { focusVisible } from '../../internal/focus-visible'; import { clamp } from '../../internal/math'; import styles from 'sass:./rating.scss'; @@ -14,12 +15,14 @@ import styles from 'sass:./rating.scss'; * * @dependency sl-icon * - * @part base - The component's base wrapper. + * @event sl-change Emitted when the rating's value changes. * - * @customProperty --symbol-color - The inactive color for symbols. - * @customProperty --symbol-color-active - The active color for symbols. - * @customProperty --symbol-size - The size of symbols. - * @customProperty --symbol-spacing - The spacing to use around symbols. + * @csspart base The component's base wrapper. + * + * @cssproperty --symbol-color The inactive color for symbols. + * @cssproperty --symbol-color-active The active color for symbols. + * @cssproperty --symbol-size The size of symbols. + * @cssproperty --symbol-spacing The spacing to use around symbols. */ @customElement('sl-rating') export default class SlRating extends LitElement { @@ -49,9 +52,6 @@ export default class SlRating extends LitElement { // @ts-ignore @property() getSymbol = (value?: number) => ''; - /** Emitted when the rating's value changes. */ - @event('sl-change') slChange: EventEmitter; - /** Sets focus on the rating. */ focus(options?: FocusOptions) { this.rating.focus(options); @@ -165,7 +165,7 @@ export default class SlRating extends LitElement { @watch('value', { waitUntilFirstUpdate: true }) handleValueChange() { - this.slChange.emit(); + emit(this, 'sl-change'); } roundToPrecision(numberToRound: number, precision = 0.5) { diff --git a/src/components/relative-time/relative-time.ts b/src/components/relative-time/relative-time.ts index 851cfeec..86b15a5c 100644 --- a/src/components/relative-time/relative-time.ts +++ b/src/components/relative-time/relative-time.ts @@ -1,6 +1,6 @@ import { LitElement, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { watch } from '../../internal/decorators'; +import { watch } from '../../internal/watch'; /** * @since 2.0 diff --git a/src/components/resize-observer/resize-observer.ts b/src/components/resize-observer/resize-observer.ts index ed2696b5..39f99fa3 100644 --- a/src/components/resize-observer/resize-observer.ts +++ b/src/components/resize-observer/resize-observer.ts @@ -1,11 +1,13 @@ import { LitElement, html, unsafeCSS } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { event, EventEmitter } from '../../internal/decorators'; +import { emit } from '../../internal/event'; import styles from 'sass:./resize-observer.scss'; /** * @since 2.0 * @status stable + * + * @event {{ entries: ResizeObserverEntry[] }} sl-resize Emitted when the element is resized. */ @customElement('sl-resize-observer') export default class SlResizeObserver extends LitElement { @@ -14,13 +16,10 @@ export default class SlResizeObserver extends LitElement { private resizeObserver: ResizeObserver; private observedElements: HTMLElement[] = []; - /** Emitted when the element is resized. */ - @event('sl-resize') slResize: EventEmitter<{ entries: ResizeObserverEntry[] }>; - connectedCallback() { super.connectedCallback(); this.resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => { - this.slResize.emit({ detail: { entries } }); + emit(this, 'sl-resize', { detail: { entries } }); }); } diff --git a/src/components/responsive-media/responsive-media.ts b/src/components/responsive-media/responsive-media.ts index 28905966..a7f80b34 100644 --- a/src/components/responsive-media/responsive-media.ts +++ b/src/components/responsive-media/responsive-media.ts @@ -7,7 +7,7 @@ import styles from 'sass:./responsive-media.scss'; * @since 2.0 * @status stable * - * @slot - The element to receive the aspect ratio. Should be a replaced element, such as ``, `