From fdb6c84b6e2403145494b8c9e9391f9818198b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 17 Oct 2023 13:09:27 +0200 Subject: [PATCH] build: Implement eslint rules to move style prop in the bottom of the class (#615) * setup * format styles --- .eslintrc.js | 4 +- eslint-local-rules.cjs | 88 ++ package-lock.json | 11 +- package.json | 1 + .../lib/uui-avatar-group.element.ts | 42 +- packages/uui-avatar/lib/uui-avatar.element.ts | 70 +- packages/uui-badge/lib/uui-badge.element.ts | 62 +- .../lib/uui-boolean-input.element.ts | 88 +- packages/uui-box/lib/uui-box.element.ts | 46 +- .../lib/uui-breadcrumb-item.element.ts | 78 +- .../lib/uui-breadcrumbs.element.ts | 36 +- .../lib/uui-button-group.element.ts | 8 +- .../lib/uui-button-inline-create.element.ts | 114 +- packages/uui-button/lib/uui-button.element.ts | 357 ++--- .../lib/uui-card-content-node.element.ts | 138 +- .../lib/uui-card-media.element.ts | 200 +-- .../lib/uui-card-user.element.ts | 96 +- packages/uui-card/lib/uui-card.element.ts | 116 +- packages/uui-caret/lib/uui-caret.element.ts | 40 +- .../uui-checkbox/lib/uui-checkbox.element.ts | 16 +- .../lib/uui-color-area.element.ts | 108 +- .../lib/uui-color-picker.element.ts | 362 ++--- .../lib/uui-color-slider.element.ts | 226 +-- .../lib/uui-color-swatch.element.ts | 268 ++-- .../lib/uui-color-swatches.element.ts | 41 +- .../lib/uui-combobox-list-option.element.ts | 136 +- .../lib/uui-combobox-list.element.ts | 20 +- .../lib/uui-combobox-async-example.ts | 24 +- .../uui-combobox/lib/uui-combobox.element.ts | 144 +- .../lib/uui-dialog-layout.element.ts | 34 +- packages/uui-dialog/lib/uui-dialog.element.ts | 8 +- .../lib/uui-file-dropzone.element.ts | 84 +- .../lib/uui-file-preview.element.ts | 144 +- .../lib/uui-form-layout-item.element.ts | 45 +- .../uui-form-validation-message.element.ts | 16 +- packages/uui-icon/lib/uui-icon.element.ts | 48 +- .../lib/uui-input-file.element.ts | 106 +- .../lib/uui-input-lock.element.ts | 36 +- .../lib/uui-input-password.element.ts | 28 +- packages/uui-input/lib/uui-input.element.ts | 280 ++-- .../lib/uui-key.element.ts | 8 +- .../lib/uui-keyboard-shortcut.element.ts | 8 +- packages/uui-label/lib/uui-label.element.ts | 38 +- .../lib/uui-loader-bar.element.ts | 104 +- .../lib/uui-loader-circle.element.ts | 146 +- packages/uui-loader/lib/uui-loader.element.ts | 16 +- .../lib/uui-menu-item.element.ts | 330 ++--- .../lib/uui-pagination.element.ts | 68 +- .../uui-popover/lib/uui-popover.element.ts | 62 +- .../lib/uui-progress-bar.element.ts | 42 +- .../uui-radio/lib/uui-radio-group.element.ts | 34 +- packages/uui-radio/lib/uui-radio.element.ts | 244 ++-- .../lib/uui-range-slider.element.ts | 1296 ++++++++--------- .../uui-ref-list/lib/uui-ref-list.element.ts | 8 +- .../lib/uui-ref-node-data-type.element.ts | 4 +- .../lib/uui-ref-node-document-type.element.ts | 4 +- .../lib/uui-ref-node-form.element.ts | 4 +- .../lib/uui-ref-node-member.element.ts | 4 +- .../lib/uui-ref-node-package.element.ts | 4 +- .../lib/uui-ref-node-user.element.ts | 4 +- .../uui-ref-node/lib/uui-ref-node.element.ts | 128 +- packages/uui-ref/lib/uui-ref.element.ts | 60 +- .../lib/uui-scroll-container.element.ts | 34 +- packages/uui-select/lib/uui-select.element.ts | 150 +- packages/uui-slider/lib/uui-slider.element.ts | 290 ++-- .../lib/uui-symbol-expand.element.ts | 30 +- .../lib/uui-symbol-file-dropzone.element.ts | 42 +- .../lib/uui-symbol-file-thumbnail.element.ts | 54 +- .../lib/uui-symbol-file.element.ts | 42 +- .../lib/uui-symbol-folder.element.ts | 22 +- .../lib/uui-symbol-lock.element.ts | 26 +- .../lib/uui-symbol-more.element.ts | 8 +- .../lib/uui-symbol-sort.element.ts | 40 +- .../lib/uui-table-advanced-example.ts | 86 +- .../uui-table/lib/uui-table-cell.element.ts | 76 +- .../lib/uui-table-head-cell.element.ts | 10 +- .../uui-table/lib/uui-table-head.element.ts | 18 +- .../uui-table/lib/uui-table-row.element.ts | 52 +- packages/uui-table/lib/uui-table.element.ts | 22 +- .../uui-tabs/lib/uui-tab-group.element.ts | 110 +- packages/uui-tabs/lib/uui-tab.element.ts | 154 +- packages/uui-tag/lib/uui-tag.element.ts | 44 +- .../uui-textarea/lib/uui-textarea.element.ts | 174 +-- ...ui-toast-notification-container.element.ts | 64 +- .../uui-toast-notification-layout.element.ts | 52 +- .../lib/uui-toast-notification.element.ts | 202 +-- packages/uui-toggle/lib/uui-toggle.element.ts | 26 +- 87 files changed, 4157 insertions(+), 4056 deletions(-) create mode 100644 eslint-local-rules.cjs diff --git a/.eslintrc.js b/.eslintrc.js index 4649e62cf..04d334e36 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { ignorePatterns: ['vite.*.js', 'packages/**/*.js', 'src/**/*'], root: true, - plugins: ['html', 'import'], + plugins: ['html', 'import', 'eslint-plugin-local-rules'], overrides: [ { files: ['*.ts', '*.tsx'], @@ -25,6 +25,8 @@ module.exports = { '@typescript-eslint/ban-types': 'off', //TODO: Remove (maybe) 'lit/no-useless-template-literals': 'error', 'lit/prefer-nothing': 'error', + 'local-rules/uui-class-prefix': 'warn', + 'local-rules/prefer-static-styles-last': 'warn', }, parserOptions: { project: './tsconfig.json', diff --git a/eslint-local-rules.cjs b/eslint-local-rules.cjs new file mode 100644 index 000000000..274f26e3c --- /dev/null +++ b/eslint-local-rules.cjs @@ -0,0 +1,88 @@ +'use strict'; + +// eslint-disable-next-line no-undef +module.exports = { + /** @type {import('eslint').Rule.RuleModule} */ + 'uui-class-prefix': { + meta: { + type: 'problem', + docs: { + description: + 'Ensure that all class declarations are prefixed with "UUI"', + category: 'Best Practices', + recommended: true, + }, + schema: [], + }, + create: function (context) { + function checkClassName(node) { + if (node.id && node.id.name && !node.id.name.startsWith('UUI')) { + context.report({ + node: node.id, + message: 'Class declaration should be prefixed with "UUI"', + }); + } + } + + return { + ClassDeclaration: checkClassName, + }; + }, + }, + + /** @type {import('eslint').Rule.RuleModule}*/ + 'prefer-static-styles-last': { + meta: { + type: 'suggestion', + docs: { + description: + 'Enforce the "styles" property with the static modifier to be the last property of a class that ends with "Element".', + category: 'Best Practices', + recommended: true, + }, + fixable: 'code', + schema: [], + }, + create: function (context) { + return { + ClassDeclaration(node) { + const className = node.id.name; + if (className.endsWith('Element')) { + const staticStylesProperty = node.body.body.find(bodyNode => { + return ( + bodyNode.type === 'PropertyDefinition' && + bodyNode.key.name === 'styles' && + bodyNode.static + ); + }); + if (staticStylesProperty) { + const lastProperty = node.body.body[node.body.body.length - 1]; + if (lastProperty.key.name !== staticStylesProperty.key.name) { + context.report({ + node: staticStylesProperty, + message: + 'The "styles" property should be the last property of a class declaration.', + data: { + className: className, + }, + fix: function (fixer) { + const sourceCode = context.getSourceCode(); + const staticStylesPropertyText = + sourceCode.getText(staticStylesProperty); + return [ + fixer.replaceTextRange(staticStylesProperty.range, ''), + fixer.insertTextAfterRange( + lastProperty.range, + '\n \n ' + staticStylesPropertyText + ), + ]; + }, + }); + } + } + } + }, + }; + }, + }, +}; diff --git a/package-lock.json b/package-lock.json index eeff97b10..1fcba39ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "eslint-plugin-import": "2.27.5", "eslint-plugin-lit": "1.8.3", "eslint-plugin-lit-a11y": "4.1.0", + "eslint-plugin-local-rules": "^2.0.0", "eslint-plugin-storybook": "0.6.14", "eslint-plugin-wc": "1.5.0", "github-markdown-css": "5.2.0", @@ -16585,6 +16586,11 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/eslint-plugin-local-rules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-2.0.0.tgz", + "integrity": "sha512-sWueme0kUcP0JC1+6OBDQ9edBDVFJR92WJHSRbhiRExlenMEuUisdaVBPR+ItFBFXo2Pdw6FD2UfGZWkz8e93g==" + }, "node_modules/eslint-plugin-storybook": { "version": "0.6.14", "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.14.tgz", @@ -32871,7 +32877,10 @@ "version": "1.5.0-rc.0", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.5.0-rc.0" + "@umbraco-ui/uui-base": "1.5.0-rc.0", + "@umbraco-ui/uui-button": "1.5.0-rc.0", + "@umbraco-ui/uui-popover-container": "1.5.0-rc.0", + "@umbraco-ui/uui-symbol-more": "1.5.0-rc.0" } }, "packages/uui-tag": { diff --git a/package.json b/package.json index b7c0df583..3b902ad70 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "eslint-plugin-import": "2.27.5", "eslint-plugin-lit": "1.8.3", "eslint-plugin-lit-a11y": "4.1.0", + "eslint-plugin-local-rules": "^2.0.0", "eslint-plugin-storybook": "0.6.14", "eslint-plugin-wc": "1.5.0", "github-markdown-css": "5.2.0", diff --git a/packages/uui-avatar-group/lib/uui-avatar-group.element.ts b/packages/uui-avatar-group/lib/uui-avatar-group.element.ts index 5b383bd44..8b1c1b208 100644 --- a/packages/uui-avatar-group/lib/uui-avatar-group.element.ts +++ b/packages/uui-avatar-group/lib/uui-avatar-group.element.ts @@ -10,27 +10,6 @@ import { property, queryAssignedElements, state } from 'lit/decorators.js'; */ @defineElement('uui-avatar-group') export class UUIAvatarGroupElement extends LitElement { - static styles = [ - css` - :host { - display: inline-flex; - align-items: center; - padding-left: 3px; - padding-right: 3px; - } - - ::slotted(uui-avatar) { - margin-left: -0.2em; - margin-right: -0.2em; - border: 0.1em solid var(--uui-avatar-border-color); - } - - #overflow-indication { - margin-left: 6px; - } - `, - ]; - @queryAssignedElements({ selector: 'uui-avatar, [uui-avatar]', flatten: true, @@ -88,6 +67,27 @@ export class UUIAvatarGroupElement extends LitElement { : ''} `; } + + static styles = [ + css` + :host { + display: inline-flex; + align-items: center; + padding-left: 3px; + padding-right: 3px; + } + + ::slotted(uui-avatar) { + margin-left: -0.2em; + margin-right: -0.2em; + border: 0.1em solid var(--uui-avatar-border-color); + } + + #overflow-indication { + margin-left: 6px; + } + `, + ]; } declare global { diff --git a/packages/uui-avatar/lib/uui-avatar.element.ts b/packages/uui-avatar/lib/uui-avatar.element.ts index 3eb127d5a..4ebad1b9a 100644 --- a/packages/uui-avatar/lib/uui-avatar.element.ts +++ b/packages/uui-avatar/lib/uui-avatar.element.ts @@ -9,41 +9,6 @@ import { property, state } from 'lit/decorators.js'; */ @defineElement('uui-avatar') export class UUIAvatarElement extends LitElement { - static styles = [ - css` - :host { - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - position: relative; - overflow: hidden; - border-radius: 50%; - font-weight: 700; - -webkit-font-smoothing: subpixel-antialiased; - width: calc(2em + 4px); - height: calc(2em + 4px); - user-select: none; - /* box-sizing: border-box; */ - aspect-ratio: 1; - background-color: var(--uui-palette-spanish-pink); - color: var(--uui-palette-space-cadet); - } - - :host([overflow]) { - overflow: unset; - } - - img { - object-fit: cover; - height: 100%; - width: 100%; - overflow: hidden; - border-radius: 50%; - } - `, - ]; - /** * Set to true to prevent content from getting hidden if going outside the parent. Useful in combination with something like a Badge. * @type {boolean} @@ -136,6 +101,41 @@ export class UUIAvatarElement extends LitElement { `; } + + static styles = [ + css` + :host { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + position: relative; + overflow: hidden; + border-radius: 50%; + font-weight: 700; + -webkit-font-smoothing: subpixel-antialiased; + width: calc(2em + 4px); + height: calc(2em + 4px); + user-select: none; + /* box-sizing: border-box; */ + aspect-ratio: 1; + background-color: var(--uui-palette-spanish-pink); + color: var(--uui-palette-space-cadet); + } + + :host([overflow]) { + overflow: unset; + } + + img { + object-fit: cover; + height: 100%; + width: 100%; + overflow: hidden; + border-radius: 50%; + } + `, + ]; } declare global { diff --git a/packages/uui-badge/lib/uui-badge.element.ts b/packages/uui-badge/lib/uui-badge.element.ts index a164c28bd..88b2845b1 100644 --- a/packages/uui-badge/lib/uui-badge.element.ts +++ b/packages/uui-badge/lib/uui-badge.element.ts @@ -14,6 +14,37 @@ import type { @defineElement('uui-badge') export class UUIBadgeElement extends LitElement { + /** + * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. + * @type {"default" | "positive" | "warning" | "danger"} + * @attr + * @default "default" + */ + @property({ reflect: true }) + color: InterfaceColor = 'default'; + + /** + * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. + * @type {"default" | "primary" | "secondary" | "outline" | "placeholder"} + * @attr + * @default "default" + */ + @property({ reflect: true }) + look: InterfaceLook = 'primary'; + + /** + * Bring attention to this badge by applying a bounce animation. + * @type Boolean + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + attention = false; + + render() { + return html` `; + } + static styles = [ css` :host { @@ -127,37 +158,6 @@ export class UUIBadgeElement extends LitElement { } `, ]; - - /** - * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. - * @type {"default" | "positive" | "warning" | "danger"} - * @attr - * @default "default" - */ - @property({ reflect: true }) - color: InterfaceColor = 'default'; - - /** - * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. - * @type {"default" | "primary" | "secondary" | "outline" | "placeholder"} - * @attr - * @default "default" - */ - @property({ reflect: true }) - look: InterfaceLook = 'primary'; - - /** - * Bring attention to this badge by applying a bounce animation. - * @type Boolean - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - attention = false; - - render() { - return html` `; - } } declare global { diff --git a/packages/uui-boolean-input/lib/uui-boolean-input.element.ts b/packages/uui-boolean-input/lib/uui-boolean-input.element.ts index 0054b6801..5cd31632b 100644 --- a/packages/uui-boolean-input/lib/uui-boolean-input.element.ts +++ b/packages/uui-boolean-input/lib/uui-boolean-input.element.ts @@ -15,50 +15,6 @@ type LabelPosition = 'left' | 'right' | 'top' | 'bottom'; export abstract class UUIBooleanInputElement extends FormControlMixin( LabelMixin('', LitElement) ) { - static styles = [ - css` - :host { - display: inline-block; - } - - label { - cursor: pointer; - user-select: none; - - display: flex; - flex-wrap: nowrap; - align-items: center; - justify-items: center; - gap: var(--uui-size-3); - } - - input { - position: absolute; - height: 0px; - width: 0px; - margin-top: -4px; - } - - :host([label-position='left']) label { - flex-direction: row-reverse; - } - - :host([label-position='top']) label { - gap: var(--uui-size-half-base-unit); - flex-direction: column-reverse; - } - - :host([label-position='bottom']) label { - gap: var(--uui-size-half-base-unit); - flex-direction: column; - } - - :host([disabled]) .label { - opacity: 0.5; - } - `, - ]; - /** intentional overwrite of FormControlMixins value getter and setter method. */ get value() { return this._value as string; @@ -221,4 +177,48 @@ export abstract class UUIBooleanInputElement extends FormControlMixin( `; } + + static styles = [ + css` + :host { + display: inline-block; + } + + label { + cursor: pointer; + user-select: none; + + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-items: center; + gap: var(--uui-size-3); + } + + input { + position: absolute; + height: 0px; + width: 0px; + margin-top: -4px; + } + + :host([label-position='left']) label { + flex-direction: row-reverse; + } + + :host([label-position='top']) label { + gap: var(--uui-size-half-base-unit); + flex-direction: column-reverse; + } + + :host([label-position='bottom']) label { + gap: var(--uui-size-half-base-unit); + flex-direction: column; + } + + :host([disabled]) .label { + opacity: 0.5; + } + `, + ]; } diff --git a/packages/uui-box/lib/uui-box.element.ts b/packages/uui-box/lib/uui-box.element.ts index 9200c3c29..74777d2c0 100644 --- a/packages/uui-box/lib/uui-box.element.ts +++ b/packages/uui-box/lib/uui-box.element.ts @@ -16,29 +16,6 @@ import { StaticValue, html, literal, unsafeStatic } from 'lit/static-html.js'; */ @defineElement('uui-box') export class UUIBoxElement extends LitElement { - static styles = [ - UUITextStyles, - css` - :host { - display: block; - box-shadow: var(--uui-shadow-depth-1); - border-radius: var(--uui-border-radius); - background-color: var(--uui-color-surface); - } - - #header { - display: block; - border-bottom: 1px solid var(--uui-color-divider-standalone); - padding: var(--uui-size-space-4) var(--uui-size-space-5); - } - - slot:not([name]) { - display: block; - padding: var(--uui-box-default-padding, var(--uui-size-space-5)); - } - `, - ]; - /** * Headline for this box, can also be set via the 'box' slot. * @type string @@ -121,6 +98,29 @@ export class UUIBoxElement extends LitElement { `; } + + static styles = [ + UUITextStyles, + css` + :host { + display: block; + box-shadow: var(--uui-shadow-depth-1); + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-surface); + } + + #header { + display: block; + border-bottom: 1px solid var(--uui-color-divider-standalone); + padding: var(--uui-size-space-4) var(--uui-size-space-5); + } + + slot:not([name]) { + display: block; + padding: var(--uui-box-default-padding, var(--uui-size-space-5)); + } + `, + ]; } declare global { diff --git a/packages/uui-breadcrumbs/lib/uui-breadcrumb-item.element.ts b/packages/uui-breadcrumbs/lib/uui-breadcrumb-item.element.ts index 208912bf5..7842f2b6e 100644 --- a/packages/uui-breadcrumbs/lib/uui-breadcrumb-item.element.ts +++ b/packages/uui-breadcrumbs/lib/uui-breadcrumb-item.element.ts @@ -10,6 +10,45 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-breadcrumb-item') export class UUIBreadcrumbItemElement extends LitElement { + connectedCallback() { + super.connectedCallback(); + this.setAttribute('role', 'listitem'); + } + + // TODO: ability for adding aria-label? + + /** + * Specifies the link href. + * @type {String} + * @default '#' + */ + @property() + href = '#'; + + /** + * Specifies if the element is the last item in the uui-breadcrumbs. Last item will not render as a link + * @type {Boolean} + * @attr last-item + * @default 'false' + */ + @property({ type: Boolean, attribute: 'last-item' }) + lastItem = false; + + renderLinkAndSeparator() { + return html``; + } + + renderCurrent() { + return html``; + } + + render() { + return html`${this.lastItem + ? this.renderCurrent() + : this.renderLinkAndSeparator()}`; + } + static styles = [ css` :host { @@ -51,45 +90,6 @@ export class UUIBreadcrumbItemElement extends LitElement { } `, ]; - - connectedCallback() { - super.connectedCallback(); - this.setAttribute('role', 'listitem'); - } - - // TODO: ability for adding aria-label? - - /** - * Specifies the link href. - * @type {String} - * @default '#' - */ - @property() - href = '#'; - - /** - * Specifies if the element is the last item in the uui-breadcrumbs. Last item will not render as a link - * @type {Boolean} - * @attr last-item - * @default 'false' - */ - @property({ type: Boolean, attribute: 'last-item' }) - lastItem = false; - - renderLinkAndSeparator() { - return html``; - } - - renderCurrent() { - return html``; - } - - render() { - return html`${this.lastItem - ? this.renderCurrent() - : this.renderLinkAndSeparator()}`; - } } declare global { diff --git a/packages/uui-breadcrumbs/lib/uui-breadcrumbs.element.ts b/packages/uui-breadcrumbs/lib/uui-breadcrumbs.element.ts index e4524456d..0a0ce2bbd 100644 --- a/packages/uui-breadcrumbs/lib/uui-breadcrumbs.element.ts +++ b/packages/uui-breadcrumbs/lib/uui-breadcrumbs.element.ts @@ -12,24 +12,6 @@ import { UUIBreadcrumbItemElement } from './uui-breadcrumb-item.element'; */ @defineElement('uui-breadcrumbs') export class UUIBreadcrumbsElement extends LitElement { - static styles = [ - css` - :host { - display: flex; - } - - #breadcrumbs-list { - display: flex; - list-style-type: decimal; - margin-block-start: 0em; - margin-block-end: 0em; - margin-inline-start: 0px; - margin-inline-end: 0px; - padding-inline-start: 0px; - } - `, - ]; - @queryAssignedElements({ flatten: true, selector: 'uui-breadcrumb-item, [uui-breadcrumb-item], [role=listitem]', @@ -62,6 +44,24 @@ export class UUIBreadcrumbsElement extends LitElement { `; } + + static styles = [ + css` + :host { + display: flex; + } + + #breadcrumbs-list { + display: flex; + list-style-type: decimal; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 0px; + } + `, + ]; } declare global { diff --git a/packages/uui-button-group/lib/uui-button-group.element.ts b/packages/uui-button-group/lib/uui-button-group.element.ts index 246400195..95e75de90 100644 --- a/packages/uui-button-group/lib/uui-button-group.element.ts +++ b/packages/uui-button-group/lib/uui-button-group.element.ts @@ -8,6 +8,10 @@ import { css, html, LitElement } from 'lit'; */ @defineElement('uui-button-group') export class UUIButtonGroupElement extends LitElement { + render() { + return html``; + } + static styles = [ css` :host { @@ -57,10 +61,6 @@ export class UUIButtonGroupElement extends LitElement { } `, ]; - - render() { - return html``; - } } declare global { diff --git a/packages/uui-button-inline-create/lib/uui-button-inline-create.element.ts b/packages/uui-button-inline-create/lib/uui-button-inline-create.element.ts index c4b3cd7f2..8b4052bcf 100644 --- a/packages/uui-button-inline-create/lib/uui-button-inline-create.element.ts +++ b/packages/uui-button-inline-create/lib/uui-button-inline-create.element.ts @@ -18,6 +18,63 @@ import { UUIButtonInlineCreateEvent } from './UUIButtonInlineCreateEvent'; @defineElement('uui-button-inline-create') export class UUIButtonInlineCreateElement extends LitElement { + @state() + private _position = 0; + + /** + * Label to be used for aria-label and eventually as visual label + * @type {string} + * @attr + */ + @property({ type: String }) + public label?: string; + + /** + * Place the button vertically + * @type {Boolean} + * @attr + */ + @property({ type: Boolean, reflect: true }) + vertical = false; + + private _onMouseMove(e: MouseEvent) { + this._position = this.vertical ? e.offsetY : e.offsetX; + } + + private _handleClick(e: MouseEvent) { + e.preventDefault(); + e.stopImmediatePropagation(); + + this.dispatchEvent( + new UUIButtonInlineCreateEvent(UUIButtonInlineCreateEvent.CLICK) + ); + } + + render() { + return html` + + `; + } + static styles = [ UUIBlinkKeyframes, css` @@ -172,63 +229,6 @@ export class UUIButtonInlineCreateElement extends LitElement { } `, ]; - - @state() - private _position = 0; - - /** - * Label to be used for aria-label and eventually as visual label - * @type {string} - * @attr - */ - @property({ type: String }) - public label?: string; - - /** - * Place the button vertically - * @type {Boolean} - * @attr - */ - @property({ type: Boolean, reflect: true }) - vertical = false; - - private _onMouseMove(e: MouseEvent) { - this._position = this.vertical ? e.offsetY : e.offsetX; - } - - private _handleClick(e: MouseEvent) { - e.preventDefault(); - e.stopImmediatePropagation(); - - this.dispatchEvent( - new UUIButtonInlineCreateEvent(UUIButtonInlineCreateEvent.CLICK) - ); - } - - render() { - return html` - - `; - } } declare global { diff --git a/packages/uui-button/lib/uui-button.element.ts b/packages/uui-button/lib/uui-button.element.ts index 850f59788..74bd87743 100644 --- a/packages/uui-button/lib/uui-button.element.ts +++ b/packages/uui-button/lib/uui-button.element.ts @@ -49,6 +49,185 @@ export type UUIButtonType = 'submit' | 'button' | 'reset'; export class UUIButtonElement extends FormControlMixin( LabelMixin('', PopoverTargetMixin(LitElement)) ) { + /** + * Specifies the type of button + * @type { "submit" | "button" | "reset" } + * @attr + * @default "button" + */ + @property({ type: String, reflect: true }) + type: UUIButtonType = 'button'; + + /** + * Disables the button, changes the looks of it and prevents if from emitting the click event + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + disabled = false; + + /** + * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. + * @type {"default" | "primary" | "secondary" | "outline" | "placeholder"} + * @attr + * @default "default" + */ + @property({ reflect: true }) + look: InterfaceLook = 'default'; + + /** + * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. + * @type {"default" | "positive" | "warning" | "danger"} + * @attr + * @default "default" + */ + @property({ reflect: true }) + color: InterfaceColor = 'default'; + + /** + * Makes the left and right padding of the button narrower. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + compact = false; + + /** + * Sets the state of the button. With waiting state a loader will show, the success state and fail states display icons. State is reset do default automatically after 3 seconds. + * @type {undefined |'waiting' | 'success' | 'failed'} + * @attr + * @default undefined + */ + @property({ type: String, reflect: true }) + state: UUIButtonState = undefined; + + /** + * Set an href, this will turns the button into a anchor tag. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public href?: string; + + /** + * Set an anchor tag target, only used when using href. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public target?: '_blank' | '_parent' | '_self' | '_top'; + + @query('#button') + protected _button!: HTMLInputElement; + + constructor() { + super(); + this.addEventListener('click', this._onHostClick); + } + + protected getFormElement(): HTMLElement { + return this._button; + } + + private _onHostClick(e: MouseEvent) { + if (this.disabled) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + if (this._internals?.form) { + switch (this.type) { + case 'reset': + this._internals.form.reset(); + break; + case 'button': + break; + default: + if (this._internals.form.requestSubmit) { + this._internals.form.requestSubmit(); + } else { + this._internals.form.dispatchEvent(new SubmitEvent('submit')); + } + break; + } + } + + this._togglePopover(); + } + + private _resetStateTimeout?: number; + + // Reset the state after 2sec if it is 'success' or 'failed'. + updated(changedProperties: Map) { + super.updated(changedProperties); + if (changedProperties.has('state')) { + clearTimeout(this._resetStateTimeout); + if (this.state === 'success' || this.state === 'failed') { + this._resetStateTimeout = setTimeout( + () => (this.state = undefined), + 2000 + ) as any; + } + } + } + + protected renderState(): TemplateResult | typeof nothing { + let element: TemplateResult; + switch (this.state) { + case 'waiting': + demandCustomElement(this, 'uui-loader-circle'); + element = html``; + break; + case 'success': + demandCustomElement(this, 'uui-icon'); + element = html``; + break; + case 'failed': + demandCustomElement(this, 'uui-icon'); + element = html``; + break; + default: + return nothing; + } + + return html`
${element}
`; + } + + render() { + return this.href + ? html` + + ${this.renderState()} ${this.renderLabel()} + + + ` + : html` + + `; + } + static styles = [ UUIHorizontalShakeKeyframes, css` @@ -356,184 +535,6 @@ export class UUIButtonElement extends FormControlMixin( } `, ]; - /** - * Specifies the type of button - * @type { "submit" | "button" | "reset" } - * @attr - * @default "button" - */ - @property({ type: String, reflect: true }) - type: UUIButtonType = 'button'; - - /** - * Disables the button, changes the looks of it and prevents if from emitting the click event - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - disabled = false; - - /** - * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. - * @type {"default" | "primary" | "secondary" | "outline" | "placeholder"} - * @attr - * @default "default" - */ - @property({ reflect: true }) - look: InterfaceLook = 'default'; - - /** - * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. - * @type {"default" | "positive" | "warning" | "danger"} - * @attr - * @default "default" - */ - @property({ reflect: true }) - color: InterfaceColor = 'default'; - - /** - * Makes the left and right padding of the button narrower. - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - compact = false; - - /** - * Sets the state of the button. With waiting state a loader will show, the success state and fail states display icons. State is reset do default automatically after 3 seconds. - * @type {undefined |'waiting' | 'success' | 'failed'} - * @attr - * @default undefined - */ - @property({ type: String, reflect: true }) - state: UUIButtonState = undefined; - - /** - * Set an href, this will turns the button into a anchor tag. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public href?: string; - - /** - * Set an anchor tag target, only used when using href. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public target?: '_blank' | '_parent' | '_self' | '_top'; - - @query('#button') - protected _button!: HTMLInputElement; - - constructor() { - super(); - this.addEventListener('click', this._onHostClick); - } - - protected getFormElement(): HTMLElement { - return this._button; - } - - private _onHostClick(e: MouseEvent) { - if (this.disabled) { - e.preventDefault(); - e.stopImmediatePropagation(); - return; - } - - if (this._internals?.form) { - switch (this.type) { - case 'reset': - this._internals.form.reset(); - break; - case 'button': - break; - default: - if (this._internals.form.requestSubmit) { - this._internals.form.requestSubmit(); - } else { - this._internals.form.dispatchEvent(new SubmitEvent('submit')); - } - break; - } - } - - this._togglePopover(); - } - - private _resetStateTimeout?: number; - - // Reset the state after 2sec if it is 'success' or 'failed'. - updated(changedProperties: Map) { - super.updated(changedProperties); - if (changedProperties.has('state')) { - clearTimeout(this._resetStateTimeout); - if (this.state === 'success' || this.state === 'failed') { - this._resetStateTimeout = setTimeout( - () => (this.state = undefined), - 2000 - ) as any; - } - } - } - - protected renderState(): TemplateResult | typeof nothing { - let element: TemplateResult; - switch (this.state) { - case 'waiting': - demandCustomElement(this, 'uui-loader-circle'); - element = html``; - break; - case 'success': - demandCustomElement(this, 'uui-icon'); - element = html``; - break; - case 'failed': - demandCustomElement(this, 'uui-icon'); - element = html``; - break; - default: - return nothing; - } - - return html`
${element}
`; - } - - render() { - return this.href - ? html` - - ${this.renderState()} ${this.renderLabel()} - - - ` - : html` - - `; - } } declare global { diff --git a/packages/uui-card-content-node/lib/uui-card-content-node.element.ts b/packages/uui-card-content-node/lib/uui-card-content-node.element.ts index 27a1d3ac9..2cc0e2f87 100644 --- a/packages/uui-card-content-node/lib/uui-card-content-node.element.ts +++ b/packages/uui-card-content-node/lib/uui-card-content-node.element.ts @@ -15,6 +15,75 @@ import { ifDefined } from 'lit/directives/if-defined.js'; */ @defineElement('uui-card-content-node') export class UUICardContentNodeElement extends UUICardElement { + /** + * Node name + * @type {string} + * @attr name + * @default '' + */ + @property({ type: String }) + name = ''; + + @state() + private _iconSlotHasContent = false; + + protected fallbackIcon = + ''; + + private _onSlotIconChange(event: Event) { + this._iconSlotHasContent = + (event.target as HTMLSlotElement).assignedNodes({ flatten: true }) + .length > 0; + } + + private _renderFallbackIcon() { + demandCustomElement(this, 'uui-icon'); + return html``; + } + + #renderButton() { + return html`
+ + + ${this._iconSlotHasContent === false ? this._renderFallbackIcon() : ''} + + ${this.name} +
`; + } + + #renderLink() { + return html` + + + ${this._iconSlotHasContent === false ? this._renderFallbackIcon() : ''} + + ${this.name} + `; + } + + public render() { + return html` + ${this.href ? this.#renderLink() : this.#renderButton()} + +
+ + + + + `; + } + static styles = [ ...UUICardElement.styles, css` @@ -87,75 +156,6 @@ export class UUICardContentNodeElement extends UUICardElement { } `, ]; - - /** - * Node name - * @type {string} - * @attr name - * @default '' - */ - @property({ type: String }) - name = ''; - - @state() - private _iconSlotHasContent = false; - - protected fallbackIcon = - ''; - - private _onSlotIconChange(event: Event) { - this._iconSlotHasContent = - (event.target as HTMLSlotElement).assignedNodes({ flatten: true }) - .length > 0; - } - - private _renderFallbackIcon() { - demandCustomElement(this, 'uui-icon'); - return html``; - } - - #renderButton() { - return html`
- - - ${this._iconSlotHasContent === false ? this._renderFallbackIcon() : ''} - - ${this.name} -
`; - } - - #renderLink() { - return html` - - - ${this._iconSlotHasContent === false ? this._renderFallbackIcon() : ''} - - ${this.name} - `; - } - - public render() { - return html` - ${this.href ? this.#renderLink() : this.#renderButton()} - -
- - - - - `; - } } declare global { diff --git a/packages/uui-card-media/lib/uui-card-media.element.ts b/packages/uui-card-media/lib/uui-card-media.element.ts index 31d9c3d0f..b7ea89e68 100644 --- a/packages/uui-card-media/lib/uui-card-media.element.ts +++ b/packages/uui-card-media/lib/uui-card-media.element.ts @@ -17,106 +17,6 @@ import '@umbraco-ui/uui-symbol-file/lib'; */ @defineElement('uui-card-media') export class UUICardMediaElement extends UUICardElement { - static styles = [ - ...UUICardElement.styles, - css` - #file-symbol, - #folder-symbol { - align-self: center; - margin: var(--uui-size-14); - width: 80%; - } - - slot[name='tag'] { - position: absolute; - top: var(--uui-size-4); - right: var(--uui-size-4); - display: flex; - justify-content: right; - } - - slot[name='actions'] { - position: absolute; - top: var(--uui-size-4); - right: var(--uui-size-4); - display: flex; - justify-content: right; - - opacity: 0; - transition: opacity 120ms; - } - :host(:focus) slot[name='actions'], - :host(:focus-within) slot[name='actions'], - :host(:hover) slot[name='actions'] { - opacity: 1; - } - - slot:not([name])::slotted(*) { - align-self: center; - border-radius: var(--uui-border-radius); - object-fit: cover; - width: 100%; - height: 100%; - } - - #open-part { - position: absolute; - bottom: 0; - width: 100%; - background-color: var(--uui-color-surface); - color: var(--uui-color-interactive); - border: none; - cursor: pointer; - border-top: 1px solid var(--uui-color-divider); - border-radius: 0 0 var(--uui-border-radius) var(--uui-border-radius); - display: flex; - justify-content: flex-start; - align-items: center; - font-family: inherit; - font-size: var(--uui-type-small-size); - box-sizing: border-box; - padding: var(--uui-size-2) var(--uui-size-4); - } - - :host([disabled]) #open-part { - pointer-events: none; - background: var(--uui-color-disabled); - color: var(--uui-color-contrast-disabled); - } - - #open-part:hover { - text-decoration: underline; - color: var(--uui-color-interactive-emphasis); - } - - :host([image]:not([image=''])) #open-part { - transition: opacity 0.5s 0.5s; - opacity: 0; - } - - :host( - [image]:not([image='']):hover, - [image]:not([image='']):focus, - [image]:not([image='']):focus-within, - [selected][image]:not([image='']), - [error][image]:not([image='']) - ) - #open-part { - opacity: 1; - transition-duration: 120ms; - transition-delay: 0s; - } - - /* - #info-icon { - margin-right: var(--uui-size-2); - display: flex; - height: var(--uui-size-8); - } - */ - `, - ]; - /** * Media name * @type {string} @@ -217,6 +117,106 @@ export class UUICardMediaElement extends UUICardElement { `; } + + static styles = [ + ...UUICardElement.styles, + css` + #file-symbol, + #folder-symbol { + align-self: center; + margin: var(--uui-size-14); + width: 80%; + } + + slot[name='tag'] { + position: absolute; + top: var(--uui-size-4); + right: var(--uui-size-4); + display: flex; + justify-content: right; + } + + slot[name='actions'] { + position: absolute; + top: var(--uui-size-4); + right: var(--uui-size-4); + display: flex; + justify-content: right; + + opacity: 0; + transition: opacity 120ms; + } + :host(:focus) slot[name='actions'], + :host(:focus-within) slot[name='actions'], + :host(:hover) slot[name='actions'] { + opacity: 1; + } + + slot:not([name])::slotted(*) { + align-self: center; + border-radius: var(--uui-border-radius); + object-fit: cover; + width: 100%; + height: 100%; + } + + #open-part { + position: absolute; + bottom: 0; + width: 100%; + background-color: var(--uui-color-surface); + color: var(--uui-color-interactive); + border: none; + cursor: pointer; + border-top: 1px solid var(--uui-color-divider); + border-radius: 0 0 var(--uui-border-radius) var(--uui-border-radius); + display: flex; + justify-content: flex-start; + align-items: center; + font-family: inherit; + font-size: var(--uui-type-small-size); + box-sizing: border-box; + padding: var(--uui-size-2) var(--uui-size-4); + } + + :host([disabled]) #open-part { + pointer-events: none; + background: var(--uui-color-disabled); + color: var(--uui-color-contrast-disabled); + } + + #open-part:hover { + text-decoration: underline; + color: var(--uui-color-interactive-emphasis); + } + + :host([image]:not([image=''])) #open-part { + transition: opacity 0.5s 0.5s; + opacity: 0; + } + + :host( + [image]:not([image='']):hover, + [image]:not([image='']):focus, + [image]:not([image='']):focus-within, + [selected][image]:not([image='']), + [error][image]:not([image='']) + ) + #open-part { + opacity: 1; + transition-duration: 120ms; + transition-delay: 0s; + } + + /* + #info-icon { + margin-right: var(--uui-size-2); + display: flex; + height: var(--uui-size-8); + } + */ + `, + ]; } declare global { diff --git a/packages/uui-card-user/lib/uui-card-user.element.ts b/packages/uui-card-user/lib/uui-card-user.element.ts index d605ecfb0..d9230350b 100644 --- a/packages/uui-card-user/lib/uui-card-user.element.ts +++ b/packages/uui-card-user/lib/uui-card-user.element.ts @@ -14,6 +14,54 @@ import { ifDefined } from 'lit/directives/if-defined.js'; */ @defineElement('uui-card-user') export class UUICardUserElement extends UUICardElement { + /** + * User name + * @type {string} + * @attr name + * @default '' + */ + @property({ type: String }) + name = ''; + + connectedCallback(): void { + super.connectedCallback(); + + demandCustomElement(this, 'uui-avatar'); + } + + #renderButton() { + return html`
+ ${this.name} +
`; + } + + #renderLink() { + return html` + ${this.name} + `; + } + + public render() { + return html` + + ${this.href ? this.#renderLink() : this.#renderButton()} + + + + `; + } + static styles = [ ...UUICardElement.styles, css` @@ -90,54 +138,6 @@ export class UUICardUserElement extends UUICardElement { } `, ]; - - /** - * User name - * @type {string} - * @attr name - * @default '' - */ - @property({ type: String }) - name = ''; - - connectedCallback(): void { - super.connectedCallback(); - - demandCustomElement(this, 'uui-avatar'); - } - - #renderButton() { - return html`
- ${this.name} -
`; - } - - #renderLink() { - return html` - ${this.name} - `; - } - - public render() { - return html` - - ${this.href ? this.#renderLink() : this.#renderButton()} - - - - `; - } } declare global { diff --git a/packages/uui-card/lib/uui-card.element.ts b/packages/uui-card/lib/uui-card.element.ts index 8bba180f3..7c55ffddc 100644 --- a/packages/uui-card/lib/uui-card.element.ts +++ b/packages/uui-card/lib/uui-card.element.ts @@ -18,6 +18,64 @@ import { UUICardEvent } from './UUICardEvent'; export class UUICardElement extends SelectOnlyMixin( SelectableMixin(LitElement) ) { + /** + * Set to true to prevent opening of this item. + * This does not prevent selection, selection is controlled by property 'selectable' + * @type {boolean} + * @attr disabled + * @default false + */ + @property({ type: Boolean, reflect: true, attribute: 'disabled' }) + disabled = false; + + /** + * Set to true to highlight there is an error with this item. + * @type {boolean} + * @attr error + * @default false + */ + @property({ type: Boolean, reflect: true }) + error = false; + + /** + * Set an href, this will turns the name of the card into an anchor tag. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public href?: string; + + /** + * Set an anchor tag target, only used when using href. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public target?: '_blank' | '_parent' | '_self' | '_top'; + + // This is deprecated - use href instead + protected handleOpenClick(e: Event) { + if (this.disabled) return; + + e.stopPropagation(); + this.dispatchEvent(new UUICardEvent(UUICardEvent.OPEN)); + } + // This is deprecated - use href instead + protected handleOpenKeydown(e: KeyboardEvent) { + if (this.disabled) return; + if (e.key !== 'Enter') return; + + e.preventDefault(); + e.stopPropagation(); + this.dispatchEvent(new UUICardEvent(UUICardEvent.OPEN)); + } + + protected render() { + return html``; + } + static styles = [ css` :host { @@ -105,64 +163,6 @@ export class UUICardElement extends SelectOnlyMixin( } `, ]; - - /** - * Set to true to prevent opening of this item. - * This does not prevent selection, selection is controlled by property 'selectable' - * @type {boolean} - * @attr disabled - * @default false - */ - @property({ type: Boolean, reflect: true, attribute: 'disabled' }) - disabled = false; - - /** - * Set to true to highlight there is an error with this item. - * @type {boolean} - * @attr error - * @default false - */ - @property({ type: Boolean, reflect: true }) - error = false; - - /** - * Set an href, this will turns the name of the card into an anchor tag. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public href?: string; - - /** - * Set an anchor tag target, only used when using href. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public target?: '_blank' | '_parent' | '_self' | '_top'; - - // This is deprecated - use href instead - protected handleOpenClick(e: Event) { - if (this.disabled) return; - - e.stopPropagation(); - this.dispatchEvent(new UUICardEvent(UUICardEvent.OPEN)); - } - // This is deprecated - use href instead - protected handleOpenKeydown(e: KeyboardEvent) { - if (this.disabled) return; - if (e.key !== 'Enter') return; - - e.preventDefault(); - e.stopPropagation(); - this.dispatchEvent(new UUICardEvent(UUICardEvent.OPEN)); - } - - protected render() { - return html``; - } } declare global { diff --git a/packages/uui-caret/lib/uui-caret.element.ts b/packages/uui-caret/lib/uui-caret.element.ts index 6355dfc6c..99a456b2f 100644 --- a/packages/uui-caret/lib/uui-caret.element.ts +++ b/packages/uui-caret/lib/uui-caret.element.ts @@ -9,26 +9,6 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-caret') export class UUICaretElement extends LitElement { - static styles = [ - css` - :host { - display: inline-block; - width: 12px; - vertical-align: middle; - } - - svg { - fill: currentColor; - transform-origin: 50% 50%; - transition: transform 280ms cubic-bezier(0.17, -0.88, 0.82, 1.84); /* Julia's beloved easing */ - } - - :host([open]) svg { - transform: rotate(180deg); - } - `, - ]; - /** * Turns the arrow around. * @type {boolean} @@ -49,6 +29,26 @@ export class UUICaretElement extends LitElement { `; } + + static styles = [ + css` + :host { + display: inline-block; + width: 12px; + vertical-align: middle; + } + + svg { + fill: currentColor; + transform-origin: 50% 50%; + transition: transform 280ms cubic-bezier(0.17, -0.88, 0.82, 1.84); /* Julia's beloved easing */ + } + + :host([open]) svg { + transform: rotate(180deg); + } + `, + ]; } declare global { diff --git a/packages/uui-checkbox/lib/uui-checkbox.element.ts b/packages/uui-checkbox/lib/uui-checkbox.element.ts index 47c54a7d6..40c828139 100644 --- a/packages/uui-checkbox/lib/uui-checkbox.element.ts +++ b/packages/uui-checkbox/lib/uui-checkbox.element.ts @@ -22,6 +22,14 @@ export class UUICheckboxElement extends UUIBooleanInputElement { */ static readonly formAssociated = true; + renderCheckbox() { + return html` +
+
${iconCheck}
+
+ `; + } + static styles = [ ...UUIBooleanInputElement.styles, UUIHorizontalShakeKeyframes, @@ -168,14 +176,6 @@ export class UUICheckboxElement extends UUIBooleanInputElement { } `, ]; - - renderCheckbox() { - return html` -
-
${iconCheck}
-
- `; - } } declare global { diff --git a/packages/uui-color-area/lib/uui-color-area.element.ts b/packages/uui-color-area/lib/uui-color-area.element.ts index 0bb2c8565..48203c9bf 100644 --- a/packages/uui-color-area/lib/uui-color-area.element.ts +++ b/packages/uui-color-area/lib/uui-color-area.element.ts @@ -17,60 +17,6 @@ import { UUIColorAreaEvent } from './UUIColorAreaEvent'; */ @defineElement('uui-color-area') export class UUIColorAreaElement extends LitElement { - static styles = [ - css` - :host { - display: inline-block; - width: 280px; - height: 200px; - } - - :host([disabled]) { - cursor: not-allowed; - } - - :host([disabled]) .color-area { - user-select: none; - pointer-events: none; - opacity: 0.55; - } - - .color-area { - position: relative; - height: 100%; - width: 100%; - background-image: linear-gradient( - to bottom, - rgba(0, 0, 0, 0) 0%, - rgba(0, 0, 0, 1) 100% - ), - linear-gradient(to right, #fff 0%, rgba(255, 255, 255, 0) 100%); - box-sizing: border-box; - cursor: crosshair; - forced-color-adjust: none; - } - .color-area__handle { - position: absolute; - width: var(--uui-color-area-grid-handle-size, 16px); - height: var(--uui-color-area-grid-handle-size, 16px); - border-radius: 50%; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); - border: solid 2px white; - margin-top: calc(var(--uui-color-area-grid-handle-size, 16px) / -2); - margin-left: calc(var(--uui-color-area-grid-handle-size, 16px) / -2); - transition: 150ms transform; - box-sizing: inherit; - } - .color-area__handle--dragging { - cursor: none; - transform: scale(1.5); - } - .color-area__handle--empty { - display: none; - } - `, - ]; - @state() private isDraggingGridHandle = false; /** @@ -303,6 +249,60 @@ export class UUIColorAreaElement extends LitElement { `; } + + static styles = [ + css` + :host { + display: inline-block; + width: 280px; + height: 200px; + } + + :host([disabled]) { + cursor: not-allowed; + } + + :host([disabled]) .color-area { + user-select: none; + pointer-events: none; + opacity: 0.55; + } + + .color-area { + position: relative; + height: 100%; + width: 100%; + background-image: linear-gradient( + to bottom, + rgba(0, 0, 0, 0) 0%, + rgba(0, 0, 0, 1) 100% + ), + linear-gradient(to right, #fff 0%, rgba(255, 255, 255, 0) 100%); + box-sizing: border-box; + cursor: crosshair; + forced-color-adjust: none; + } + .color-area__handle { + position: absolute; + width: var(--uui-color-area-grid-handle-size, 16px); + height: var(--uui-color-area-grid-handle-size, 16px); + border-radius: 50%; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); + border: solid 2px white; + margin-top: calc(var(--uui-color-area-grid-handle-size, 16px) / -2); + margin-left: calc(var(--uui-color-area-grid-handle-size, 16px) / -2); + transition: 150ms transform; + box-sizing: inherit; + } + .color-area__handle--dragging { + cursor: none; + transform: scale(1.5); + } + .color-area__handle--empty { + display: none; + } + `, + ]; } declare global { diff --git a/packages/uui-color-picker/lib/uui-color-picker.element.ts b/packages/uui-color-picker/lib/uui-color-picker.element.ts index b07587bda..36c897cf1 100644 --- a/packages/uui-color-picker/lib/uui-color-picker.element.ts +++ b/packages/uui-color-picker/lib/uui-color-picker.element.ts @@ -65,151 +65,6 @@ type UUIColorPickerSize = 'small' | 'medium' | 'large'; */ @defineElement('uui-color-picker') export class UUIColorPickerElement extends LabelMixin('label', LitElement) { - static styles = [ - css` - :host { - --uui-look-outline-border: #ddd; - --uui-look-outline-border-hover: #aaa; - font-size: 0.8rem; - display: block; - width: var(--uui-color-picker-width, 280px); - } - .color-picker { - width: 100%; - background-color: #fff; - user-select: none; - border: solid 1px #e4e4e7; - } - .color-picker__user-input { - display: flex; - padding: 0 0.75rem 0.75rem 0.75rem; - } - .color-picker__user-input uui-button { - border: var(--uui-input-border-width, 1px) solid - var(--uui-input-border-color, var(--uui-color-border)); - border-left: none; - } - .color-picker__preview, - .color-picker__trigger { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - justify-content: center; - position: relative; - width: 2.25rem; - height: 2.25rem; - border: none; - border-radius: 50%; - background: none; - } - .color-picker__preview { - cursor: copy; - margin-left: 0.75rem; - border-radius: 50%; - } - .color-picker__trigger[disabled] { - cursor: not-allowed; - opacity: 0.5; - } - .color-picker__preview::before, - .color-picker__trigger::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border-radius: inherit; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); - /* We use a custom property in lieu of currentColor because of https://bugs.webkit.org/show_bug.cgi?id=216780 */ - background-color: var(--preview-color); - } - - .color-dropdown__trigger--empty::before { - background-color: transparent; - } - - .color-picker__transparent-bg { - background-image: linear-gradient( - 45deg, - var(--uui-palette-grey) 25%, - transparent 25% - ), - linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), - linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), - linear-gradient(45deg, var(--uui-palette-grey) 25%, transparent 25%); - background-size: 10px 10px; - background-position: 0 0, 0 0, -5px -5px, 5px 5px; - } - - .color-picker__preview-color--copied { - animation: pulse 0.75s; - } - - @keyframes pulse { - 0% { - box-shadow: 0 0 0 0 var(--uui-palette-space-cadet-light); - } - 70% { - box-shadow: 0 0 0 0.5rem transparent; - } - 100% { - box-shadow: 0 0 0 0 transparent; - } - } - - .color-picker__controls { - padding: 0.75rem; - display: flex; - align-items: center; - } - .color-picker__sliders { - flex: 1 1 auto; - } - - uui-color-slider:not(:last-of-type) { - margin-bottom: 1rem; - } - - .color-picker__toggle-format { - min-width: 45px; - --uui-button-font-size: 0.8rem; - } - .color-picker__toggle-format > span { - text-transform: uppercase; - } - - uui-color-swatches { - border-top: solid 1px #d4d4d8; - padding: 0.75rem; - } - - button[slot='trigger'] { - border-radius: 50%; - cursor: pointer; - width: 36px; - height: 36px; - } - - uui-popover { - display: block; - width: 100%; - margin: 5px 0; - } - - uui-input { - /* lower the font size to avoid overflow with hlsa format */ - font-size: 0.85rem; - box-sizing: content-box; - flex: 1; - } - - uui-color-area { - width: 100%; - } - `, - ]; - @query('[part="input"]') _input!: UUIInputElement; @query('.color-picker__preview') _previewButton!: HTMLButtonElement; @query('#swatches') _swatches!: UUIColorSwatchesElement; @@ -498,7 +353,7 @@ export class UUIColorPickerElement extends LabelMixin('label', LitElement) { closeColorPicker(event: Event) { const target = event.target as UUIPopoverElement; - const trigger = target.querySelector("button[part=trigger]"); + const trigger = target.querySelector('button[part=trigger]'); if (trigger) { trigger.setAttribute('aria-expanded', 'false'); @@ -617,13 +472,13 @@ export class UUIColorPickerElement extends LabelMixin('label', LitElement) { ${!this.noFormatToggle ? html` - ${this.format} - ` + label="Toggle color format" + @click=${this.handleFormatToggle} + class="color-picker__toggle-format" + ?disabled=${this.disabled} + compact> + ${this.format} + ` : ''} ${hasEyeDropper ? html` ${this.swatches.map( swatch => - html` + html` ` )} `; } private _renderPreviewButton() { - return html` - -
${this._renderColorPicker()}
-
`; + return html` + +
${this._renderColorPicker()}
+
`; } render() { @@ -693,6 +548,151 @@ export class UUIColorPickerElement extends LabelMixin('label', LitElement) { ? this._renderColorPicker() : this._renderPreviewButton(); } + + static styles = [ + css` + :host { + --uui-look-outline-border: #ddd; + --uui-look-outline-border-hover: #aaa; + font-size: 0.8rem; + display: block; + width: var(--uui-color-picker-width, 280px); + } + .color-picker { + width: 100%; + background-color: #fff; + user-select: none; + border: solid 1px #e4e4e7; + } + .color-picker__user-input { + display: flex; + padding: 0 0.75rem 0.75rem 0.75rem; + } + .color-picker__user-input uui-button { + border: var(--uui-input-border-width, 1px) solid + var(--uui-input-border-color, var(--uui-color-border)); + border-left: none; + } + .color-picker__preview, + .color-picker__trigger { + flex: 0 0 auto; + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + width: 2.25rem; + height: 2.25rem; + border: none; + border-radius: 50%; + background: none; + } + .color-picker__preview { + cursor: copy; + margin-left: 0.75rem; + border-radius: 50%; + } + .color-picker__trigger[disabled] { + cursor: not-allowed; + opacity: 0.5; + } + .color-picker__preview::before, + .color-picker__trigger::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: inherit; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); + /* We use a custom property in lieu of currentColor because of https://bugs.webkit.org/show_bug.cgi?id=216780 */ + background-color: var(--preview-color); + } + + .color-dropdown__trigger--empty::before { + background-color: transparent; + } + + .color-picker__transparent-bg { + background-image: linear-gradient( + 45deg, + var(--uui-palette-grey) 25%, + transparent 25% + ), + linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), + linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), + linear-gradient(45deg, var(--uui-palette-grey) 25%, transparent 25%); + background-size: 10px 10px; + background-position: 0 0, 0 0, -5px -5px, 5px 5px; + } + + .color-picker__preview-color--copied { + animation: pulse 0.75s; + } + + @keyframes pulse { + 0% { + box-shadow: 0 0 0 0 var(--uui-palette-space-cadet-light); + } + 70% { + box-shadow: 0 0 0 0.5rem transparent; + } + 100% { + box-shadow: 0 0 0 0 transparent; + } + } + + .color-picker__controls { + padding: 0.75rem; + display: flex; + align-items: center; + } + .color-picker__sliders { + flex: 1 1 auto; + } + + uui-color-slider:not(:last-of-type) { + margin-bottom: 1rem; + } + + .color-picker__toggle-format { + min-width: 45px; + --uui-button-font-size: 0.8rem; + } + .color-picker__toggle-format > span { + text-transform: uppercase; + } + + uui-color-swatches { + border-top: solid 1px #d4d4d8; + padding: 0.75rem; + } + + button[slot='trigger'] { + border-radius: 50%; + cursor: pointer; + width: 36px; + height: 36px; + } + + uui-popover { + display: block; + width: 100%; + margin: 5px 0; + } + + uui-input { + /* lower the font size to avoid overflow with hlsa format */ + font-size: 0.85rem; + box-sizing: content-box; + flex: 1; + } + + uui-color-area { + width: 100%; + } + `, + ]; } declare global { diff --git a/packages/uui-color-slider/lib/uui-color-slider.element.ts b/packages/uui-color-slider/lib/uui-color-slider.element.ts index 10f8c47f0..e205f6061 100644 --- a/packages/uui-color-slider/lib/uui-color-slider.element.ts +++ b/packages/uui-color-slider/lib/uui-color-slider.element.ts @@ -28,119 +28,6 @@ export type UUIColorSliderType = 'hue' | 'opacity'; */ @defineElement('uui-color-slider') export class UUIColorSliderElement extends LabelMixin('label', LitElement) { - static styles = [ - css` - :host { - --uui-slider-height: 15px; - --uui-slider-handle-size: 17px; - --uui-slider-background-image: #fff; - --uui-slider-background-size: 100%; - --uui-slider-background-position: top left; - display: block; - } - - :host([type='hue']) { - --uui-slider-background-image: linear-gradient( - to right, - rgb(255, 0, 0) 0%, - rgb(255, 255, 0) 17%, - rgb(0, 255, 0) 33%, - rgb(0, 255, 255) 50%, - rgb(0, 0, 255) 67%, - rgb(255, 0, 255) 83%, - rgb(255, 0, 0) 100% - ); - } - - :host([vertical][type='hue']) { - --uui-slider-background-image: linear-gradient( - to top, - rgb(255, 0, 0) 0%, - rgb(255, 255, 0) 17%, - rgb(0, 255, 0) 33%, - rgb(0, 255, 255) 50%, - rgb(0, 0, 255) 67%, - rgb(255, 0, 255) 83%, - rgb(255, 0, 0) 100% - ); - } - - :host([type='opacity']) { - --uui-slider-background-image: linear-gradient( - 45deg, - var(--uui-palette-grey) 25%, - transparent 25% - ), - linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), - linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), - linear-gradient(45deg, var(--uui-palette-grey) 25%, transparent 25%); - - --uui-slider-background-size: 10px 10px; - --uui-slider-background-position: 0 0, 0 0, -5px -5px, 5px 5px; - } - - #color-slider { - position: relative; - background-image: var(--uui-slider-background-image); - background-size: var(--uui-slider-background-size); - background-position: var(--uui-slider-background-position); - border-radius: 3px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); - width: 100%; - height: var(--uui-slider-height); - } - - :host([vertical]) #color-slider { - width: var(--uui-slider-height); - height: 300px; - } - - :host([disabled]) { - cursor: not-allowed; - } - - :host([disabled]) #color-slider { - user-select: none; - pointer-events: none; - opacity: 0.55; - } - - #color-slider__handle { - position: absolute; - top: calc(50% - var(--uui-slider-handle-size) / 2); - width: var(--uui-slider-handle-size); - height: var(--uui-slider-handle-size); - background-color: white; - border-radius: 50%; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); - margin-left: calc(var(--uui-slider-handle-size) / -2); - left: var(--current-value, 0%); - } - - :host([vertical]) #color-slider__handle { - left: unset; - top: var(--current-value, 100%); - margin-left: -1px; - margin-top: calc(var(--uui-slider-handle-size) / -2); - } - - ::slotted(*:first-child) { - border-radius: 3px; - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: 100%; - } - - #current-hue { - border-radius: 3px; - position: absolute; - inset: 0 0 0 0; - } - `, - ]; - /** * The type of the slider. * @type {UUIColorSliderType} @@ -375,6 +262,119 @@ export class UUIColorSliderElement extends LabelMixin('label', LitElement) { ${Math.round(this.value)}`; } + + static styles = [ + css` + :host { + --uui-slider-height: 15px; + --uui-slider-handle-size: 17px; + --uui-slider-background-image: #fff; + --uui-slider-background-size: 100%; + --uui-slider-background-position: top left; + display: block; + } + + :host([type='hue']) { + --uui-slider-background-image: linear-gradient( + to right, + rgb(255, 0, 0) 0%, + rgb(255, 255, 0) 17%, + rgb(0, 255, 0) 33%, + rgb(0, 255, 255) 50%, + rgb(0, 0, 255) 67%, + rgb(255, 0, 255) 83%, + rgb(255, 0, 0) 100% + ); + } + + :host([vertical][type='hue']) { + --uui-slider-background-image: linear-gradient( + to top, + rgb(255, 0, 0) 0%, + rgb(255, 255, 0) 17%, + rgb(0, 255, 0) 33%, + rgb(0, 255, 255) 50%, + rgb(0, 0, 255) 67%, + rgb(255, 0, 255) 83%, + rgb(255, 0, 0) 100% + ); + } + + :host([type='opacity']) { + --uui-slider-background-image: linear-gradient( + 45deg, + var(--uui-palette-grey) 25%, + transparent 25% + ), + linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), + linear-gradient(45deg, transparent 75%, var(--uui-palette-grey) 75%), + linear-gradient(45deg, var(--uui-palette-grey) 25%, transparent 25%); + + --uui-slider-background-size: 10px 10px; + --uui-slider-background-position: 0 0, 0 0, -5px -5px, 5px 5px; + } + + #color-slider { + position: relative; + background-image: var(--uui-slider-background-image); + background-size: var(--uui-slider-background-size); + background-position: var(--uui-slider-background-position); + border-radius: 3px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); + width: 100%; + height: var(--uui-slider-height); + } + + :host([vertical]) #color-slider { + width: var(--uui-slider-height); + height: 300px; + } + + :host([disabled]) { + cursor: not-allowed; + } + + :host([disabled]) #color-slider { + user-select: none; + pointer-events: none; + opacity: 0.55; + } + + #color-slider__handle { + position: absolute; + top: calc(50% - var(--uui-slider-handle-size) / 2); + width: var(--uui-slider-handle-size); + height: var(--uui-slider-handle-size); + background-color: white; + border-radius: 50%; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25); + margin-left: calc(var(--uui-slider-handle-size) / -2); + left: var(--current-value, 0%); + } + + :host([vertical]) #color-slider__handle { + left: unset; + top: var(--current-value, 100%); + margin-left: -1px; + margin-top: calc(var(--uui-slider-handle-size) / -2); + } + + ::slotted(*:first-child) { + border-radius: 3px; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + } + + #current-hue { + border-radius: 3px; + position: absolute; + inset: 0 0 0 0; + } + `, + ]; } declare global { diff --git a/packages/uui-color-swatch/lib/uui-color-swatch.element.ts b/packages/uui-color-swatch/lib/uui-color-swatch.element.ts index 1b16eb532..d5b728a23 100644 --- a/packages/uui-color-swatch/lib/uui-color-swatch.element.ts +++ b/packages/uui-color-swatch/lib/uui-color-swatch.element.ts @@ -26,6 +26,140 @@ export class UUIColorSwatchElement extends LabelMixin( 'label', SelectableMixin(ActiveMixin(LitElement)) ) { + private _value: string | undefined = ''; + + /** + * Value of the swatch. Should be a valid hex, hexa, rgb, rgba, hsl or hsla string. Should fulfill this [css spec](https://www.w3.org/TR/css-color-4/#color-type). If not provided element will look at its text content. + * + * @attr + */ + @property() + get value(): string { + return this._value ? this._value : this.textContent?.trim() || ''; + } + + set value(newValue: string) { + const oldValue = this._value; + this._value = newValue; + this.requestUpdate('value', oldValue); + } + + /** + * Determines if the options is disabled. If true the option can't be selected + * + * @attr + */ + @property({ type: Boolean, reflect: true }) + disabled = false; + + /** + * When true shows element label below the color checkbox + * + * @attr + * @memberof UUIColorSwatchElement + */ + @property({ type: Boolean, attribute: 'show-label' }) + showLabel = false; + /** + * Colord object instance based on the value provided to the element. If the value is not a valid color, it falls back to black (like Amy Winehouse). For more information about Colord, see [Colord](https://omgovich.github.io/colord/) + * + * @memberof UUIColorSwatchElement + */ + get color(): Colord | null { + return this._color; + } + + set color(_) { + // do nothing, this is just to prevent the color from being set from outside + return; + } + private _color: Colord | null = null; + + /** + * Returns true if the color brightness is >= 0.5 + * + * @readonly + * @memberof UUIColorSwatchElement + */ + get isLight() { + return this.color?.isLight() ?? false; + } + + constructor() { + super(); + this.addEventListener('click', this._setAriaAttributes); + } + + private _initializeColor() { + this._color = new Colord(this.value ?? ''); + if (!this._color.isValid()) { + this.disabled = true; + console.error( + `Invalid color provided to uui-color-swatch: ${this.value}` + ); + } + } + + private _setAriaAttributes() { + if (this.selectable) + this.setAttribute('aria-checked', this.selected.toString()); + } + + firstUpdated() { + this._initializeColor(); + this._setAriaAttributes(); + } + + willUpdate(changedProperties: Map) { + if (changedProperties.has('value')) { + this._initializeColor(); + } + if (changedProperties.has('disabled')) { + if (this.selectable) { + this.selectable = !this.disabled; + this.deselectable = !this.disabled; + } + } + if ( + changedProperties.has('selectable') || + changedProperties.has('selected') + ) { + this._setAriaAttributes(); + } + } + + render() { + return html` + + `; + } + + private _renderWithLabel() { + if (!this.showLabel) return nothing; + return html`
+ ${this.renderLabel()} + ${this.value} +
`; + } + static styles = [ css` :host { @@ -191,140 +325,6 @@ export class UUIColorSwatchElement extends LabelMixin( } `, ]; - - private _value: string | undefined = ''; - - /** - * Value of the swatch. Should be a valid hex, hexa, rgb, rgba, hsl or hsla string. Should fulfill this [css spec](https://www.w3.org/TR/css-color-4/#color-type). If not provided element will look at its text content. - * - * @attr - */ - @property() - get value(): string { - return this._value ? this._value : this.textContent?.trim() || ''; - } - - set value(newValue: string) { - const oldValue = this._value; - this._value = newValue; - this.requestUpdate('value', oldValue); - } - - /** - * Determines if the options is disabled. If true the option can't be selected - * - * @attr - */ - @property({ type: Boolean, reflect: true }) - disabled = false; - - /** - * When true shows element label below the color checkbox - * - * @attr - * @memberof UUIColorSwatchElement - */ - @property({ type: Boolean, attribute: 'show-label' }) - showLabel = false; - /** - * Colord object instance based on the value provided to the element. If the value is not a valid color, it falls back to black (like Amy Winehouse). For more information about Colord, see [Colord](https://omgovich.github.io/colord/) - * - * @memberof UUIColorSwatchElement - */ - get color(): Colord | null { - return this._color; - } - - set color(_) { - // do nothing, this is just to prevent the color from being set from outside - return; - } - private _color: Colord | null = null; - - /** - * Returns true if the color brightness is >= 0.5 - * - * @readonly - * @memberof UUIColorSwatchElement - */ - get isLight() { - return this.color?.isLight() ?? false; - } - - constructor() { - super(); - this.addEventListener('click', this._setAriaAttributes); - } - - private _initializeColor() { - this._color = new Colord(this.value ?? ''); - if (!this._color.isValid()) { - this.disabled = true; - console.error( - `Invalid color provided to uui-color-swatch: ${this.value}` - ); - } - } - - private _setAriaAttributes() { - if (this.selectable) - this.setAttribute('aria-checked', this.selected.toString()); - } - - firstUpdated() { - this._initializeColor(); - this._setAriaAttributes(); - } - - willUpdate(changedProperties: Map) { - if (changedProperties.has('value')) { - this._initializeColor(); - } - if (changedProperties.has('disabled')) { - if (this.selectable) { - this.selectable = !this.disabled; - this.deselectable = !this.disabled; - } - } - if ( - changedProperties.has('selectable') || - changedProperties.has('selected') - ) { - this._setAriaAttributes(); - } - } - - render() { - return html` - - `; - } - - private _renderWithLabel() { - if (!this.showLabel) return nothing; - return html`
- ${this.renderLabel()} - ${this.value} -
`; - } } declare global { diff --git a/packages/uui-color-swatches/lib/uui-color-swatches.element.ts b/packages/uui-color-swatches/lib/uui-color-swatches.element.ts index 856994d5c..50b9ba20e 100644 --- a/packages/uui-color-swatches/lib/uui-color-swatches.element.ts +++ b/packages/uui-color-swatches/lib/uui-color-swatches.element.ts @@ -17,16 +17,6 @@ import { UUIColorSwatchesEvent } from './UUIColorSwatchesEvent'; */ @defineElement('uui-color-swatches') export class UUIColorSwatchesElement extends LabelMixin('label', LitElement) { - static styles = [ - css` - :host { - display: flex; - flex-wrap: wrap; - gap: 0.4rem; - } - `, - ]; - /** * Value of selected option. * @@ -35,13 +25,13 @@ export class UUIColorSwatchesElement extends LabelMixin('label', LitElement) { @property() value = ''; - /** - * Disables the color swatches. - * @type {boolean} - * @attr - * @default false - **/ - @property({ type: Boolean, reflect: true }) disabled = false; + /** + * Disables the color swatches. + * @type {boolean} + * @attr + * @default false + **/ + @property({ type: Boolean, reflect: true }) disabled = false; @queryAssignedElements({ selector: 'uui-color-swatch' }) swatches!: Array; @@ -90,9 +80,8 @@ export class UUIColorSwatchesElement extends LabelMixin('label', LitElement) { swatch.setAttribute('role', 'radio'); if (this.disabled) { - swatch.setAttribute('disabled', '') - } - else { + swatch.setAttribute('disabled', ''); + } else { // For some reason the value it really wants the attribute to be set not the value. If value is set then it is not reflected properly. :cry: swatch.setAttribute('selectable', 'selectable'); } @@ -141,7 +130,7 @@ export class UUIColorSwatchesElement extends LabelMixin('label', LitElement) { ); } }; - + /** * Deselects all swatches. * @@ -161,6 +150,16 @@ export class UUIColorSwatchesElement extends LabelMixin('label', LitElement) { render() { return html``; } + + static styles = [ + css` + :host { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + } + `, + ]; } declare global { diff --git a/packages/uui-combobox-list/lib/uui-combobox-list-option.element.ts b/packages/uui-combobox-list/lib/uui-combobox-list-option.element.ts index f4a655b99..bd1d4c908 100644 --- a/packages/uui-combobox-list/lib/uui-combobox-list-option.element.ts +++ b/packages/uui-combobox-list/lib/uui-combobox-list-option.element.ts @@ -12,6 +12,74 @@ import { ActiveMixin, SelectableMixin } from '@umbraco-ui/uui-base/lib/mixins'; export class UUIComboboxListOptionElement extends SelectableMixin( ActiveMixin(LitElement) ) { + private _value: string | undefined; + + @state() + private _disabled = false; + + @state() _displayValue = ''; + + /** + * Value of the option. + * @type { string } + * @attr + * @default "" + */ + @property({ type: String }) + public get value(): string { + return this._value ? this._value : this.textContent?.trim() || ''; + } + public set value(newValue: string) { + const oldValue = this._value; + this._value = newValue; + this.requestUpdate('value', oldValue); + } + + /** + * A readable value. + * @type { string } + * @attr + * @default "" + */ + @property({ type: String, attribute: 'display-value' }) + public get displayValue() { + return this._displayValue + ? this._displayValue + : this.textContent?.trim() || ''; + } + public set displayValue(newValue) { + const oldValue = this._displayValue; + this._displayValue = newValue; + this.requestUpdate('displayValue', oldValue); + } + + /** + * Determines if the options is disabled. If true the option can't be selected + * @type { boolean } + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + public get disabled() { + return this._disabled; + } + public set disabled(newValue) { + const oldValue = this._disabled; + this._disabled = newValue; + this.selectable = !this._disabled; + this.requestUpdate('disabled', oldValue); + } + + constructor() { + super(); + this.selectable = true; + this.deselectable = false; + } + + render() { + return html``; + } + static styles = [ css` :host { @@ -84,74 +152,6 @@ export class UUIComboboxListOptionElement extends SelectableMixin( } `, ]; - - private _value: string | undefined; - - @state() - private _disabled = false; - - @state() _displayValue = ''; - - /** - * Value of the option. - * @type { string } - * @attr - * @default "" - */ - @property({ type: String }) - public get value(): string { - return this._value ? this._value : this.textContent?.trim() || ''; - } - public set value(newValue: string) { - const oldValue = this._value; - this._value = newValue; - this.requestUpdate('value', oldValue); - } - - /** - * A readable value. - * @type { string } - * @attr - * @default "" - */ - @property({ type: String, attribute: 'display-value' }) - public get displayValue() { - return this._displayValue - ? this._displayValue - : this.textContent?.trim() || ''; - } - public set displayValue(newValue) { - const oldValue = this._displayValue; - this._displayValue = newValue; - this.requestUpdate('displayValue', oldValue); - } - - /** - * Determines if the options is disabled. If true the option can't be selected - * @type { boolean } - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - public get disabled() { - return this._disabled; - } - public set disabled(newValue) { - const oldValue = this._disabled; - this._disabled = newValue; - this.selectable = !this._disabled; - this.requestUpdate('disabled', oldValue); - } - - constructor() { - super(); - this.selectable = true; - this.deselectable = false; - } - - render() { - return html``; - } } declare global { diff --git a/packages/uui-combobox-list/lib/uui-combobox-list.element.ts b/packages/uui-combobox-list/lib/uui-combobox-list.element.ts index e00bb4f92..b2c0746a7 100644 --- a/packages/uui-combobox-list/lib/uui-combobox-list.element.ts +++ b/packages/uui-combobox-list/lib/uui-combobox-list.element.ts @@ -13,16 +13,6 @@ import { UUIComboboxListEvent } from './UUIComboboxListEvent'; */ @defineElement('uui-combobox-list') export class UUIComboboxListElement extends LitElement { - static styles = [ - css` - :host { - display: flex; - flex-direction: column; - box-sizing: border-box; - } - `, - ]; - /** * Value of selected option. * @type { FormDataEntryValue | FormData } @@ -250,6 +240,16 @@ export class UUIComboboxListElement extends LitElement { render() { return html` `; } + + static styles = [ + css` + :host { + display: flex; + flex-direction: column; + box-sizing: border-box; + } + `, + ]; } declare global { diff --git a/packages/uui-combobox/lib/uui-combobox-async-example.ts b/packages/uui-combobox/lib/uui-combobox-async-example.ts index 33704c829..6bf5334b9 100644 --- a/packages/uui-combobox/lib/uui-combobox-async-example.ts +++ b/packages/uui-combobox/lib/uui-combobox-async-example.ts @@ -28,18 +28,6 @@ const data: Array = [ @customElement('uui-combobox-async-example') export class UUIComboboxAsyncExampleElement extends LitElement { - static styles = [ - css` - #loader { - position: absolute; - } - - .help { - padding: var(--uui-size-4); - } - `, - ]; - @state() _options: any[] = []; @@ -101,4 +89,16 @@ export class UUIComboboxAsyncExampleElement extends LitElement { `; } + + static styles = [ + css` + #loader { + position: absolute; + } + + .help { + padding: var(--uui-size-4); + } + `, + ]; } diff --git a/packages/uui-combobox/lib/uui-combobox.element.ts b/packages/uui-combobox/lib/uui-combobox.element.ts index 91f595c3c..9751c8a42 100644 --- a/packages/uui-combobox/lib/uui-combobox.element.ts +++ b/packages/uui-combobox/lib/uui-combobox.element.ts @@ -27,78 +27,6 @@ import { UUIComboboxEvent } from './UUIComboboxEvent'; */ @defineElement('uui-combobox') export class UUIComboboxElement extends FormControlMixin(LitElement) { - static styles = [ - css` - :host { - display: inline-block; - } - - #combobox-input { - width: 100%; - border-radius: var(--uui-size-1); - } - - #scroll-container { - overflow-y: auto; - width: 100%; - max-height: var(--uui-combobox-popover-max-height, 500px); - } - - #dropdown { - overflow: hidden; - z-index: -1; - background-color: var( - --uui-combobox-popover-background-color, - var(--uui-color-surface) - ); - border: 1px solid var(--uui-color-border); - border-radius: var(--uui-border-radius); - width: 100%; - height: 100%; - box-sizing: border-box; - box-shadow: var(--uui-shadow-depth-3); - } - - #caret { - margin-right: var(--uui-size-3, 9px); - display: flex; - width: 1.15em; - flex-shrink: 0; - margin-top: -1px; - } - - :host([disabled]) #caret { - fill: var(--uui-color-disabled-contrast); - } - - #phone-wrapper { - position: fixed; - inset: 0; - display: flex; - flex-direction: column; - z-index: 1; - font-size: 1.1em; - } - - #phone-wrapper #dropdown { - display: flex; - } - - #phone-wrapper #combobox-input { - height: var(--uui-size-16); - } - - #phone-wrapper > uui-button { - height: var(--uui-size-14); - width: 100%; - } - - #phone-wrapper #scroll-container { - max-height: unset; - } - `, - ]; - @property({ attribute: 'value', reflect: true }) get value() { return this._value; @@ -384,6 +312,78 @@ export class UUIComboboxElement extends FormControlMixin(LitElement) { `; } } + + static styles = [ + css` + :host { + display: inline-block; + } + + #combobox-input { + width: 100%; + border-radius: var(--uui-size-1); + } + + #scroll-container { + overflow-y: auto; + width: 100%; + max-height: var(--uui-combobox-popover-max-height, 500px); + } + + #dropdown { + overflow: hidden; + z-index: -1; + background-color: var( + --uui-combobox-popover-background-color, + var(--uui-color-surface) + ); + border: 1px solid var(--uui-color-border); + border-radius: var(--uui-border-radius); + width: 100%; + height: 100%; + box-sizing: border-box; + box-shadow: var(--uui-shadow-depth-3); + } + + #caret { + margin-right: var(--uui-size-3, 9px); + display: flex; + width: 1.15em; + flex-shrink: 0; + margin-top: -1px; + } + + :host([disabled]) #caret { + fill: var(--uui-color-disabled-contrast); + } + + #phone-wrapper { + position: fixed; + inset: 0; + display: flex; + flex-direction: column; + z-index: 1; + font-size: 1.1em; + } + + #phone-wrapper #dropdown { + display: flex; + } + + #phone-wrapper #combobox-input { + height: var(--uui-size-16); + } + + #phone-wrapper > uui-button { + height: var(--uui-size-14); + width: 100%; + } + + #phone-wrapper #scroll-container { + max-height: unset; + } + `, + ]; } declare global { diff --git a/packages/uui-dialog-layout/lib/uui-dialog-layout.element.ts b/packages/uui-dialog-layout/lib/uui-dialog-layout.element.ts index 30a4c6f65..86ad0dcd9 100644 --- a/packages/uui-dialog-layout/lib/uui-dialog-layout.element.ts +++ b/packages/uui-dialog-layout/lib/uui-dialog-layout.element.ts @@ -11,23 +11,6 @@ import { property, state } from 'lit/decorators.js'; */ @defineElement('uui-dialog-layout') export class UUIDialogLayoutElement extends LitElement { - static styles = [ - css` - :host { - display: block; - padding: var(--uui-size-10) var(--uui-size-14); - color: var(--uui-color-text); - } - - #actions { - margin-top: var(--uui-size-8); - display: flex; - justify-content: end; - gap: var(--uui-size-4); - } - `, - ]; - /** * Headline for this notification, can also be set via the 'headline' slot. * @type string @@ -85,4 +68,21 @@ export class UUIDialogLayoutElement extends LitElement { return html`${this.renderHeadline()} ${this.renderContent()} ${this.renderActions()} `; } + + static styles = [ + css` + :host { + display: block; + padding: var(--uui-size-10) var(--uui-size-14); + color: var(--uui-color-text); + } + + #actions { + margin-top: var(--uui-size-8); + display: flex; + justify-content: end; + gap: var(--uui-size-4); + } + `, + ]; } diff --git a/packages/uui-dialog/lib/uui-dialog.element.ts b/packages/uui-dialog/lib/uui-dialog.element.ts index 06c96dc9d..9fe273502 100644 --- a/packages/uui-dialog/lib/uui-dialog.element.ts +++ b/packages/uui-dialog/lib/uui-dialog.element.ts @@ -8,6 +8,10 @@ import { css, html, LitElement } from 'lit'; */ @defineElement('uui-dialog') export class UUIDialogElement extends LitElement { + render() { + return html``; + } + static styles = [ css` :host { @@ -29,10 +33,6 @@ export class UUIDialogElement extends LitElement { } `, ]; - - render() { - return html``; - } } declare global { diff --git a/packages/uui-file-dropzone/lib/uui-file-dropzone.element.ts b/packages/uui-file-dropzone/lib/uui-file-dropzone.element.ts index 8f53c1c1a..026f4e141 100644 --- a/packages/uui-file-dropzone/lib/uui-file-dropzone.element.ts +++ b/packages/uui-file-dropzone/lib/uui-file-dropzone.element.ts @@ -13,48 +13,6 @@ import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils'; */ @defineElement('uui-file-dropzone') export class UUIFileDropzoneElement extends LabelMixin('', LitElement) { - static styles = [ - css` - #dropzone { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - padding: var(--uui-size-4); - border: 3px solid transparent; - margin: -3px; - backdrop-filter: blur(2px); - } - #dropzone.hover { - border-color: var(--uui-color-default); - } - #dropzone.hover::before { - content: ''; - position: absolute; - inset: 0; - opacity: 0.2; - border-color: var(--uui-color-default); - background-color: var(--uui-color-default); - } - #symbol { - color: var(--uui-color-default); - max-width: 100%; - max-height: 100%; - } - #input { - position: absolute; - width: 0px; - height: 0px; - opacity: 0; - display: none; - } - `, - ]; - @query('#input') private _input!: HTMLInputElement; @@ -282,6 +240,48 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) { `; } + + static styles = [ + css` + #dropzone { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + box-sizing: border-box; + width: 100%; + height: 100%; + padding: var(--uui-size-4); + border: 3px solid transparent; + margin: -3px; + backdrop-filter: blur(2px); + } + #dropzone.hover { + border-color: var(--uui-color-default); + } + #dropzone.hover::before { + content: ''; + position: absolute; + inset: 0; + opacity: 0.2; + border-color: var(--uui-color-default); + background-color: var(--uui-color-default); + } + #symbol { + color: var(--uui-color-default); + max-width: 100%; + max-height: 100%; + } + #input { + position: absolute; + width: 0px; + height: 0px; + opacity: 0; + display: none; + } + `, + ]; } declare global { diff --git a/packages/uui-file-preview/lib/uui-file-preview.element.ts b/packages/uui-file-preview/lib/uui-file-preview.element.ts index ad9311799..464932db5 100644 --- a/packages/uui-file-preview/lib/uui-file-preview.element.ts +++ b/packages/uui-file-preview/lib/uui-file-preview.element.ts @@ -12,78 +12,6 @@ import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils'; */ @defineElement('uui-file-preview') export class UUIFilePreviewElement extends LitElement { - static styles = [ - css` - :host { - position: relative; - display: grid; - /* These have to be changed together */ - grid-template-rows: 100px 50px; - width: 150px; - height: 150px; - /* --------------------------------- */ - box-sizing: border-box; - aspect-ratio: 1; - color: var(--uui-color-text); - } - - :host(:hover) slot[name='actions']::slotted(*) { - opacity: 1; - } - - slot[name='actions']::slotted(*) { - position: absolute; - top: 8px; - right: 8px; - max-width: 100%; - height: 32px; - font-size: 13px; - opacity: 0; - z-index: 1; - transition: opacity 150ms; - } - - #file-symbol { - aspect-ratio: 1 / 1; - margin: auto; - max-width: 100%; - max-height: 100%; - } - - #file-info { - min-width: 0; - display: flex; - text-align: center; - flex-direction: column; - font-size: 1rem; - } - - #file-name { - display: flex; - justify-content: center; - } - - #file-name-start { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - #file-name-end { - white-space: nowrap; - } - - #file-size { - opacity: 0.6; - } - - .has-source:hover { - text-decoration: underline; - cursor: pointer; - } - `, - ]; - /** * Name of the file. * @type {string} @@ -245,6 +173,78 @@ export class UUIFilePreviewElement extends LitElement { `; } + + static styles = [ + css` + :host { + position: relative; + display: grid; + /* These have to be changed together */ + grid-template-rows: 100px 50px; + width: 150px; + height: 150px; + /* --------------------------------- */ + box-sizing: border-box; + aspect-ratio: 1; + color: var(--uui-color-text); + } + + :host(:hover) slot[name='actions']::slotted(*) { + opacity: 1; + } + + slot[name='actions']::slotted(*) { + position: absolute; + top: 8px; + right: 8px; + max-width: 100%; + height: 32px; + font-size: 13px; + opacity: 0; + z-index: 1; + transition: opacity 150ms; + } + + #file-symbol { + aspect-ratio: 1 / 1; + margin: auto; + max-width: 100%; + max-height: 100%; + } + + #file-info { + min-width: 0; + display: flex; + text-align: center; + flex-direction: column; + font-size: 1rem; + } + + #file-name { + display: flex; + justify-content: center; + } + + #file-name-start { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + #file-name-end { + white-space: nowrap; + } + + #file-size { + opacity: 0.6; + } + + .has-source:hover { + text-decoration: underline; + cursor: pointer; + } + `, + ]; } declare global { diff --git a/packages/uui-form-layout-item/lib/uui-form-layout-item.element.ts b/packages/uui-form-layout-item/lib/uui-form-layout-item.element.ts index 56849222a..5d5f29a2a 100644 --- a/packages/uui-form-layout-item/lib/uui-form-layout-item.element.ts +++ b/packages/uui-form-layout-item/lib/uui-form-layout-item.element.ts @@ -16,28 +16,6 @@ import { property, state } from 'lit/decorators.js'; @defineElement('uui-form-layout-item') export class UUIFormLayoutItemElement extends LitElement { - static styles = [ - css` - :host { - position: relative; - display: block; - margin-top: var(--uui-size-space-5); - margin-bottom: var(--uui-size-space-5); - } - #label { - margin-top: -5px; - margin-bottom: 5px; - } - #description { - color: var(--uui-color-disabled-contrast); - font-size: var(--uui-type-small-size); - } - #label + #description { - margin-top: -8px; - min-height: 8px; - } - `, - ]; /* @property({type: String}) label: string | null = null; @@ -89,6 +67,29 @@ export class UUIFormLayoutItemElement extends LitElement { `; } + + static styles = [ + css` + :host { + position: relative; + display: block; + margin-top: var(--uui-size-space-5); + margin-bottom: var(--uui-size-space-5); + } + #label { + margin-top: -5px; + margin-bottom: 5px; + } + #description { + color: var(--uui-color-disabled-contrast); + font-size: var(--uui-type-small-size); + } + #label + #description { + margin-top: -8px; + min-height: 8px; + } + `, + ]; } declare global { diff --git a/packages/uui-form-validation-message/lib/uui-form-validation-message.element.ts b/packages/uui-form-validation-message/lib/uui-form-validation-message.element.ts index 76f885e8b..4669b1bd7 100644 --- a/packages/uui-form-validation-message/lib/uui-form-validation-message.element.ts +++ b/packages/uui-form-validation-message/lib/uui-form-validation-message.element.ts @@ -16,14 +16,6 @@ import { repeat } from 'lit/directives/repeat.js'; @defineElement('uui-form-validation-message') export class UUIFormValidationMessageElement extends LitElement { - static styles = [ - css` - #messages { - color: var(--uui-color-danger-standalone); - } - `, - ]; - /** * Set the element containing Form Controls of interest. * @type {string} @@ -103,6 +95,14 @@ export class UUIFormValidationMessageElement extends LitElement { `; } + + static styles = [ + css` + #messages { + color: var(--uui-color-danger-standalone); + } + `, + ]; } declare global { interface HTMLElementTagNameMap { diff --git a/packages/uui-icon/lib/uui-icon.element.ts b/packages/uui-icon/lib/uui-icon.element.ts index 1179cab53..4b20fd52b 100644 --- a/packages/uui-icon/lib/uui-icon.element.ts +++ b/packages/uui-icon/lib/uui-icon.element.ts @@ -13,30 +13,6 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js'; */ @defineElement('uui-icon') export class UUIIconElement extends LitElement { - static styles = [ - css` - :host { - display: inline-block; - vertical-align: bottom; - width: 1.15em; - height: 1.15em; - } - - :host svg, - ::slotted(svg) { - fill: var(--uui-icon-color, currentColor); - } - - :host-context(div[slot='prepend']) { - margin-left: var(--uui-size-2, 6px); - } - - :host-context(div[slot='append']) { - margin-right: var(--uui-size-2, 6px); - } - `, - ]; - private _name: string | null = null; private _retrievedNameIcon: boolean = false; @@ -137,6 +113,30 @@ export class UUIIconElement extends LitElement { return html``; } + + static styles = [ + css` + :host { + display: inline-block; + vertical-align: bottom; + width: 1.15em; + height: 1.15em; + } + + :host svg, + ::slotted(svg) { + fill: var(--uui-icon-color, currentColor); + } + + :host-context(div[slot='prepend']) { + margin-left: var(--uui-size-2, 6px); + } + + :host-context(div[slot='append']) { + margin-right: var(--uui-size-2, 6px); + } + `, + ]; } declare global { diff --git a/packages/uui-input-file/lib/uui-input-file.element.ts b/packages/uui-input-file/lib/uui-input-file.element.ts index 8a187af0e..5edd225be 100644 --- a/packages/uui-input-file/lib/uui-input-file.element.ts +++ b/packages/uui-input-file/lib/uui-input-file.element.ts @@ -15,59 +15,6 @@ import { repeat } from 'lit/directives/repeat.js'; */ @defineElement('uui-input-file') export class UUIInputFileElement extends FormControlMixin(LitElement) { - static styles = [ - css` - :host { - width: 100%; - height: 100%; - position: relative; - display: flex; - box-sizing: border-box; - border: 1px solid var(--uui-color-border); - } - - #input { - position: absolute; - width: 0px; - height: 0px; - opacity: 0; - display: none; - } - - #files { - display: grid; - box-sizing: border-box; - justify-items: center; - width: 100%; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - grid-auto-rows: min-content; - gap: 16px; - padding: 16px; - overflow: auto; - max-height: 100%; - } - - #dropzone { - display: none; - position: absolute; - inset: 0px; - z-index: 10; - justify-content: center; - align-items: center; - } - - #add-button { - width: 150px; - height: 150px; - display: flex; - padding: 16px; - box-sizing: border-box; - justify-content: center; - align-items: stretch; - } - `, - ]; - @query('#dropzone') private _dropzone!: UUIFileDropzoneElement; @@ -276,6 +223,59 @@ export class UUIInputFileElement extends FormControlMixin(LitElement) { `; } + + static styles = [ + css` + :host { + width: 100%; + height: 100%; + position: relative; + display: flex; + box-sizing: border-box; + border: 1px solid var(--uui-color-border); + } + + #input { + position: absolute; + width: 0px; + height: 0px; + opacity: 0; + display: none; + } + + #files { + display: grid; + box-sizing: border-box; + justify-items: center; + width: 100%; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + grid-auto-rows: min-content; + gap: 16px; + padding: 16px; + overflow: auto; + max-height: 100%; + } + + #dropzone { + display: none; + position: absolute; + inset: 0px; + z-index: 10; + justify-content: center; + align-items: center; + } + + #add-button { + width: 150px; + height: 150px; + display: flex; + padding: 16px; + box-sizing: border-box; + justify-content: center; + align-items: stretch; + } + `, + ]; } declare global { diff --git a/packages/uui-input-lock/lib/uui-input-lock.element.ts b/packages/uui-input-lock/lib/uui-input-lock.element.ts index e55efb1c4..aeb42d019 100644 --- a/packages/uui-input-lock/lib/uui-input-lock.element.ts +++ b/packages/uui-input-lock/lib/uui-input-lock.element.ts @@ -14,24 +14,6 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-input-lock') export class UUIInputLockElement extends UUIInputElement { - static styles = [ - ...UUIInputElement.styles, - css` - #lock { - height: 100%; - - --uui-button-padding-left-factor: 0.75; - --uui-button-padding-right-factor: 0.75; - font-size: 12px; - } - - :host([locked]) #input { - cursor: not-allowed; - opacity: 0.55; - } - `, - ]; - /** * Determine the inputs locked state. * @type {boolean} @@ -75,6 +57,24 @@ export class UUIInputLockElement extends UUIInputElement { ${this.renderIcon()}
`; } + + static styles = [ + ...UUIInputElement.styles, + css` + #lock { + height: 100%; + + --uui-button-padding-left-factor: 0.75; + --uui-button-padding-right-factor: 0.75; + font-size: 12px; + } + + :host([locked]) #input { + cursor: not-allowed; + opacity: 0.55; + } + `, + ]; } declare global { diff --git a/packages/uui-input-password/lib/uui-input-password.element.ts b/packages/uui-input-password/lib/uui-input-password.element.ts index 1cd14537f..4d1407b03 100644 --- a/packages/uui-input-password/lib/uui-input-password.element.ts +++ b/packages/uui-input-password/lib/uui-input-password.element.ts @@ -14,20 +14,6 @@ import { property, state } from 'lit/decorators.js'; */ @defineElement('uui-input-password') export class UUIInputPasswordElement extends UUIInputElement { - static styles = [ - ...UUIInputElement.styles, - css` - #eye { - height: 100%; - margin-left: -6px; - } - - #clear:hover { - color: black; - } - `, - ]; - @state() private passwordType: InputType = 'password'; @@ -78,6 +64,20 @@ export class UUIInputPasswordElement extends UUIInputElement { ${this.renderIcon()} `; } + + static styles = [ + ...UUIInputElement.styles, + css` + #eye { + height: 100%; + margin-left: -6px; + } + + #clear:hover { + color: black; + } + `, + ]; } declare global { diff --git a/packages/uui-input/lib/uui-input.element.ts b/packages/uui-input/lib/uui-input.element.ts index c909d53ae..f80871404 100644 --- a/packages/uui-input/lib/uui-input.element.ts +++ b/packages/uui-input/lib/uui-input.element.ts @@ -41,146 +41,6 @@ export class UUIInputElement extends FormControlMixin( */ static readonly formAssociated = true; - static styles = [ - css` - :host { - position: relative; - display: inline-flex; - align-items: stretch; - height: var(--uui-size-11); - text-align: left; - box-sizing: border-box; - background-color: var( - --uui-input-background-color, - var(--uui-color-surface) - ); - border: var(--uui-input-border-width, 1px) solid - var(--uui-input-border-color, var(--uui-color-border)); - - --uui-button-height: 100%; - --auto-width-text-margin-right: 0; - --auto-width-text-margin-left: 0; - } - - #control { - position: relative; - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: center; - } - - #auto { - border: 0 1px solid transparent; - visibility: hidden; - white-space: pre; - z-index: -1; - height: 0px; - padding: 0 var(--uui-size-space-3); - margin: 0 var(--auto-width-text-margin-right) 0 - var(--auto-width-text-margin-left); - } - - :host([auto-width]) #input { - width: 10px; - min-width: 100%; - } - - :host(:hover) { - border-color: var( - --uui-input-border-color-hover, - var(--uui-color-border-standalone) - ); - } - /* TODO: Fix so we dont get double outline when there is focus on things in the slot. */ - :host(:focus-within) { - border-color: var( - --uui-input-border-color-focus, - var(--uui-color-border-emphasis) - ); - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } - :host(:focus) { - border-color: var( - --uui-input-border-color-focus, - var(--uui-color-border-emphasis) - ); - } - :host([disabled]) { - background-color: var( - --uui-input-background-color-disabled, - var(--uui-color-disabled) - ); - border-color: var( - --uui-input-border-color-disabled, - var(--uui-color-disabled) - ); - - color: var(--uui-color-disabled-contrast); - } - :host([disabled]) input { - -webkit-text-fill-color: var( - --uui-color-disabled-contrast - ); /* required on Safari and IOS */ - } - :host([readonly]) { - background-color: var( - --uui-input-background-color-readonly, - var(--uui-color-disabled) - ); - border-color: var( - --uui-input-border-color-readonly, - var(--uui-color-disabled-standalone) - ); - } - - :host(:not([pristine]):invalid), - /* polyfill support */ - :host(:not([pristine])[internals-invalid]) { - border-color: var(--uui-color-danger); - } - - input { - font-family: inherit; - padding: var(--uui-size-1) var(--uui-size-space-3); - font-size: inherit; - color: inherit; - border-radius: 0; - box-sizing: border-box; - border: none; - background: none; - width: 100%; - text-align: inherit; - outline: none; - } - - input::placeholder { - transition: opacity 120ms; - } - - input[type='password']::-ms-reveal { - display: none; - } - - :host(:not([readonly])) input:focus::placeholder { - opacity: 0; - } - - /* TODO: make sure color looks good, or remove it as an option as we want to provide color-picker component */ - input[type='color'] { - width: 30px; - padding: 0; - border: none; - } - - ::slotted(uui-input) { - height: 100%; - --uui-input-border-width: 0; - } - `, - ]; - /** * Sets the min value of the input. * Examples: the first date the user may pick in date and datetime-local, or the min numeric value the user can pick in a number input. @@ -453,6 +313,146 @@ export class UUIInputElement extends FormControlMixin( ? this.value : this.placeholder}`; } + + static styles = [ + css` + :host { + position: relative; + display: inline-flex; + align-items: stretch; + height: var(--uui-size-11); + text-align: left; + box-sizing: border-box; + background-color: var( + --uui-input-background-color, + var(--uui-color-surface) + ); + border: var(--uui-input-border-width, 1px) solid + var(--uui-input-border-color, var(--uui-color-border)); + + --uui-button-height: 100%; + --auto-width-text-margin-right: 0; + --auto-width-text-margin-left: 0; + } + + #control { + position: relative; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: center; + } + + #auto { + border: 0 1px solid transparent; + visibility: hidden; + white-space: pre; + z-index: -1; + height: 0px; + padding: 0 var(--uui-size-space-3); + margin: 0 var(--auto-width-text-margin-right) 0 + var(--auto-width-text-margin-left); + } + + :host([auto-width]) #input { + width: 10px; + min-width: 100%; + } + + :host(:hover) { + border-color: var( + --uui-input-border-color-hover, + var(--uui-color-border-standalone) + ); + } + /* TODO: Fix so we dont get double outline when there is focus on things in the slot. */ + :host(:focus-within) { + border-color: var( + --uui-input-border-color-focus, + var(--uui-color-border-emphasis) + ); + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + :host(:focus) { + border-color: var( + --uui-input-border-color-focus, + var(--uui-color-border-emphasis) + ); + } + :host([disabled]) { + background-color: var( + --uui-input-background-color-disabled, + var(--uui-color-disabled) + ); + border-color: var( + --uui-input-border-color-disabled, + var(--uui-color-disabled) + ); + + color: var(--uui-color-disabled-contrast); + } + :host([disabled]) input { + -webkit-text-fill-color: var( + --uui-color-disabled-contrast + ); /* required on Safari and IOS */ + } + :host([readonly]) { + background-color: var( + --uui-input-background-color-readonly, + var(--uui-color-disabled) + ); + border-color: var( + --uui-input-border-color-readonly, + var(--uui-color-disabled-standalone) + ); + } + + :host(:not([pristine]):invalid), + /* polyfill support */ + :host(:not([pristine])[internals-invalid]) { + border-color: var(--uui-color-danger); + } + + input { + font-family: inherit; + padding: var(--uui-size-1) var(--uui-size-space-3); + font-size: inherit; + color: inherit; + border-radius: 0; + box-sizing: border-box; + border: none; + background: none; + width: 100%; + text-align: inherit; + outline: none; + } + + input::placeholder { + transition: opacity 120ms; + } + + input[type='password']::-ms-reveal { + display: none; + } + + :host(:not([readonly])) input:focus::placeholder { + opacity: 0; + } + + /* TODO: make sure color looks good, or remove it as an option as we want to provide color-picker component */ + input[type='color'] { + width: 30px; + padding: 0; + border: none; + } + + ::slotted(uui-input) { + height: 100%; + --uui-input-border-width: 0; + } + `, + ]; } declare global { diff --git a/packages/uui-keyboard-shortcut/lib/uui-key.element.ts b/packages/uui-keyboard-shortcut/lib/uui-key.element.ts index 47c31bd23..deaab3384 100644 --- a/packages/uui-keyboard-shortcut/lib/uui-key.element.ts +++ b/packages/uui-keyboard-shortcut/lib/uui-key.element.ts @@ -8,6 +8,10 @@ import { css, html, LitElement } from 'lit'; */ @defineElement('uui-key') export class UUIKeyElement extends LitElement { + render() { + return html``; + } + static styles = [ css` :host { @@ -25,10 +29,6 @@ export class UUIKeyElement extends LitElement { } `, ]; - - render() { - return html``; - } } declare global { diff --git a/packages/uui-keyboard-shortcut/lib/uui-keyboard-shortcut.element.ts b/packages/uui-keyboard-shortcut/lib/uui-keyboard-shortcut.element.ts index bcdae7609..e87ed13fe 100644 --- a/packages/uui-keyboard-shortcut/lib/uui-keyboard-shortcut.element.ts +++ b/packages/uui-keyboard-shortcut/lib/uui-keyboard-shortcut.element.ts @@ -8,6 +8,10 @@ import { css, html, LitElement } from 'lit'; */ @defineElement('uui-keyboard-shortcut') export class UUIKeyboardShortcutElement extends LitElement { + render() { + return html``; + } + static styles = [ css` :host { @@ -26,10 +30,6 @@ export class UUIKeyboardShortcutElement extends LitElement { } `, ]; - - render() { - return html``; - } } declare global { diff --git a/packages/uui-label/lib/uui-label.element.ts b/packages/uui-label/lib/uui-label.element.ts index 2d322afc5..6ab279966 100644 --- a/packages/uui-label/lib/uui-label.element.ts +++ b/packages/uui-label/lib/uui-label.element.ts @@ -9,25 +9,6 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-label') export class UUILabelElement extends LitElement { - static styles = [ - css` - :host { - font-weight: 700; - } - :host([for]) { - cursor: pointer; - } - :host([disabled]) { - cursor: default; - } - #required { - display: inline; - color: var(--uui-color-danger); - font-weight: 900; - } - `, - ]; - /** * Disables the label. * @type {boolean} @@ -84,6 +65,25 @@ export class UUILabelElement extends LitElement { ${this.required ? html`
*
` : ''} `; } + + static styles = [ + css` + :host { + font-weight: 700; + } + :host([for]) { + cursor: pointer; + } + :host([disabled]) { + cursor: default; + } + #required { + display: inline; + color: var(--uui-color-danger); + font-weight: 900; + } + `, + ]; } declare global { diff --git a/packages/uui-loader-bar/lib/uui-loader-bar.element.ts b/packages/uui-loader-bar/lib/uui-loader-bar.element.ts index 8f169b3ca..405b4af6d 100644 --- a/packages/uui-loader-bar/lib/uui-loader-bar.element.ts +++ b/packages/uui-loader-bar/lib/uui-loader-bar.element.ts @@ -11,6 +11,58 @@ const clamp = (num: number, min: number, max: number) => */ @defineElement('uui-loader-bar') export class UUILoaderBarElement extends LitElement { + private _progress = 0; + + /** + * Set this to a number between 0 and 100 to reflect the progress of some operation. When the value is left at 0 loader will looped animation + * @type {number} + * @attr + * @default 0 + */ + @property({ type: Number }) + get progress() { + return this._progress; + } + + set progress(newVal) { + const oldVal = this._progress; + this._progress = clamp(newVal, 0, 100); + this.requestUpdate('progress', oldVal); + } + + private _animationDuration = 1; + /** + * Set this to a number greater then 0 to define the length of loader animation in seconds. Passing 0 or a negative number as a value will set it to 1 second. This is because negative values are illegal in the `animation-duration` css property, and value of 0 just stops the animation. + * @type {number} + * @attr + * @default 1 + */ + @property({ type: Number }) + get animationDuration() { + return this._animationDuration; + } + + set animationDuration(newVal) { + const oldVal = this._animationDuration; + this._animationDuration = newVal >= 0 ? newVal : 1; + this.requestUpdate('animationDuration', oldVal); + } + + render() { + return html` + ${this.progress + ? html`
` + : ''} +
+
+ `; + } + static styles = [ css` :host { @@ -73,58 +125,6 @@ export class UUILoaderBarElement extends LitElement { } `, ]; - - private _progress = 0; - - /** - * Set this to a number between 0 and 100 to reflect the progress of some operation. When the value is left at 0 loader will looped animation - * @type {number} - * @attr - * @default 0 - */ - @property({ type: Number }) - get progress() { - return this._progress; - } - - set progress(newVal) { - const oldVal = this._progress; - this._progress = clamp(newVal, 0, 100); - this.requestUpdate('progress', oldVal); - } - - private _animationDuration = 1; - /** - * Set this to a number greater then 0 to define the length of loader animation in seconds. Passing 0 or a negative number as a value will set it to 1 second. This is because negative values are illegal in the `animation-duration` css property, and value of 0 just stops the animation. - * @type {number} - * @attr - * @default 1 - */ - @property({ type: Number }) - get animationDuration() { - return this._animationDuration; - } - - set animationDuration(newVal) { - const oldVal = this._animationDuration; - this._animationDuration = newVal >= 0 ? newVal : 1; - this.requestUpdate('animationDuration', oldVal); - } - - render() { - return html` - ${this.progress - ? html`
` - : ''} -
-
- `; - } } declare global { diff --git a/packages/uui-loader-circle/lib/uui-loader-circle.element.ts b/packages/uui-loader-circle/lib/uui-loader-circle.element.ts index d71cfe001..278d9362c 100644 --- a/packages/uui-loader-circle/lib/uui-loader-circle.element.ts +++ b/packages/uui-loader-circle/lib/uui-loader-circle.element.ts @@ -9,6 +9,79 @@ import { styleMap } from 'lit/directives/style-map.js'; */ @defineElement('uui-loader-circle') export class UUILoaderCircleElement extends LitElement { + private _circleStyle() { + if (this.progress) { + return { strokeDasharray: `${this.progress} 100` }; + } else { + return { strokeDasharray: '100 100' }; + } + } + + /** + * Sets the progress that loader shows + * @type {number} + * @attr + * @default 0 + */ + @property({ type: Number }) + progress = 0; + + /** + * If true then element displays progress number at bigger sizes + * @type {boolean} + * @attr show-progress + * @default false + */ + @property({ type: Boolean, reflect: true, attribute: 'show-progress' }) + showProgress = false; + + private _resizeObserver = new ResizeObserver(() => this.onResize()); + private _isLarge = false; + + firstUpdated() { + this._resizeObserver.observe(this); + } + + disconnectedCallback() { + this._resizeObserver.disconnect(); + } + + onResize() { + const newIsLarge = this.clientHeight >= 30; + + if (this._isLarge != newIsLarge) { + this._isLarge = newIsLarge; + this.requestUpdate(); + } + } + + protected renderProgress() { + return this._isLarge && this.progress && this.showProgress + ? html`${this.progress}` + : ''; + } + + render() { + return html` + + + + + + + ${this.renderProgress()} + `; + } + static styles = [ css` :host { @@ -97,79 +170,6 @@ export class UUILoaderCircleElement extends LitElement { } `, ]; - - private _circleStyle() { - if (this.progress) { - return { strokeDasharray: `${this.progress} 100` }; - } else { - return { strokeDasharray: '100 100' }; - } - } - - /** - * Sets the progress that loader shows - * @type {number} - * @attr - * @default 0 - */ - @property({ type: Number }) - progress = 0; - - /** - * If true then element displays progress number at bigger sizes - * @type {boolean} - * @attr show-progress - * @default false - */ - @property({ type: Boolean, reflect: true, attribute: 'show-progress' }) - showProgress = false; - - private _resizeObserver = new ResizeObserver(() => this.onResize()); - private _isLarge = false; - - firstUpdated() { - this._resizeObserver.observe(this); - } - - disconnectedCallback() { - this._resizeObserver.disconnect(); - } - - onResize() { - const newIsLarge = this.clientHeight >= 30; - - if (this._isLarge != newIsLarge) { - this._isLarge = newIsLarge; - this.requestUpdate(); - } - } - - protected renderProgress() { - return this._isLarge && this.progress && this.showProgress - ? html`${this.progress}` - : ''; - } - - render() { - return html` - - - - - - - ${this.renderProgress()} - `; - } } declare global { diff --git a/packages/uui-loader/lib/uui-loader.element.ts b/packages/uui-loader/lib/uui-loader.element.ts index a2e0b6757..e6d20661f 100644 --- a/packages/uui-loader/lib/uui-loader.element.ts +++ b/packages/uui-loader/lib/uui-loader.element.ts @@ -7,6 +7,14 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; */ @defineElement('uui-loader') export class UUILoaderElement extends LitElement { + render() { + return html` +
+
+
+ `; + } + static styles = [ css` :host { @@ -50,14 +58,6 @@ export class UUILoaderElement extends LitElement { } `, ]; - - render() { - return html` -
-
-
- `; - } } declare global { diff --git a/packages/uui-menu-item/lib/uui-menu-item.element.ts b/packages/uui-menu-item/lib/uui-menu-item.element.ts index 9f99e3c80..4636a6cd4 100644 --- a/packages/uui-menu-item/lib/uui-menu-item.element.ts +++ b/packages/uui-menu-item/lib/uui-menu-item.element.ts @@ -28,6 +28,171 @@ import { UUIMenuItemEvent } from './UUIMenuItemEvent'; export class UUIMenuItemElement extends SelectOnlyMixin( SelectableMixin(ActiveMixin(LabelMixin('label', LitElement))) ) { + /** + * Disables the menu item, changes the looks of it and prevents it from emitting the click event + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + public disabled = false; + + /** + * Controls if nested items should be shown. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true, attribute: 'show-children' }) + public showChildren = false; + + // TODO: Should this be a getter that just checks on its own if there is any children? + /** + * Shows/hides the caret. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, attribute: 'has-children' }) + public hasChildren = false; + + /** + * Shows/hides the loading indicator + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, attribute: 'loading' }) + public loading = false; + + /** + * Set an href, this will turns the label into a anchor tag. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public href?: string; + + /** + * Set an anchor tag target, only used when using href. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public target?: '_blank' | '_parent' | '_self' | '_top'; + + /** + * Sets the selection mode. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String, attribute: 'select-mode', reflect: true }) + public selectMode?: 'highlight' | 'persisting'; + + @state() + private iconSlotHasContent = false; + + connectedCallback() { + super.connectedCallback(); + if (!this.hasAttribute('role')) this.setAttribute('role', 'menu'); + + demandCustomElement(this, 'uui-symbol-expand'); + demandCustomElement(this, 'uui-loader-bar'); + } + + private _labelButtonChanged = (label?: Element | undefined) => { + this.selectableTarget = label || this; + }; + + private _iconSlotChanged = (e: any): void => { + this.iconSlotHasContent = + (e.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0; + }; + + private _onCaretClicked = () => { + const eventName = this.showChildren + ? UUIMenuItemEvent.HIDE_CHILDREN + : UUIMenuItemEvent.SHOW_CHILDREN; + const event = new UUIMenuItemEvent(eventName, { cancelable: true }); + this.dispatchEvent(event); + + if (event.defaultPrevented) return; + + this.showChildren = !this.showChildren; + }; + + private _onLabelClicked = () => { + const event = new UUIMenuItemEvent(UUIMenuItemEvent.CLICK_LABEL); + this.dispatchEvent(event); + }; + + private _renderLabelInside() { + return html` + ${this.renderLabel()} + `; + } + + private _renderLabelAsAnchor() { + if (this.disabled) { + return html` + ${this._renderLabelInside()} + `; + } + return html` + ${this._renderLabelInside()} + `; + } + + private _renderLabelAsButton() { + return html` `; + } + + render() { + return html` + + ${this.showChildren ? html`` : ''} + `; + } + static styles = [ css` :host { @@ -342,171 +507,6 @@ export class UUIMenuItemElement extends SelectOnlyMixin( } `, ]; - - /** - * Disables the menu item, changes the looks of it and prevents it from emitting the click event - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - public disabled = false; - - /** - * Controls if nested items should be shown. - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true, attribute: 'show-children' }) - public showChildren = false; - - // TODO: Should this be a getter that just checks on its own if there is any children? - /** - * Shows/hides the caret. - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, attribute: 'has-children' }) - public hasChildren = false; - - /** - * Shows/hides the loading indicator - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, attribute: 'loading' }) - public loading = false; - - /** - * Set an href, this will turns the label into a anchor tag. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public href?: string; - - /** - * Set an anchor tag target, only used when using href. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public target?: '_blank' | '_parent' | '_self' | '_top'; - - /** - * Sets the selection mode. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String, attribute: 'select-mode', reflect: true }) - public selectMode?: 'highlight' | 'persisting'; - - @state() - private iconSlotHasContent = false; - - connectedCallback() { - super.connectedCallback(); - if (!this.hasAttribute('role')) this.setAttribute('role', 'menu'); - - demandCustomElement(this, 'uui-symbol-expand'); - demandCustomElement(this, 'uui-loader-bar'); - } - - private _labelButtonChanged = (label?: Element | undefined) => { - this.selectableTarget = label || this; - }; - - private _iconSlotChanged = (e: any): void => { - this.iconSlotHasContent = - (e.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0; - }; - - private _onCaretClicked = () => { - const eventName = this.showChildren - ? UUIMenuItemEvent.HIDE_CHILDREN - : UUIMenuItemEvent.SHOW_CHILDREN; - const event = new UUIMenuItemEvent(eventName, { cancelable: true }); - this.dispatchEvent(event); - - if (event.defaultPrevented) return; - - this.showChildren = !this.showChildren; - }; - - private _onLabelClicked = () => { - const event = new UUIMenuItemEvent(UUIMenuItemEvent.CLICK_LABEL); - this.dispatchEvent(event); - }; - - private _renderLabelInside() { - return html` - ${this.renderLabel()} - `; - } - - private _renderLabelAsAnchor() { - if (this.disabled) { - return html` - ${this._renderLabelInside()} - `; - } - return html` - ${this._renderLabelInside()} - `; - } - - private _renderLabelAsButton() { - return html` `; - } - - render() { - return html` - - ${this.showChildren ? html`` : ''} - `; - } } declare global { diff --git a/packages/uui-pagination/lib/uui-pagination.element.ts b/packages/uui-pagination/lib/uui-pagination.element.ts index 25b15a18b..132eecc8c 100644 --- a/packages/uui-pagination/lib/uui-pagination.element.ts +++ b/packages/uui-pagination/lib/uui-pagination.element.ts @@ -24,40 +24,6 @@ const arrayOfNumbers = (start: number, stop: number) => { */ @defineElement('uui-pagination') export class UUIPaginationElement extends LitElement { - static styles = [ - css` - uui-button-group { - width: 100%; - } - - uui-button { - --uui-button-border-color: var(--uui-color-border-standalone); - --uui-button-border-color-hover: var(--uui-color-interactive-emphasis); - --uui-button-border-color-disabled: var(--uui-color-border-standalone); - } - - .page { - min-width: 36px; - max-width: 72px; - } - .page.active { - --uui-button-background-color: var(--uui-color-current); - } - - .nav { - min-width: 72px; - } - - .dots { - pointer-events: none; - } - - .active { - pointer-events: none; - } - `, - ]; - private _observer = new ResizeObserver(this._calculateRange.bind(this)); connectedCallback() { @@ -349,6 +315,40 @@ export class UUIPaginationElement extends LitElement {
`; } + + static styles = [ + css` + uui-button-group { + width: 100%; + } + + uui-button { + --uui-button-border-color: var(--uui-color-border-standalone); + --uui-button-border-color-hover: var(--uui-color-interactive-emphasis); + --uui-button-border-color-disabled: var(--uui-color-border-standalone); + } + + .page { + min-width: 36px; + max-width: 72px; + } + .page.active { + --uui-button-background-color: var(--uui-color-current); + } + + .nav { + min-width: 72px; + } + + .dots { + pointer-events: none; + } + + .active { + pointer-events: none; + } + `, + ]; } declare global { diff --git a/packages/uui-popover/lib/uui-popover.element.ts b/packages/uui-popover/lib/uui-popover.element.ts index 65751c582..32d1accfc 100644 --- a/packages/uui-popover/lib/uui-popover.element.ts +++ b/packages/uui-popover/lib/uui-popover.element.ts @@ -39,37 +39,6 @@ function mathClamp(value: number, min: number, max: number) { */ @defineElement('uui-popover') export class UUIPopoverElement extends LitElement { - static styles = [ - css` - :host { - position: relative; - display: inline-block; - width: 100%; - } - #container { - position: absolute; - width: 100%; - z-index: var(--uui-popover-z-index, 1); - } - slot[name='popover'] { - display: block; - } - #trigger { - position: relative; - width: 100%; - } - - slot[name='trigger']::slotted(uui-button) { - --uui-button-border-radius: var( - --uui-popover-toggle-slot-button-border-radius - ); - --uui-button-merge-border-left: var( - --uui-popover-toggle-slot-button-merge-border-left - ); - } - `, - ]; - // Cashed non-state variables ////////////////////////////// private intersectionObserver?: IntersectionObserver; private scrollEventHandler = this._updatePlacement.bind(this); @@ -547,4 +516,35 @@ export class UUIPopoverElement extends LitElement { `; } + + static styles = [ + css` + :host { + position: relative; + display: inline-block; + width: 100%; + } + #container { + position: absolute; + width: 100%; + z-index: var(--uui-popover-z-index, 1); + } + slot[name='popover'] { + display: block; + } + #trigger { + position: relative; + width: 100%; + } + + slot[name='trigger']::slotted(uui-button) { + --uui-button-border-radius: var( + --uui-popover-toggle-slot-button-border-radius + ); + --uui-button-merge-border-left: var( + --uui-popover-toggle-slot-button-merge-border-left + ); + } + `, + ]; } diff --git a/packages/uui-progress-bar/lib/uui-progress-bar.element.ts b/packages/uui-progress-bar/lib/uui-progress-bar.element.ts index edfb56ca7..fcbe519fc 100644 --- a/packages/uui-progress-bar/lib/uui-progress-bar.element.ts +++ b/packages/uui-progress-bar/lib/uui-progress-bar.element.ts @@ -11,27 +11,6 @@ const clamp = (num: number, min: number, max: number) => */ @defineElement('uui-progress-bar') export class UUIProgressBarElement extends LitElement { - static styles = [ - css` - :host { - width: 100%; - height: 4px; - position: relative; - overflow: hidden; - background: var(--uui-color-surface-alt); - border-radius: 100px; - display: inline-block; - } - - #bar { - transition: width 250ms ease; - background: var(--uui-color-positive); - height: 100%; - width: 0%; - } - `, - ]; - private _progress = 0; /** * Set this to a number between 0 and 100 to reflect the progress of some operation. @@ -59,6 +38,27 @@ export class UUIProgressBarElement extends LitElement {
`; } + + static styles = [ + css` + :host { + width: 100%; + height: 4px; + position: relative; + overflow: hidden; + background: var(--uui-color-surface-alt); + border-radius: 100px; + display: inline-block; + } + + #bar { + transition: width 250ms ease; + background: var(--uui-color-positive); + height: 100%; + width: 0%; + } + `, + ]; } declare global { diff --git a/packages/uui-radio/lib/uui-radio-group.element.ts b/packages/uui-radio/lib/uui-radio-group.element.ts index ff142329c..e23a6e5d5 100644 --- a/packages/uui-radio/lib/uui-radio-group.element.ts +++ b/packages/uui-radio/lib/uui-radio-group.element.ts @@ -25,23 +25,6 @@ export class UUIRadioGroupElement extends FormControlMixin(LitElement) { */ static readonly formAssociated = true; - static styles = [ - css` - :host { - display: inline-block; - padding-right: 3px; - border: 1px solid transparent; - border-radius: var(--uui-border-radius); - } - - :host(:not([pristine]):invalid), - /* polyfill support */ - :host(:not([pristine])[internals-invalid]) { - border: 1px solid var(--uui-color-danger-standalone); - } - `, - ]; - /** * Disables the input. * @type {boolean} @@ -295,6 +278,23 @@ export class UUIRadioGroupElement extends FormControlMixin(LitElement) { render() { return html` `; } + + static styles = [ + css` + :host { + display: inline-block; + padding-right: 3px; + border: 1px solid transparent; + border-radius: var(--uui-border-radius); + } + + :host(:not([pristine]):invalid), + /* polyfill support */ + :host(:not([pristine])[internals-invalid]) { + border: 1px solid var(--uui-color-danger-standalone); + } + `, + ]; } declare global { diff --git a/packages/uui-radio/lib/uui-radio.element.ts b/packages/uui-radio/lib/uui-radio.element.ts index f22f89e30..5491e9154 100644 --- a/packages/uui-radio/lib/uui-radio.element.ts +++ b/packages/uui-radio/lib/uui-radio.element.ts @@ -17,128 +17,6 @@ import { UUIRadioEvent } from './UUIRadioEvent'; */ @defineElement('uui-radio') export class UUIRadioElement extends LitElement { - static styles = [ - UUIHorizontalShakeKeyframes, - css` - :host { - display: block; - box-sizing: border-box; - font-family: inherit; - color: currentColor; - --uui-radio-button-size: var(--uui-size-6); - margin: var(--uui-size-2) 0; - } - - label { - display: block; - box-sizing: border-box; - display: flex; - align-items: center; - cursor: pointer; - line-height: 18px; - } - - #input { - width: 0; - height: 0; - opacity: 0; - margin: 0; - } - - .label { - margin-top: 2px; - } - - #button { - box-sizing: border-box; - display: inline-block; - width: var(--uui-radio-button-size, 18px); - height: var(--uui-radio-button-size, 18px); - background-color: var(--uui-color-surface); - border: 1px solid var(--uui-color-border-standalone); - border-radius: 100%; - margin-right: calc(var(--uui-size-2) * 2); - position: relative; - flex: 0 0 var(--uui-radio-button-size); - } - - #button::after { - content: ''; - width: calc(var(--uui-radio-button-size) / 2); - height: calc(var(--uui-radio-button-size) / 2); - background-color: var(--uui-color-selected); - border-radius: 100%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%) scale(0); - transition: all 0.15s ease-in-out; - } - - :host(:hover) #button { - border: 1px solid var(--uui-color-border-emphasis); - } - - :host(:focus) { - outline: none; - } - :host(:focus) #button { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } - - input:checked ~ #button::after { - transform: translate(-50%, -50%) scale(1); - } - - input:checked ~ #button { - border: 1px solid var(--uui-color-selected); - } - - input:checked:hover ~ #button { - border: 1px solid var(--uui-color-selected-emphasis); - } - - input:checked:hover ~ #button::after { - background-color: var(--uui-color-selected-emphasis); - } - - :host([disabled]) label { - cursor: default; - opacity: 0.5; - } - :host([disabled]) .label { - color: var(--uui-color-disabled-contrast); - } - - :host([disabled]) input ~ #button { - border: 1px solid var(--uui-color-disabled-contrast); - } - - :host([disabled]) input:checked ~ #button { - border: 1px solid var(--uui-color-disabled-contrast); - } - - :host([disabled]) input:checked ~ #button::after { - background-color: var(--uui-color-disabled-contrast); - } - - :host([disabled]:active) #button { - animation: ${UUIHorizontalShakeAnimationValue}; - } - - @media (prefers-reduced-motion) { - :host([disabled]:active) #button { - animation: none; - } - - #button::after { - transition: none; - } - } - `, - ]; - @query('#input') private inputElement!: HTMLInputElement; @@ -292,6 +170,128 @@ export class UUIRadioElement extends LitElement { `; } + + static styles = [ + UUIHorizontalShakeKeyframes, + css` + :host { + display: block; + box-sizing: border-box; + font-family: inherit; + color: currentColor; + --uui-radio-button-size: var(--uui-size-6); + margin: var(--uui-size-2) 0; + } + + label { + display: block; + box-sizing: border-box; + display: flex; + align-items: center; + cursor: pointer; + line-height: 18px; + } + + #input { + width: 0; + height: 0; + opacity: 0; + margin: 0; + } + + .label { + margin-top: 2px; + } + + #button { + box-sizing: border-box; + display: inline-block; + width: var(--uui-radio-button-size, 18px); + height: var(--uui-radio-button-size, 18px); + background-color: var(--uui-color-surface); + border: 1px solid var(--uui-color-border-standalone); + border-radius: 100%; + margin-right: calc(var(--uui-size-2) * 2); + position: relative; + flex: 0 0 var(--uui-radio-button-size); + } + + #button::after { + content: ''; + width: calc(var(--uui-radio-button-size) / 2); + height: calc(var(--uui-radio-button-size) / 2); + background-color: var(--uui-color-selected); + border-radius: 100%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + transition: all 0.15s ease-in-out; + } + + :host(:hover) #button { + border: 1px solid var(--uui-color-border-emphasis); + } + + :host(:focus) { + outline: none; + } + :host(:focus) #button { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + + input:checked ~ #button::after { + transform: translate(-50%, -50%) scale(1); + } + + input:checked ~ #button { + border: 1px solid var(--uui-color-selected); + } + + input:checked:hover ~ #button { + border: 1px solid var(--uui-color-selected-emphasis); + } + + input:checked:hover ~ #button::after { + background-color: var(--uui-color-selected-emphasis); + } + + :host([disabled]) label { + cursor: default; + opacity: 0.5; + } + :host([disabled]) .label { + color: var(--uui-color-disabled-contrast); + } + + :host([disabled]) input ~ #button { + border: 1px solid var(--uui-color-disabled-contrast); + } + + :host([disabled]) input:checked ~ #button { + border: 1px solid var(--uui-color-disabled-contrast); + } + + :host([disabled]) input:checked ~ #button::after { + background-color: var(--uui-color-disabled-contrast); + } + + :host([disabled]:active) #button { + animation: ${UUIHorizontalShakeAnimationValue}; + } + + @media (prefers-reduced-motion) { + :host([disabled]:active) #button { + animation: none; + } + + #button::after { + transition: none; + } + } + `, + ]; } declare global { diff --git a/packages/uui-range-slider/lib/uui-range-slider.element.ts b/packages/uui-range-slider/lib/uui-range-slider.element.ts index aff184ba7..8f69b27ab 100644 --- a/packages/uui-range-slider/lib/uui-range-slider.element.ts +++ b/packages/uui-range-slider/lib/uui-range-slider.element.ts @@ -17,722 +17,379 @@ const STEP_MIN_WIDTH = 24; */ @defineElement('uui-range-slider') export class UUIRangeSliderElement extends FormControlMixin(LitElement) { - static styles = [ - UUIHorizontalPulseKeyframes, - css` - :host { - display: block; - min-height: 50px; - width: 100%; - place-items: center; - -webkit-user-select: none; /* Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+/Edge */ - user-select: none; - cursor: pointer; - } + static readonly formAssociated = true; - :host([disabled]) { - cursor: default; - } + /** + * Disables the input. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + disabled = false; - /** NATIVE INPUT STYLING */ + /** + * Label to be used for aria-label and eventually as visual label. Adds " low value" and " high value" endings for the two values. + * @type {string} + * @attr + */ + @property({ type: String }) + label!: String; - input { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - position: absolute; - top: 0; - background-color: transparent; - pointer-events: none; - left: 0; - right: 0; - border-radius: 20px; - } + /** + * This reflects the behavior of a native input step attribute. + * @type {number} + * @attr + * @default 1 + */ + @property({ type: Number }) + step = 1; - input::-webkit-slider-thumb { - pointer-events: all; - position: relative; - z-index: 1; - outline: 0; - } + /** + * Hides the numbers representing the value of each steps. Dots will still be visible + * @type {boolean} + * @attr 'hide-step-values' + * @default false + */ + @property({ type: Boolean, attribute: 'hide-step-values' }) + hideStepValues = false; - input::-moz-range-thumb { - pointer-events: all; - position: relative; - z-index: 10; - -moz-appearance: none; - background: linear-gradient(to bottom, #ededed 0%, #dedede 100%); - width: 11px; - } + /** + * Sets the minimum allowed value. + * @type {number} + * @attr min + * @default 0 + */ + @property({ type: Number }) + min = 0; - input::-moz-range-track { - position: relative; - z-index: -1; - background-color: rgba(0, 0, 0, 0.15); - border: 0; - } + /** + * Sets the maximum allowed value. + * @type {number} + * @attr max + * @default 100 + */ + @property({ type: Number }) + max = 100; - input:last-of-type::-moz-range-track { - -moz-appearance: none; - background: none transparent; - border: 0; - } + /** + * Minimum value gap between the the two picked values. Cannot be lower than the step value and cannot be higher than the maximum gap + * @type {number} + * @attr min-gap + * @default undefined + */ + @property({ type: Number, attribute: 'min-gap' }) + minGap?: number; - /** TRACK */ + /** + * Maximum value gap between the the two picked values. Cannot be lower than the minimum gap. + * @type {number} + * @attr max-gap + * @default undefined + */ + @property({ type: Number, attribute: 'max-gap' }) + maxGap?: number; - #inner-track .color-target { - position: absolute; - z-index: 2; - left: 0; - right: 0; - height: 25px; - transform: translateY(-50%); - } + /** + * This is a value property of the uui-range-slider. Split the two values with comma, forexample 10,50 sets the values to 10 and 50. + * @type {string} + * @attr + * @default 0,100 + */ + @property({ type: String }) + get value() { + return this._value; + } + set value(newVal) { + if (newVal instanceof String) { + super.value = newVal; + const values = newVal.split(','); + this.valueLow = parseInt(values[0]); + this.valueHigh = parseInt(values[1]); + } + } - #inner-track .color { - height: 3px; - position: absolute; - transition: background-color 320ms ease-out; - } + private _valueLow = 0; + /** + * The lower picked value. + * @type {number} + * @attr value-low + * @default 0 + */ + @property({ type: Number, attribute: 'value-low' }) + set valueLow(newLow) { + const old = this._valueHigh; + if (newLow <= this.min) { + this._valueLow = this.min; + super.value = `${this.min},${this.valueHigh}`; + this.requestUpdate('valueLow', old); + return; + } + if (newLow >= this.valueHigh - this.step) { + this._valueLow = this.valueHigh - this.step; + super.value = `${this.valueHigh - this.step},${this.valueHigh}`; + this.requestUpdate('valueLow', old); + return; + } + this._valueLow = newLow; + super.value = `${newLow},${this.valueHigh}`; + this.requestUpdate('valueLow', old); + } + get valueLow() { + return this._valueLow; + } - :host(:not([disabled])) - #range-slider - #inner-track - .color:has(.color-target:hover), - :host(:not([disabled])) - #range-slider - #inner-track - .color:has(.color-target:active) { - background-color: var(--uui-color-focus); - } + private _valueHigh = 100; + /** + * The higher picked value. + * @type {number} + * @attr value-high + * @default 100 + */ + @property({ type: Number, attribute: 'value-high' }) + set valueHigh(newHigh) { + const old = this._valueHigh; + if (newHigh >= this.max) { + this._valueHigh = this.max; + super.value = `${this.valueLow},${this.max}`; + this.requestUpdate('valueHigh', old); + return; + } + if (newHigh <= this.valueLow + this.step) { + this._valueHigh = this.valueLow + this.step; + super.value = `${this.valueLow},${this.valueLow + this.step}`; + this.requestUpdate('valueHigh', old); + return; + } + this._valueHigh = newHigh; + super.value = `${this.valueLow},${newHigh}`; + this.requestUpdate('valueHigh', old); + } + get valueHigh() { + return this._valueHigh; + } - :host(:not([disabled])) #range-slider .color { - background-color: var(--uui-color-selected); - } + @state() + private _trackWidth = 0; - :host([disabled]) #range-slider .color { - background-color: #555; - } + @state() + private _currentInputFocus?: HTMLInputElement; - #range-slider { - transform: translateY(50%); - position: relative; - height: 18px; - display: flex; - flex-direction: column; - width: 100%; - } + @state() + private _currentThumbFocus: 'high' | 'low' = 'low'; - #inner-track { - border-radius: 10px; - position: absolute; - height: 3px; - background-color: var(--uui-color-border-standalone); - left: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ - right: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ - } + @state() + private _grabbingBoth?: boolean; - #range-slider:hover #inner-track, - #range-slider:active #inner-track { - background-color: #a1a1a1; - } + @state() + private _startPos = 0; - /** STEP VALUES */ - - .track-step { - fill: var(--uui-color-border); - } + @state() + private _startLow = 0; - :host .track-step.filled { - fill: var(--uui-color-selected) !important; - } + @state() + private _startHigh = 0; - :host .track-step.filled-disabled { - fill: var(--uui-palette-mine-grey) !important; - } + @query('#low-input') + private _inputLow!: HTMLInputElement; - #range-slider:hover .track-step, - #range-slider:active .track-step { - fill: #a1a1a1; - } + @query('#high-input') + private _inputHigh!: HTMLInputElement; - #step-values { - margin: 0 ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ - padding-top: ${TRACK_PADDING + 3}px; - display: flex; - align-items: flex-end; - box-sizing: border-box; - } + @query('#range-slider') + private _outerTrack!: HTMLElement; - #step-values > span { - flex-basis: 0; - flex-grow: 1; - color: var(--uui-color-disabled-contrast); - } + @query('#inner-track') + private _innerTrack!: HTMLElement; - #step-values > span > span { - transform: translateX(-50%); - display: inline-block; - text-align: center; - font-size: var(--uui-type-small-size); - } + @query('#low-thumb') + private _thumbLow!: HTMLElement; - #step-values > span:last-child { - width: 0; - flex-grow: 0; - } + @query('#high-thumb') + private _thumbHigh!: HTMLElement; - .svg-wrapper { - margin: 0 ${-1 * TRACK_PADDING}px; - height: 18px; - transform: translateY(-75%); - } + @query('.color') + private _innerColor!: HTMLElement; - .svg-wrapper svg { - margin-top: ${TRACK_PADDING / 2}px; - } + @query('.color-target') + private _bothThumbsTarget!: HTMLElement; - /** FOCUS */ + #setValue(val?: string) { + this._value = val ? val : `${this.valueLow},${this.valueHigh}`; + } - input[type='range'] { - position: absolute; - left: 0; - right: 0; - top: -50%; - } + protected getFormElement(): HTMLInputElement { + return this._currentInputFocus ? this._currentInputFocus : this._inputLow; + } - input[type='range']:focus-visible { - outline: none; - } + public focus() { + this._currentInputFocus + ? this._currentInputFocus.focus() + : this._inputLow.focus(); + } - #low-input:focus-visible ~ #inner-track #low-thumb, - #high-input:focus-visible ~ #inner-track #high-thumb, - #low-input:focus ~ #inner-track #low.thumb, - #high-input:focus ~ #inner-track #high-thumb, - #low-input:active ~ #inner-track #low.thumb, - #high-input:active ~ #inner-track #high-thumb { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } + private _onKeypress(e: KeyboardEvent) { + if (e.key == 'Enter') { + this.submit(); + } + } - input[type='range']:focus + .thumb { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } + /** Thumb position */ - :host(:not([disabled])) - #range-slider - #inner-track - .color:has(.color-target:hover) - ~ #low-thumb, - :host(:not([disabled])) - #range-slider - #inner-track - .color:has(.color-target:active) - ~ #low-thumb, - :host(:not([disabled])) - #range-slider - #inner-track - .color:has(.color-target:hover) - ~ #high-thumb, - :host(:not([disabled])) - #range-slider - #inner-track - .color:has(.color-target:active) - ~ #high-thumb { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } + private _sliderLowThumbPosition() { + const ratio = (this.valueLow - this.min) / (this.max - this.min); + const valueLowPercent = `${Math.floor(ratio * 100000) / 1000}%`; + return valueLowPercent; + } - /** THUMBS */ + private _sliderHighThumbPosition() { + const ratio = (this.valueHigh - this.min) / (this.max - this.min); + const valueHighPercent = `${Math.floor(ratio * 100000) / 1000}%`; + return valueHighPercent; + } - .thumb { - z-index: 3; - transform: translateY(-50%); - position: absolute; - top: 2px; - bottom: 0px; - left: 0px; - height: 17px; - width: 17px; - margin-left: -8px; - margin-right: -8px; - border-radius: 50%; - box-sizing: border-box; - background-color: var(--uui-color-surface, #fff); - border: 2px solid var(--uui-color-selected, #3544b1); - transition: left 120ms ease 0s; - } + /** Coloring of the line between thumbs */ - .thumb:after { - content: ''; - position: absolute; - top: 2px; - left: 2px; - height: 9px; - width: 9px; - border-radius: 50%; - background-color: var(--uui-color-selected); - } + private _fillColor() { + const percentStart = + ((this.valueLow - this.min) / (this.max - this.min)) * 100; + const percentEnd = + ((this.valueHigh - this.min) / (this.max - this.min)) * 100; - :host([disabled]) .thumb { - background-color: var(--uui-color-disabled); - border-color: var(--uui-palette-mine-grey); - } - :host([disabled]) .thumb:after { - background-color: var(--uui-palette-mine-grey); - } + this._innerColor.style.left = `${percentStart}%`; + this._innerColor.style.right = `${100 - percentEnd}%`; + } - .thumb .value { - position: absolute; - box-sizing: border-box; - font-weight: 700; - bottom: 15px; - left: 50%; - width: 40px; - margin-left: -20px; - text-align: center; - opacity: 1; - transition: 120ms opacity; - color: var(--uui-color-selected); - visibility: hidden; - opacity: 0; - } + /** Moving thumb */ - :host([disabled]) .thumb .value { - color: var(--uui-palette-mine-grey); - } + private _moveThumb(pageX: number) { + const value = this._getValue(pageX); + if (value >= this.valueHigh) this._setThumb(this._thumbHigh); + if (value <= this.valueLow) this._setThumb(this._thumbLow); + this._setValueBasedOnCurrentThumb( + this._validateValueBasedOnCurrentThumb(value) + ); + } - #range-slider:active .thumb .value, - #range-slider:focus .thumb .value, - #range-slider:hover .thumb .value { - visibility: visible; - opacity: 1; - } + /** Mouse events */ - /** NATIVE THUMB STYLING */ + private _onMouseDown = (e: MouseEvent) => { + e.preventDefault(); + if (this.disabled) return; + window.addEventListener('mouseup', this._onMouseUp); + window.addEventListener('mousemove', this._onMouseMove); - input[type='range']::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 17px; - height: 17px; - background-color: transparent; - display: block; - border-radius: 100%; - pointer-events: auto; - cursor: pointer; - } - input[type='range']:disabled::-webkit-slider-thumb { - cursor: default; - } + const target = e.composedPath()[0]; + const pageX = e.pageX; - input[type='range']::-moz-range-thumb { - -moz-appearance: none; - appearance: none; - width: 17px; - height: 17px; - background-color: transparent; - display: block; - border-radius: 100%; - pointer-events: auto; - cursor: pointer; - } - input[type='range']:disabled::-moz-range-thumb { - cursor: default; - } + target == this._bothThumbsTarget + ? (this._grabbingBoth = true) + : (this._grabbingBoth = false); - input[type='range']::-ms-thumb { - appearance: none; - width: 17px; - height: 17px; - background-color: transparent; - display: block; - border-radius: 100%; - pointer-events: auto; - cursor: pointer; - } - input[type='range']:disabled::-ms-thumb { - cursor: default; - } - `, - ]; - - static readonly formAssociated = true; + if (this._grabbingBoth) { + this._saveStartPoint(pageX, this.valueLow, this.valueHigh); + return; + } - /** - * Disables the input. - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - disabled = false; + this._moveThumb(pageX); + }; - /** - * Label to be used for aria-label and eventually as visual label. Adds " low value" and " high value" endings for the two values. - * @type {string} - * @attr - */ - @property({ type: String }) - label!: String; + private _onMouseMove = (e: MouseEvent) => { + e.preventDefault(); + const pageX = e.pageX; + const val = this._getValue(pageX); + if (!this._grabbingBoth) + this._setValueBasedOnCurrentThumb( + this._validateValueBasedOnCurrentThumb(val) + ); + else this._moveBoth(pageX); - /** - * This reflects the behavior of a native input step attribute. - * @type {number} - * @attr - * @default 1 - */ - @property({ type: Number }) - step = 1; + this.dispatchEvent(new UUIRangeSliderEvent(UUIRangeSliderEvent.INPUT)); + }; - /** - * Hides the numbers representing the value of each steps. Dots will still be visible - * @type {boolean} - * @attr 'hide-step-values' - * @default false - */ - @property({ type: Boolean, attribute: 'hide-step-values' }) - hideStepValues = false; + private _onMouseUp = () => { + this._stop(); + window.removeEventListener('mouseup', this._onMouseUp); + window.removeEventListener('mousemove', this._onMouseMove); + }; - /** - * Sets the minimum allowed value. - * @type {number} - * @attr min - * @default 0 - */ - @property({ type: Number }) - min = 0; + /** Touch / mobile events */ - /** - * Sets the maximum allowed value. - * @type {number} - * @attr max - * @default 100 - */ - @property({ type: Number }) - max = 100; + private _onTouchStart = (e: TouchEvent) => { + e.preventDefault(); + if (this.disabled) return; - /** - * Minimum value gap between the the two picked values. Cannot be lower than the step value and cannot be higher than the maximum gap - * @type {number} - * @attr min-gap - * @default undefined - */ - @property({ type: Number, attribute: 'min-gap' }) - minGap?: number; + window.addEventListener('touchend', this._onTouchEnd); + window.addEventListener('touchmove', this._onTouchMove); - /** - * Maximum value gap between the the two picked values. Cannot be lower than the minimum gap. - * @type {number} - * @attr max-gap - * @default undefined - */ - @property({ type: Number, attribute: 'max-gap' }) - maxGap?: number; + const target = e.composedPath()[0]; + const pageX = e.touches[0].pageX; - /** - * This is a value property of the uui-range-slider. Split the two values with comma, forexample 10,50 sets the values to 10 and 50. - * @type {string} - * @attr - * @default 0,100 - */ - @property({ type: String }) - get value() { - return this._value; - } - set value(newVal) { - if (newVal instanceof String) { - super.value = newVal; - const values = newVal.split(','); - this.valueLow = parseInt(values[0]); - this.valueHigh = parseInt(values[1]); - } - } + target == this._bothThumbsTarget + ? (this._grabbingBoth = true) + : (this._grabbingBoth = false); - private _valueLow = 0; - /** - * The lower picked value. - * @type {number} - * @attr value-low - * @default 0 - */ - @property({ type: Number, attribute: 'value-low' }) - set valueLow(newLow) { - const old = this._valueHigh; - if (newLow <= this.min) { - this._valueLow = this.min; - super.value = `${this.min},${this.valueHigh}`; - this.requestUpdate('valueLow', old); - return; - } - if (newLow >= this.valueHigh - this.step) { - this._valueLow = this.valueHigh - this.step; - super.value = `${this.valueHigh - this.step},${this.valueHigh}`; - this.requestUpdate('valueLow', old); + if (this._grabbingBoth) { + this._saveStartPoint(pageX, this.valueLow, this.valueHigh); return; } - this._valueLow = newLow; - super.value = `${newLow},${this.valueHigh}`; - this.requestUpdate('valueLow', old); - } - get valueLow() { - return this._valueLow; - } + this._moveThumb(pageX); + }; - private _valueHigh = 100; - /** - * The higher picked value. - * @type {number} - * @attr value-high - * @default 100 - */ - @property({ type: Number, attribute: 'value-high' }) - set valueHigh(newHigh) { - const old = this._valueHigh; - if (newHigh >= this.max) { - this._valueHigh = this.max; - super.value = `${this.valueLow},${this.max}`; - this.requestUpdate('valueHigh', old); - return; - } - if (newHigh <= this.valueLow + this.step) { - this._valueHigh = this.valueLow + this.step; - super.value = `${this.valueLow},${this.valueLow + this.step}`; - this.requestUpdate('valueHigh', old); - return; - } - this._valueHigh = newHigh; - super.value = `${this.valueLow},${newHigh}`; - this.requestUpdate('valueHigh', old); - } - get valueHigh() { - return this._valueHigh; - } + private _onTouchMove = (e: TouchEvent) => { + const pageX = e.touches[0].pageX; + const val = this._getValue(pageX); + if (!this._grabbingBoth) + this._setValueBasedOnCurrentThumb( + this._validateValueBasedOnCurrentThumb(val) + ); + else this._moveBoth(pageX); - @state() - private _trackWidth = 0; + this.dispatchEvent(new UUIRangeSliderEvent(UUIRangeSliderEvent.INPUT)); + }; - @state() - private _currentInputFocus?: HTMLInputElement; + private _onTouchEnd = () => { + this._stop(); + window.removeEventListener('touchend', this._onTouchEnd); + window.removeEventListener('touchmove', this._onTouchMove); + }; - @state() - private _currentThumbFocus: 'high' | 'low' = 'low'; + /** */ - @state() - private _grabbingBoth?: boolean; + private _stop() { + this._grabbingBoth = false; + this.pristine = false; + this.dispatchEvent(new UUIRangeSliderEvent(UUIRangeSliderEvent.CHANGE)); + } - @state() - private _startPos = 0; + /** The latest thumb in use */ - @state() - private _startLow = 0; + private _setThumb(target: EventTarget | HTMLElement) { + this._currentThumbFocus = target === this._thumbLow ? 'low' : 'high'; - @state() - private _startHigh = 0; + this._currentThumbFocus === 'low' + ? (this._currentInputFocus = this._inputLow) + : (this._currentInputFocus = this._inputHigh); - @query('#low-input') - private _inputLow!: HTMLInputElement; + this.focus(); + } - @query('#high-input') - private _inputHigh!: HTMLInputElement; + private _setValueBasedOnCurrentThumb(val: number) { + this._currentThumbFocus === 'low' + ? (this.valueLow = val) + : (this.valueHigh = val); + } - @query('#range-slider') - private _outerTrack!: HTMLElement; + /** Get the value depends on where clicked/touched */ - @query('#inner-track') - private _innerTrack!: HTMLElement; + private _getValue(pageX: number) { + const mouseXPosition = + pageX - this._innerTrack.getBoundingClientRect().left; + const clickPercent = + mouseXPosition / (this._trackWidth - TRACK_PADDING * 2); - @query('#low-thumb') - private _thumbLow!: HTMLElement; - - @query('#high-thumb') - private _thumbHigh!: HTMLElement; - - @query('.color') - private _innerColor!: HTMLElement; - - @query('.color-target') - private _bothThumbsTarget!: HTMLElement; - - #setValue(val?: string) { - this._value = val ? val : `${this.valueLow},${this.valueHigh}`; - } - - protected getFormElement(): HTMLInputElement { - return this._currentInputFocus ? this._currentInputFocus : this._inputLow; - } - - public focus() { - this._currentInputFocus - ? this._currentInputFocus.focus() - : this._inputLow.focus(); - } - - private _onKeypress(e: KeyboardEvent) { - if (e.key == 'Enter') { - this.submit(); - } - } - - /** Thumb position */ - - private _sliderLowThumbPosition() { - const ratio = (this.valueLow - this.min) / (this.max - this.min); - const valueLowPercent = `${Math.floor(ratio * 100000) / 1000}%`; - return valueLowPercent; - } - - private _sliderHighThumbPosition() { - const ratio = (this.valueHigh - this.min) / (this.max - this.min); - const valueHighPercent = `${Math.floor(ratio * 100000) / 1000}%`; - return valueHighPercent; - } - - /** Coloring of the line between thumbs */ - - private _fillColor() { - const percentStart = - ((this.valueLow - this.min) / (this.max - this.min)) * 100; - const percentEnd = - ((this.valueHigh - this.min) / (this.max - this.min)) * 100; - - this._innerColor.style.left = `${percentStart}%`; - this._innerColor.style.right = `${100 - percentEnd}%`; - } - - /** Moving thumb */ - - private _moveThumb(pageX: number) { - const value = this._getValue(pageX); - if (value >= this.valueHigh) this._setThumb(this._thumbHigh); - if (value <= this.valueLow) this._setThumb(this._thumbLow); - this._setValueBasedOnCurrentThumb( - this._validateValueBasedOnCurrentThumb(value) - ); - } - - /** Mouse events */ - - private _onMouseDown = (e: MouseEvent) => { - e.preventDefault(); - if (this.disabled) return; - window.addEventListener('mouseup', this._onMouseUp); - window.addEventListener('mousemove', this._onMouseMove); - - const target = e.composedPath()[0]; - const pageX = e.pageX; - - target == this._bothThumbsTarget - ? (this._grabbingBoth = true) - : (this._grabbingBoth = false); - - if (this._grabbingBoth) { - this._saveStartPoint(pageX, this.valueLow, this.valueHigh); - return; - } - - this._moveThumb(pageX); - }; - - private _onMouseMove = (e: MouseEvent) => { - e.preventDefault(); - const pageX = e.pageX; - const val = this._getValue(pageX); - if (!this._grabbingBoth) - this._setValueBasedOnCurrentThumb( - this._validateValueBasedOnCurrentThumb(val) - ); - else this._moveBoth(pageX); - - this.dispatchEvent(new UUIRangeSliderEvent(UUIRangeSliderEvent.INPUT)); - }; - - private _onMouseUp = () => { - this._stop(); - window.removeEventListener('mouseup', this._onMouseUp); - window.removeEventListener('mousemove', this._onMouseMove); - }; - - /** Touch / mobile events */ - - private _onTouchStart = (e: TouchEvent) => { - e.preventDefault(); - if (this.disabled) return; - - window.addEventListener('touchend', this._onTouchEnd); - window.addEventListener('touchmove', this._onTouchMove); - - const target = e.composedPath()[0]; - const pageX = e.touches[0].pageX; - - target == this._bothThumbsTarget - ? (this._grabbingBoth = true) - : (this._grabbingBoth = false); - - if (this._grabbingBoth) { - this._saveStartPoint(pageX, this.valueLow, this.valueHigh); - return; - } - this._moveThumb(pageX); - }; - - private _onTouchMove = (e: TouchEvent) => { - const pageX = e.touches[0].pageX; - const val = this._getValue(pageX); - if (!this._grabbingBoth) - this._setValueBasedOnCurrentThumb( - this._validateValueBasedOnCurrentThumb(val) - ); - else this._moveBoth(pageX); - - this.dispatchEvent(new UUIRangeSliderEvent(UUIRangeSliderEvent.INPUT)); - }; - - private _onTouchEnd = () => { - this._stop(); - window.removeEventListener('touchend', this._onTouchEnd); - window.removeEventListener('touchmove', this._onTouchMove); - }; - - /** */ - - private _stop() { - this._grabbingBoth = false; - this.pristine = false; - this.dispatchEvent(new UUIRangeSliderEvent(UUIRangeSliderEvent.CHANGE)); - } - - /** The latest thumb in use */ - - private _setThumb(target: EventTarget | HTMLElement) { - this._currentThumbFocus = target === this._thumbLow ? 'low' : 'high'; - - this._currentThumbFocus === 'low' - ? (this._currentInputFocus = this._inputLow) - : (this._currentInputFocus = this._inputHigh); - - this.focus(); - } - - private _setValueBasedOnCurrentThumb(val: number) { - this._currentThumbFocus === 'low' - ? (this.valueLow = val) - : (this.valueHigh = val); - } - - /** Get the value depends on where clicked/touched */ - - private _getValue(pageX: number) { - const mouseXPosition = - pageX - this._innerTrack.getBoundingClientRect().left; - const clickPercent = - mouseXPosition / (this._trackWidth - TRACK_PADDING * 2); - - const clickedValue = clickPercent * (this.max - this.min) + this.min; - const newValue = Math.round(clickedValue / this.step) * this.step; + const clickedValue = clickPercent * (this.max - this.min) + this.min; + const newValue = Math.round(clickedValue / this.step) * this.step; return newValue; } @@ -1026,6 +683,349 @@ export class UUIRangeSliderElement extends FormControlMixin(LitElement) { return nothing; } } + + static styles = [ + UUIHorizontalPulseKeyframes, + css` + :host { + display: block; + min-height: 50px; + width: 100%; + place-items: center; + -webkit-user-select: none; /* Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+/Edge */ + user-select: none; + cursor: pointer; + } + + :host([disabled]) { + cursor: default; + } + + /** NATIVE INPUT STYLING */ + + input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + position: absolute; + top: 0; + background-color: transparent; + pointer-events: none; + left: 0; + right: 0; + border-radius: 20px; + } + + input::-webkit-slider-thumb { + pointer-events: all; + position: relative; + z-index: 1; + outline: 0; + } + + input::-moz-range-thumb { + pointer-events: all; + position: relative; + z-index: 10; + -moz-appearance: none; + background: linear-gradient(to bottom, #ededed 0%, #dedede 100%); + width: 11px; + } + + input::-moz-range-track { + position: relative; + z-index: -1; + background-color: rgba(0, 0, 0, 0.15); + border: 0; + } + + input:last-of-type::-moz-range-track { + -moz-appearance: none; + background: none transparent; + border: 0; + } + + /** TRACK */ + + #inner-track .color-target { + position: absolute; + z-index: 2; + left: 0; + right: 0; + height: 25px; + transform: translateY(-50%); + } + + #inner-track .color { + height: 3px; + position: absolute; + transition: background-color 320ms ease-out; + } + + :host(:not([disabled])) + #range-slider + #inner-track + .color:has(.color-target:hover), + :host(:not([disabled])) + #range-slider + #inner-track + .color:has(.color-target:active) { + background-color: var(--uui-color-focus); + } + + :host(:not([disabled])) #range-slider .color { + background-color: var(--uui-color-selected); + } + + :host([disabled]) #range-slider .color { + background-color: #555; + } + + #range-slider { + transform: translateY(50%); + position: relative; + height: 18px; + display: flex; + flex-direction: column; + width: 100%; + } + + #inner-track { + border-radius: 10px; + position: absolute; + height: 3px; + background-color: var(--uui-color-border-standalone); + left: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ + right: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ + } + + #range-slider:hover #inner-track, + #range-slider:active #inner-track { + background-color: #a1a1a1; + } + + /** STEP VALUES */ + + .track-step { + fill: var(--uui-color-border); + } + + :host .track-step.filled { + fill: var(--uui-color-selected) !important; + } + + :host .track-step.filled-disabled { + fill: var(--uui-palette-mine-grey) !important; + } + + #range-slider:hover .track-step, + #range-slider:active .track-step { + fill: #a1a1a1; + } + + #step-values { + margin: 0 ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ + padding-top: ${TRACK_PADDING + 3}px; + display: flex; + align-items: flex-end; + box-sizing: border-box; + } + + #step-values > span { + flex-basis: 0; + flex-grow: 1; + color: var(--uui-color-disabled-contrast); + } + + #step-values > span > span { + transform: translateX(-50%); + display: inline-block; + text-align: center; + font-size: var(--uui-type-small-size); + } + + #step-values > span:last-child { + width: 0; + flex-grow: 0; + } + + .svg-wrapper { + margin: 0 ${-1 * TRACK_PADDING}px; + height: 18px; + transform: translateY(-75%); + } + + .svg-wrapper svg { + margin-top: ${TRACK_PADDING / 2}px; + } + + /** FOCUS */ + + input[type='range'] { + position: absolute; + left: 0; + right: 0; + top: -50%; + } + + input[type='range']:focus-visible { + outline: none; + } + + #low-input:focus-visible ~ #inner-track #low-thumb, + #high-input:focus-visible ~ #inner-track #high-thumb, + #low-input:focus ~ #inner-track #low.thumb, + #high-input:focus ~ #inner-track #high-thumb, + #low-input:active ~ #inner-track #low.thumb, + #high-input:active ~ #inner-track #high-thumb { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + + input[type='range']:focus + .thumb { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + + :host(:not([disabled])) + #range-slider + #inner-track + .color:has(.color-target:hover) + ~ #low-thumb, + :host(:not([disabled])) + #range-slider + #inner-track + .color:has(.color-target:active) + ~ #low-thumb, + :host(:not([disabled])) + #range-slider + #inner-track + .color:has(.color-target:hover) + ~ #high-thumb, + :host(:not([disabled])) + #range-slider + #inner-track + .color:has(.color-target:active) + ~ #high-thumb { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + + /** THUMBS */ + + .thumb { + z-index: 3; + transform: translateY(-50%); + position: absolute; + top: 2px; + bottom: 0px; + left: 0px; + height: 17px; + width: 17px; + margin-left: -8px; + margin-right: -8px; + border-radius: 50%; + box-sizing: border-box; + background-color: var(--uui-color-surface, #fff); + border: 2px solid var(--uui-color-selected, #3544b1); + transition: left 120ms ease 0s; + } + + .thumb:after { + content: ''; + position: absolute; + top: 2px; + left: 2px; + height: 9px; + width: 9px; + border-radius: 50%; + background-color: var(--uui-color-selected); + } + + :host([disabled]) .thumb { + background-color: var(--uui-color-disabled); + border-color: var(--uui-palette-mine-grey); + } + :host([disabled]) .thumb:after { + background-color: var(--uui-palette-mine-grey); + } + + .thumb .value { + position: absolute; + box-sizing: border-box; + font-weight: 700; + bottom: 15px; + left: 50%; + width: 40px; + margin-left: -20px; + text-align: center; + opacity: 1; + transition: 120ms opacity; + color: var(--uui-color-selected); + visibility: hidden; + opacity: 0; + } + + :host([disabled]) .thumb .value { + color: var(--uui-palette-mine-grey); + } + + #range-slider:active .thumb .value, + #range-slider:focus .thumb .value, + #range-slider:hover .thumb .value { + visibility: visible; + opacity: 1; + } + + /** NATIVE THUMB STYLING */ + + input[type='range']::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 17px; + height: 17px; + background-color: transparent; + display: block; + border-radius: 100%; + pointer-events: auto; + cursor: pointer; + } + input[type='range']:disabled::-webkit-slider-thumb { + cursor: default; + } + + input[type='range']::-moz-range-thumb { + -moz-appearance: none; + appearance: none; + width: 17px; + height: 17px; + background-color: transparent; + display: block; + border-radius: 100%; + pointer-events: auto; + cursor: pointer; + } + input[type='range']:disabled::-moz-range-thumb { + cursor: default; + } + + input[type='range']::-ms-thumb { + appearance: none; + width: 17px; + height: 17px; + background-color: transparent; + display: block; + border-radius: 100%; + pointer-events: auto; + cursor: pointer; + } + input[type='range']:disabled::-ms-thumb { + cursor: default; + } + `, + ]; } declare global { diff --git a/packages/uui-ref-list/lib/uui-ref-list.element.ts b/packages/uui-ref-list/lib/uui-ref-list.element.ts index 3db13bf7d..74f66698f 100644 --- a/packages/uui-ref-list/lib/uui-ref-list.element.ts +++ b/packages/uui-ref-list/lib/uui-ref-list.element.ts @@ -6,6 +6,10 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; */ @defineElement('uui-ref-list') export class UUIRefListElement extends LitElement { + render() { + return html``; + } + static styles = [ css` :host { @@ -25,10 +29,6 @@ export class UUIRefListElement extends LitElement { } `, ]; - - render() { - return html``; - } } declare global { diff --git a/packages/uui-ref-node-data-type/lib/uui-ref-node-data-type.element.ts b/packages/uui-ref-node-data-type/lib/uui-ref-node-data-type.element.ts index 7675dcc74..401d51095 100644 --- a/packages/uui-ref-node-data-type/lib/uui-ref-node-data-type.element.ts +++ b/packages/uui-ref-node-data-type/lib/uui-ref-node-data-type.element.ts @@ -13,8 +13,6 @@ import { property } from 'lit/decorators.js'; @defineElement('uui-ref-node-data-type') export class UUIRefNodeDataTypeElement extends UUIRefNodeElement { - static styles = [...UUIRefNodeElement.styles]; - protected fallbackIcon = ''; @@ -40,6 +38,8 @@ export class UUIRefNodeDataTypeElement extends UUIRefNodeElement { >${details.join(' | ')}`; } + + static styles = [...UUIRefNodeElement.styles]; } declare global { diff --git a/packages/uui-ref-node-document-type/lib/uui-ref-node-document-type.element.ts b/packages/uui-ref-node-document-type/lib/uui-ref-node-document-type.element.ts index 4a3b61a99..064a1f967 100644 --- a/packages/uui-ref-node-document-type/lib/uui-ref-node-document-type.element.ts +++ b/packages/uui-ref-node-document-type/lib/uui-ref-node-document-type.element.ts @@ -13,8 +13,6 @@ import { property } from 'lit/decorators.js'; @defineElement('uui-ref-node-document-type') export class UUIRefNodeDocumentTypeElement extends UUIRefNodeElement { - static styles = [...UUIRefNodeElement.styles]; - protected fallbackIcon = ''; @@ -40,6 +38,8 @@ export class UUIRefNodeDocumentTypeElement extends UUIRefNodeElement { >${details.join(' | ')}`; } + + static styles = [...UUIRefNodeElement.styles]; } declare global { diff --git a/packages/uui-ref-node-form/lib/uui-ref-node-form.element.ts b/packages/uui-ref-node-form/lib/uui-ref-node-form.element.ts index b29ece396..68a359934 100644 --- a/packages/uui-ref-node-form/lib/uui-ref-node-form.element.ts +++ b/packages/uui-ref-node-form/lib/uui-ref-node-form.element.ts @@ -11,10 +11,10 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; @defineElement('uui-ref-node-form') export class UUIRefNodeFormElement extends UUIRefNodeElement { - static styles = [...UUIRefNodeElement.styles]; - protected fallbackIcon = ''; + + static styles = [...UUIRefNodeElement.styles]; } declare global { diff --git a/packages/uui-ref-node-member/lib/uui-ref-node-member.element.ts b/packages/uui-ref-node-member/lib/uui-ref-node-member.element.ts index 1ea99e0bc..90ab8c727 100644 --- a/packages/uui-ref-node-member/lib/uui-ref-node-member.element.ts +++ b/packages/uui-ref-node-member/lib/uui-ref-node-member.element.ts @@ -13,8 +13,6 @@ import { property } from 'lit/decorators.js'; @defineElement('uui-ref-node-member') export class UUIRefNodeMemberElement extends UUIRefNodeElement { - static styles = [...UUIRefNodeElement.styles]; - protected fallbackIcon = ''; @@ -39,6 +37,8 @@ export class UUIRefNodeMemberElement extends UUIRefNodeElement { >${details.join(' | ')}`; } + + static styles = [...UUIRefNodeElement.styles]; } declare global { diff --git a/packages/uui-ref-node-package/lib/uui-ref-node-package.element.ts b/packages/uui-ref-node-package/lib/uui-ref-node-package.element.ts index cebe459cf..63b97b4ba 100644 --- a/packages/uui-ref-node-package/lib/uui-ref-node-package.element.ts +++ b/packages/uui-ref-node-package/lib/uui-ref-node-package.element.ts @@ -13,8 +13,6 @@ import { property } from 'lit/decorators.js'; @defineElement('uui-ref-node-package') export class UUIRefNodePackageElement extends UUIRefNodeElement { - static styles = [...UUIRefNodeElement.styles]; - protected fallbackIcon = ''; @@ -51,6 +49,8 @@ export class UUIRefNodePackageElement extends UUIRefNodeElement { >${details.join(' | ')}`; } + + static styles = [...UUIRefNodeElement.styles]; } declare global { diff --git a/packages/uui-ref-node-user/lib/uui-ref-node-user.element.ts b/packages/uui-ref-node-user/lib/uui-ref-node-user.element.ts index 4e109e032..668dbacc0 100644 --- a/packages/uui-ref-node-user/lib/uui-ref-node-user.element.ts +++ b/packages/uui-ref-node-user/lib/uui-ref-node-user.element.ts @@ -13,8 +13,6 @@ import { property } from 'lit/decorators.js'; @defineElement('uui-ref-node-user') export class UUIRefNodeUserElement extends UUIRefNodeElement { - static styles = [...UUIRefNodeElement.styles]; - protected fallbackIcon = ''; @@ -39,6 +37,8 @@ export class UUIRefNodeUserElement extends UUIRefNodeElement { >${details.join(' | ')}`; } + + static styles = [...UUIRefNodeElement.styles]; } declare global { diff --git a/packages/uui-ref-node/lib/uui-ref-node.element.ts b/packages/uui-ref-node/lib/uui-ref-node.element.ts index 201420ae3..d3465d581 100644 --- a/packages/uui-ref-node/lib/uui-ref-node.element.ts +++ b/packages/uui-ref-node/lib/uui-ref-node.element.ts @@ -18,70 +18,6 @@ import { property, state } from 'lit/decorators.js'; @defineElement('uui-ref-node') export class UUIRefNodeElement extends UUIRefElement { - static styles = [ - ...UUIRefElement.styles, - css` - :host { - min-width: 250px; - padding: calc(var(--uui-size-2) + 1px); - } - - #open-part { - align-self: stretch; - - display: flex; - position: relative; - align-items: center; - cursor: pointer; - } - - #icon { - font-size: 1.2em; - margin-left: var(--uui-size-2); - margin-right: var(--uui-size-1); - } - - #info { - display: flex; - flex-direction: column; - align-items: start; - justify-content: center; - height: 100%; - padding-left: var(--uui-size-2); - } - - #name { - font-weight: 700; - } - - #detail { - font-size: var(--uui-type-small-size); - } - - :host(:not([disabled])) #open-part:hover #icon { - color: var(--uui-color-interactive-emphasis); - } - :host(:not([disabled])) #open-part:hover #name { - font-weight: 700; - text-decoration: underline; - color: var(--uui-color-interactive-emphasis); - } - :host(:not([disabled])) #open-part:hover #detail { - color: var(--uui-color-interactive-emphasis); - } - - :host([disabled]) #icon { - color: var(--uui-color-disabled-contrast); - } - :host([disabled]) #name { - color: var(--uui-color-disabled-contrast); - } - :host([disabled]) #detail { - color: var(--uui-color-disabled-contrast); - } - `, - ]; - /** * Node name * @type {string} @@ -156,6 +92,70 @@ export class UUIRefNodeElement extends UUIRefElement { `; } + + static styles = [ + ...UUIRefElement.styles, + css` + :host { + min-width: 250px; + padding: calc(var(--uui-size-2) + 1px); + } + + #open-part { + align-self: stretch; + + display: flex; + position: relative; + align-items: center; + cursor: pointer; + } + + #icon { + font-size: 1.2em; + margin-left: var(--uui-size-2); + margin-right: var(--uui-size-1); + } + + #info { + display: flex; + flex-direction: column; + align-items: start; + justify-content: center; + height: 100%; + padding-left: var(--uui-size-2); + } + + #name { + font-weight: 700; + } + + #detail { + font-size: var(--uui-type-small-size); + } + + :host(:not([disabled])) #open-part:hover #icon { + color: var(--uui-color-interactive-emphasis); + } + :host(:not([disabled])) #open-part:hover #name { + font-weight: 700; + text-decoration: underline; + color: var(--uui-color-interactive-emphasis); + } + :host(:not([disabled])) #open-part:hover #detail { + color: var(--uui-color-interactive-emphasis); + } + + :host([disabled]) #icon { + color: var(--uui-color-disabled-contrast); + } + :host([disabled]) #name { + color: var(--uui-color-disabled-contrast); + } + :host([disabled]) #detail { + color: var(--uui-color-disabled-contrast); + } + `, + ]; } declare global { diff --git a/packages/uui-ref/lib/uui-ref.element.ts b/packages/uui-ref/lib/uui-ref.element.ts index 6d3faa943..0f91c644f 100644 --- a/packages/uui-ref/lib/uui-ref.element.ts +++ b/packages/uui-ref/lib/uui-ref.element.ts @@ -19,6 +19,36 @@ import { UUIRefEvent } from './UUIRefEvent'; export class UUIRefElement extends SelectOnlyMixin( SelectableMixin(LitElement) ) { + /** + * Set tot true to disable + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + disabled = false; + + /** + * Set to true to display error state + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + error = false; + + protected handleOpenClick(e: Event) { + e.stopPropagation(); + this.dispatchEvent(new UUIRefEvent(UUIRefEvent.OPEN)); + } + protected handleOpenKeydown(e: KeyboardEvent) { + // TODO: Is it correct to both be able to open by space and enter? We to investigate, i would think that enter was the only option. + if (e.key !== ' ' && e.key !== 'Enter') return; + e.preventDefault(); + e.stopPropagation(); + this.dispatchEvent(new UUIRefEvent(UUIRefEvent.OPEN)); + } + static styles = [ css` :host { @@ -170,36 +200,6 @@ export class UUIRefElement extends SelectOnlyMixin( } `, ]; - - /** - * Set tot true to disable - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - disabled = false; - - /** - * Set to true to display error state - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - error = false; - - protected handleOpenClick(e: Event) { - e.stopPropagation(); - this.dispatchEvent(new UUIRefEvent(UUIRefEvent.OPEN)); - } - protected handleOpenKeydown(e: KeyboardEvent) { - // TODO: Is it correct to both be able to open by space and enter? We to investigate, i would think that enter was the only option. - if (e.key !== ' ' && e.key !== 'Enter') return; - e.preventDefault(); - e.stopPropagation(); - this.dispatchEvent(new UUIRefEvent(UUIRefEvent.OPEN)); - } } declare global { diff --git a/packages/uui-scroll-container/lib/uui-scroll-container.element.ts b/packages/uui-scroll-container/lib/uui-scroll-container.element.ts index 4414419c3..4a953c0ba 100644 --- a/packages/uui-scroll-container/lib/uui-scroll-container.element.ts +++ b/packages/uui-scroll-container/lib/uui-scroll-container.element.ts @@ -11,6 +11,23 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-scroll-container') export class UUIScrollContainerElement extends LitElement { + /** + * @type {boolean} + * @attr forces the scrollbar to appear + */ + @property({ type: Boolean, reflect: true, attribute: 'enforce-scroll' }) + enforceScroll = false; + + connectedCallback() { + super.connectedCallback(); + if (!this.hasAttribute('tabindex')) { + this.setAttribute('tabindex', '0'); + } + } + render() { + return html``; + } + static styles = [ css` :host { @@ -40,23 +57,6 @@ export class UUIScrollContainerElement extends LitElement { } `, ]; - - /** - * @type {boolean} - * @attr forces the scrollbar to appear - */ - @property({ type: Boolean, reflect: true, attribute: 'enforce-scroll' }) - enforceScroll = false; - - connectedCallback() { - super.connectedCallback(); - if (!this.hasAttribute('tabindex')) { - this.setAttribute('tabindex', '0'); - } - } - render() { - return html``; - } } declare global { diff --git a/packages/uui-select/lib/uui-select.element.ts b/packages/uui-select/lib/uui-select.element.ts index d806458ac..f14bd9bb6 100644 --- a/packages/uui-select/lib/uui-select.element.ts +++ b/packages/uui-select/lib/uui-select.element.ts @@ -25,81 +25,6 @@ declare global { // TODO: Consider if this should use child items instead of an array. @defineElement('uui-select') export class UUISelectElement extends FormControlMixin(LitElement) { - static styles = [ - css` - :host { - position: relative; - font-family: inherit; - } - - #native { - display: inline-block; - font-family: inherit; - font-size: var(--uui-select-font-size, var(--uui-size-5)); - height: var(--uui-select-height, var(--uui-size-11)); - width: 100%; - padding: var(--uui-select-padding-y, var(--uui-size-1)) - var(--uui-select-padding-x, var(--uui-size-2)); - color: currentColor; - border-radius: 0; - box-sizing: border-box; - background-color: transparent; - border: 1px solid - var(--uui-select-border-color, var(--uui-color-border)); - transition: all 150ms ease; - } - - #native:focus { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } - - #native[disabled] { - cursor: not-allowed; - background-color: var( - --uui-select-disabled-background-color, - var(--uui-color-disabled) - ); - } - - #native:hover { - border: 1px solid - var(--uui-select-border-color-hover, var(--uui-color-border-emphasis)); - } - - option:checked { - background: var( - --uui-select-selected-option-background-color, - var(--uui-color-selected) - ); - color: var( - --uui-select-selected-option-color, - var(--uui-color-selected-contrast) - ); - } - - /* TODO: a proper focus style has to be implemented. it needs it's own variables */ - #native:focus { - outline-color: var(--uui-select-outline-color, var(--uui-color-focus)); - } - - #caret { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - } - - :host([error]) #native { - border: 1px solid var(--uui-color-danger-standalone); - } - - :host([error]) #native[disabled] { - border: 1px solid var(--uui-color-danger-standalone); - } - `, - ]; - /** * Text with which component should be labeled * @type {string} @@ -306,6 +231,81 @@ export class UUISelectElement extends FormControlMixin(LitElement) { )} `; } + + static styles = [ + css` + :host { + position: relative; + font-family: inherit; + } + + #native { + display: inline-block; + font-family: inherit; + font-size: var(--uui-select-font-size, var(--uui-size-5)); + height: var(--uui-select-height, var(--uui-size-11)); + width: 100%; + padding: var(--uui-select-padding-y, var(--uui-size-1)) + var(--uui-select-padding-x, var(--uui-size-2)); + color: currentColor; + border-radius: 0; + box-sizing: border-box; + background-color: transparent; + border: 1px solid + var(--uui-select-border-color, var(--uui-color-border)); + transition: all 150ms ease; + } + + #native:focus { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + + #native[disabled] { + cursor: not-allowed; + background-color: var( + --uui-select-disabled-background-color, + var(--uui-color-disabled) + ); + } + + #native:hover { + border: 1px solid + var(--uui-select-border-color-hover, var(--uui-color-border-emphasis)); + } + + option:checked { + background: var( + --uui-select-selected-option-background-color, + var(--uui-color-selected) + ); + color: var( + --uui-select-selected-option-color, + var(--uui-color-selected-contrast) + ); + } + + /* TODO: a proper focus style has to be implemented. it needs it's own variables */ + #native:focus { + outline-color: var(--uui-select-outline-color, var(--uui-color-focus)); + } + + #caret { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + } + + :host([error]) #native { + border: 1px solid var(--uui-color-danger-standalone); + } + + :host([error]) #native[disabled] { + border: 1px solid var(--uui-color-danger-standalone); + } + `, + ]; } declare global { diff --git a/packages/uui-slider/lib/uui-slider.element.ts b/packages/uui-slider/lib/uui-slider.element.ts index 3b00400de..3536d17a7 100644 --- a/packages/uui-slider/lib/uui-slider.element.ts +++ b/packages/uui-slider/lib/uui-slider.element.ts @@ -61,151 +61,6 @@ export class UUISliderElement extends FormControlMixin(LitElement) { */ static readonly formAssociated = true; - static styles = [ - UUIHorizontalPulseKeyframes, - nativeInputStyles, - css` - :host { - display: inline-block; - width: 100%; - position: relative; - min-height: 30px; - user-select: none; - } - - input { - box-sizing: border-box; - height: 18px; - } - - #track { - position: relative; - height: 18px; - width: 100%; - display: flex; - } - - #track svg { - height: 21px; - border-radius: 10px; - background-color: var(--uui-color-surface); - } - #track svg rect { - width: calc(100% - 18px); - fill: var(--uui-color-border-standalone); - } - input:hover ~ #track svg rect { - fill: var(--uui-color-border-emphasis); - } - - input:focus ~ #track svg { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } - - .track-step { - fill: var(--uui-color-border); - } - input:hover ~ #track svg .track-step { - fill: var(--uui-color-border-emphasis); - } - - #track-inner { - position: absolute; - left: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ - right: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ - } - - #thumb { - position: absolute; - top: 2px; - bottom: 0; - left: 0; - height: 17px; - width: 17px; - margin-left: -8px; - margin-right: -8px; - border-radius: 50%; - box-sizing: border-box; - - background-color: var(--uui-color-surface); - border: 2px solid var(--uui-color-selected); - - transition: 120ms left ease; - } - :host([disabled]) #thumb { - background-color: var(--uui-color-disabled); - border-color: var(--uui-color-disabled-standalone); - } - - #thumb:after { - content: ''; - position: absolute; - top: 2px; - left: 2px; - height: 9px; - width: 9px; - border-radius: 50%; - background-color: var(--uui-color-selected); - } - :host([disabled]) #thumb:after { - background-color: var(--uui-color-disabled); - } - - #thumb-label { - position: absolute; - box-sizing: border-box; - font-weight: 700; - bottom: 15px; - left: 50%; - width: 40px; - margin-left: -20px; - text-align: center; - opacity: 0; - transition: 120ms opacity; - color: var(--uui-color-selected); - } - - input:focus ~ #track #thumb-label, - input:hover ~ #track #thumb-label { - opacity: 1; - } - - #step-values { - margin: 0 ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ - margin-top: 6px; - display: flex; - align-items: flex-end; - box-sizing: border-box; - } - - #step-values > span { - flex-basis: 0; - flex-grow: 1; - color: var(--uui-color-disabled-contrast); - } - - #step-values > span > span { - transform: translateX(-50%); - display: inline-block; - text-align: center; - font-size: var(--uui-type-small-size); - } - - #step-values > span:last-child { - width: 0; - flex-grow: 0; - } - - :host(:not([pristine]):invalid) #thumb { - border-color: var(--uui-color-danger-standalone); - } - :host(:not([pristine]):invalid) #thumb:after { - background-color: var(--uui-color-danger); - } - `, - ]; - /** * Hides the numbers representing the value of each steps. Dots will still be visible * @type {boolean} @@ -432,6 +287,151 @@ export class UUISliderElement extends FormControlMixin(LitElement) { ${RenderStepValues(this._steps, this._stepWidth, this.hideStepValues)} `; } + + static styles = [ + UUIHorizontalPulseKeyframes, + nativeInputStyles, + css` + :host { + display: inline-block; + width: 100%; + position: relative; + min-height: 30px; + user-select: none; + } + + input { + box-sizing: border-box; + height: 18px; + } + + #track { + position: relative; + height: 18px; + width: 100%; + display: flex; + } + + #track svg { + height: 21px; + border-radius: 10px; + background-color: var(--uui-color-surface); + } + #track svg rect { + width: calc(100% - 18px); + fill: var(--uui-color-border-standalone); + } + input:hover ~ #track svg rect { + fill: var(--uui-color-border-emphasis); + } + + input:focus ~ #track svg { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + + .track-step { + fill: var(--uui-color-border); + } + input:hover ~ #track svg .track-step { + fill: var(--uui-color-border-emphasis); + } + + #track-inner { + position: absolute; + left: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ + right: ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ + } + + #thumb { + position: absolute; + top: 2px; + bottom: 0; + left: 0; + height: 17px; + width: 17px; + margin-left: -8px; + margin-right: -8px; + border-radius: 50%; + box-sizing: border-box; + + background-color: var(--uui-color-surface); + border: 2px solid var(--uui-color-selected); + + transition: 120ms left ease; + } + :host([disabled]) #thumb { + background-color: var(--uui-color-disabled); + border-color: var(--uui-color-disabled-standalone); + } + + #thumb:after { + content: ''; + position: absolute; + top: 2px; + left: 2px; + height: 9px; + width: 9px; + border-radius: 50%; + background-color: var(--uui-color-selected); + } + :host([disabled]) #thumb:after { + background-color: var(--uui-color-disabled); + } + + #thumb-label { + position: absolute; + box-sizing: border-box; + font-weight: 700; + bottom: 15px; + left: 50%; + width: 40px; + margin-left: -20px; + text-align: center; + opacity: 0; + transition: 120ms opacity; + color: var(--uui-color-selected); + } + + input:focus ~ #track #thumb-label, + input:hover ~ #track #thumb-label { + opacity: 1; + } + + #step-values { + margin: 0 ${TRACK_PADDING}px; /* Match TRACK_MARGIN */ + margin-top: 6px; + display: flex; + align-items: flex-end; + box-sizing: border-box; + } + + #step-values > span { + flex-basis: 0; + flex-grow: 1; + color: var(--uui-color-disabled-contrast); + } + + #step-values > span > span { + transform: translateX(-50%); + display: inline-block; + text-align: center; + font-size: var(--uui-type-small-size); + } + + #step-values > span:last-child { + width: 0; + flex-grow: 0; + } + + :host(:not([pristine]):invalid) #thumb { + border-color: var(--uui-color-danger-standalone); + } + :host(:not([pristine]):invalid) #thumb:after { + background-color: var(--uui-color-danger); + } + `, + ]; } declare global { diff --git a/packages/uui-symbol-expand/lib/uui-symbol-expand.element.ts b/packages/uui-symbol-expand/lib/uui-symbol-expand.element.ts index 9398bf25f..c4ac934d5 100644 --- a/packages/uui-symbol-expand/lib/uui-symbol-expand.element.ts +++ b/packages/uui-symbol-expand/lib/uui-symbol-expand.element.ts @@ -8,6 +8,21 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-symbol-expand') export class UUISymbolExpandElement extends LitElement { + /** + * Set this boolean to true for a open/expanded look. + * @type {boolean} + * @default false + * @attr + */ + @property({ type: Boolean, reflect: true }) + public open = false; + + render() { + return html` + + `; + } + static styles = [ css` :host { @@ -30,21 +45,6 @@ export class UUISymbolExpandElement extends LitElement { } `, ]; - - /** - * Set this boolean to true for a open/expanded look. - * @type {boolean} - * @default false - * @attr - */ - @property({ type: Boolean, reflect: true }) - public open = false; - - render() { - return html` - - `; - } } declare global { diff --git a/packages/uui-symbol-file-dropzone/lib/uui-symbol-file-dropzone.element.ts b/packages/uui-symbol-file-dropzone/lib/uui-symbol-file-dropzone.element.ts index f8d68943e..3f54467f0 100644 --- a/packages/uui-symbol-file-dropzone/lib/uui-symbol-file-dropzone.element.ts +++ b/packages/uui-symbol-file-dropzone/lib/uui-symbol-file-dropzone.element.ts @@ -7,6 +7,27 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-symbol-file-dropzone') export class UUISymbolFileDropzoneElement extends LitElement { + /** + * Renders a error symbol instead of the upload symbol + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + error = false; + + render() { + return html` + + `; + } + static styles = [ css` :host { @@ -29,27 +50,6 @@ export class UUISymbolFileDropzoneElement extends LitElement { } `, ]; - - /** - * Renders a error symbol instead of the upload symbol - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - error = false; - - render() { - return html` - - `; - } } declare global { diff --git a/packages/uui-symbol-file-thumbnail/lib/uui-symbol-file-thumbnail.element.ts b/packages/uui-symbol-file-thumbnail/lib/uui-symbol-file-thumbnail.element.ts index 9fac6d54f..8a8fdd501 100644 --- a/packages/uui-symbol-file-thumbnail/lib/uui-symbol-file-thumbnail.element.ts +++ b/packages/uui-symbol-file-thumbnail/lib/uui-symbol-file-thumbnail.element.ts @@ -11,33 +11,6 @@ import { iconPicture } from '@umbraco-ui/uui-icon-registry-essential/lib/svgs'; */ @defineElement('uui-symbol-file-thumbnail') export class UUISymbolFileThumbnailElement extends LitElement { - static styles = [ - css` - :host { - display: block; - width: 100%; - height: 100%; - } - - img { - object-fit: contain; - height: 100%; - width: 100%; - } - - uui-icon { - width: 100%; - height: 100%; - max-width: 100%; - display: flex; - max-height: 100%; - justify-content: center; - color: var(--uui-color-surface); - background: var(--uui-color-surface-alt); - } - `, - ]; - /** * Source of the thumbnail. * @type {string} @@ -68,6 +41,33 @@ export class UUISymbolFileThumbnailElement extends LitElement { name="picture" .fallback=${iconPicture.strings[0]}>`; } + + static styles = [ + css` + :host { + display: block; + width: 100%; + height: 100%; + } + + img { + object-fit: contain; + height: 100%; + width: 100%; + } + + uui-icon { + width: 100%; + height: 100%; + max-width: 100%; + display: flex; + max-height: 100%; + justify-content: center; + color: var(--uui-color-surface); + background: var(--uui-color-surface-alt); + } + `, + ]; } declare global { diff --git a/packages/uui-symbol-file/lib/uui-symbol-file.element.ts b/packages/uui-symbol-file/lib/uui-symbol-file.element.ts index 434ce3e5d..c949b300d 100644 --- a/packages/uui-symbol-file/lib/uui-symbol-file.element.ts +++ b/packages/uui-symbol-file/lib/uui-symbol-file.element.ts @@ -8,6 +8,27 @@ import { property } from 'lit/decorators.js'; @defineElement('uui-symbol-file') export class UUISymbolFileElement extends LitElement { + /** + * The text that will appear on the file icon + * @type {string} + */ + @property({ type: String }) + type = ''; + + render() { + return html` + + + ${this.type + ? html`${this.type.toUpperCase()}` + : ''} `; + } + static styles = [ css` :host { @@ -34,27 +55,6 @@ export class UUISymbolFileElement extends LitElement { } `, ]; - - /** - * The text that will appear on the file icon - * @type {string} - */ - @property({ type: String }) - type = ''; - - render() { - return html` - - - ${this.type - ? html`${this.type.toUpperCase()}` - : ''} `; - } } declare global { diff --git a/packages/uui-symbol-folder/lib/uui-symbol-folder.element.ts b/packages/uui-symbol-folder/lib/uui-symbol-folder.element.ts index cd29d29b6..c6b371b63 100644 --- a/packages/uui-symbol-folder/lib/uui-symbol-folder.element.ts +++ b/packages/uui-symbol-folder/lib/uui-symbol-folder.element.ts @@ -7,6 +7,17 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; @defineElement('uui-symbol-folder') export class UUISymbolFolderElement extends LitElement { + render() { + return html` + + `; + } + static styles = [ css` :host { @@ -21,17 +32,6 @@ export class UUISymbolFolderElement extends LitElement { } `, ]; - - render() { - return html` - - `; - } } declare global { diff --git a/packages/uui-symbol-lock/lib/uui-symbol-lock.element.ts b/packages/uui-symbol-lock/lib/uui-symbol-lock.element.ts index 1c835271c..32b4fd827 100644 --- a/packages/uui-symbol-lock/lib/uui-symbol-lock.element.ts +++ b/packages/uui-symbol-lock/lib/uui-symbol-lock.element.ts @@ -7,6 +7,19 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-symbol-lock') export class UUISymbolLockElement extends LitElement { + @property({ type: Boolean, reflect: true }) + public open = false; + + render() { + return svg` + ${ + this.open === true + ? svg`` + : svg`` + } + `; + } + static styles = [ css` :host { @@ -20,19 +33,6 @@ export class UUISymbolLockElement extends LitElement { } `, ]; - - @property({ type: Boolean, reflect: true }) - public open = false; - - render() { - return svg` - ${ - this.open === true - ? svg`` - : svg`` - } - `; - } } declare global { diff --git a/packages/uui-symbol-more/lib/uui-symbol-more.element.ts b/packages/uui-symbol-more/lib/uui-symbol-more.element.ts index ce3e4a00c..f4c6cbc2a 100644 --- a/packages/uui-symbol-more/lib/uui-symbol-more.element.ts +++ b/packages/uui-symbol-more/lib/uui-symbol-more.element.ts @@ -5,6 +5,10 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; */ @defineElement('uui-symbol-more') export class UUISymbolMoreElement extends LitElement { + render() { + return html`•••`; + } + static styles = [ css` :host { @@ -17,10 +21,6 @@ export class UUISymbolMoreElement extends LitElement { } `, ]; - - render() { - return html`•••`; - } } declare global { diff --git a/packages/uui-symbol-sort/lib/uui-symbol-sort.element.ts b/packages/uui-symbol-sort/lib/uui-symbol-sort.element.ts index 6b0ba02d4..819b35f71 100644 --- a/packages/uui-symbol-sort/lib/uui-symbol-sort.element.ts +++ b/packages/uui-symbol-sort/lib/uui-symbol-sort.element.ts @@ -10,6 +10,26 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-symbol-sort') export class UUISymbolSortElement extends ActiveMixin(LitElement) { + /** + * Turns the arrow around. Set this boolean to true for displaying descending sort is active. + * @type {boolean} + * @default false + */ + @property({ type: Boolean, reflect: true }) + public descending = false; + + render() { + return html` + + + + + `; + } + static styles = [ css` :host { @@ -70,26 +90,6 @@ export class UUISymbolSortElement extends ActiveMixin(LitElement) { } `, ]; - - /** - * Turns the arrow around. Set this boolean to true for displaying descending sort is active. - * @type {boolean} - * @default false - */ - @property({ type: Boolean, reflect: true }) - public descending = false; - - render() { - return html` - - - - - `; - } } declare global { diff --git a/packages/uui-table/lib/uui-table-advanced-example.ts b/packages/uui-table/lib/uui-table-advanced-example.ts index 6c6360a0a..8c1c7aaa7 100644 --- a/packages/uui-table/lib/uui-table-advanced-example.ts +++ b/packages/uui-table/lib/uui-table-advanced-example.ts @@ -27,49 +27,6 @@ interface TableItem { @customElement('uui-table-with-selection-example') export class UUITableWithSelectionExampleElement extends LitElement { - static styles = [ - UUITextStyles, - css` - uui-table-row uui-checkbox { - display: none; - } - - uui-table-row:focus uui-icon, - uui-table-row:focus-within uui-icon, - uui-table-row:hover uui-icon, - uui-table-row[select-only] uui-icon { - display: none; - } - - uui-table-row:focus uui-checkbox, - uui-table-row:focus-within uui-checkbox, - uui-table-row:hover uui-checkbox, - uui-table-row[select-only] uui-checkbox { - display: inline-block; - } - - uui-table-head-cell:focus, - uui-table-head-cell:focus-within, - uui-table-head-cell:hover { - --uui-symbol-sort-hover: 1; - } - - uui-table-head-cell button { - padding: 0; - background-color: transparent; - color: inherit; - border: none; - cursor: pointer; - font-weight: inherit; - font-size: inherit; - display: inline-flex; - align-items: center; - justify-content: space-between; - width: 100%; - } - `, - ]; - @state() private _columns: Array = []; @@ -296,4 +253,47 @@ export class UUITableWithSelectionExampleElement extends LitElement { `; } + + static styles = [ + UUITextStyles, + css` + uui-table-row uui-checkbox { + display: none; + } + + uui-table-row:focus uui-icon, + uui-table-row:focus-within uui-icon, + uui-table-row:hover uui-icon, + uui-table-row[select-only] uui-icon { + display: none; + } + + uui-table-row:focus uui-checkbox, + uui-table-row:focus-within uui-checkbox, + uui-table-row:hover uui-checkbox, + uui-table-row[select-only] uui-checkbox { + display: inline-block; + } + + uui-table-head-cell:focus, + uui-table-head-cell:focus-within, + uui-table-head-cell:hover { + --uui-symbol-sort-hover: 1; + } + + uui-table-head-cell button { + padding: 0; + background-color: transparent; + color: inherit; + border: none; + cursor: pointer; + font-weight: inherit; + font-size: inherit; + display: inline-flex; + align-items: center; + justify-content: space-between; + width: 100%; + } + `, + ]; } diff --git a/packages/uui-table/lib/uui-table-cell.element.ts b/packages/uui-table/lib/uui-table-cell.element.ts index 16d34eeea..ac5e54155 100644 --- a/packages/uui-table/lib/uui-table-cell.element.ts +++ b/packages/uui-table/lib/uui-table-cell.element.ts @@ -11,44 +11,6 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-table-cell') export class UUITableCellElement extends LitElement { - static styles = [ - css` - :host { - position: relative; - display: table-cell; - height: var(--uui-table-cell-height, var(--uui-size-12)); - padding: var( - --uui-table-cell-padding, - var(--uui-size-4) var(--uui-size-5) - ); - border-top: 1px solid var(--uui-color-border); - vertical-align: middle; - } - - :host([clip-text]) { - max-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - box-sizing: border-box; - } - - :host([disable-child-interaction]) ::slotted(*) { - pointer-events: none; - } - - :host([disable-child-interaction])::after { - content: ''; - position: absolute; - inset: 0; - } - - :host([no-padding]) { - padding: 0; - } - `, - ]; - /** * Used to enforce selection interaction by preventing other interactions, primary set by table-row for select-only mode. * @attr @@ -116,6 +78,44 @@ export class UUITableCellElement extends LitElement { render() { return html` `; } + + static styles = [ + css` + :host { + position: relative; + display: table-cell; + height: var(--uui-table-cell-height, var(--uui-size-12)); + padding: var( + --uui-table-cell-padding, + var(--uui-size-4) var(--uui-size-5) + ); + border-top: 1px solid var(--uui-color-border); + vertical-align: middle; + } + + :host([clip-text]) { + max-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + box-sizing: border-box; + } + + :host([disable-child-interaction]) ::slotted(*) { + pointer-events: none; + } + + :host([disable-child-interaction])::after { + content: ''; + position: absolute; + inset: 0; + } + + :host([no-padding]) { + padding: 0; + } + `, + ]; } declare global { diff --git a/packages/uui-table/lib/uui-table-head-cell.element.ts b/packages/uui-table/lib/uui-table-head-cell.element.ts index ed1b514a7..479d55e43 100644 --- a/packages/uui-table/lib/uui-table-head-cell.element.ts +++ b/packages/uui-table/lib/uui-table-head-cell.element.ts @@ -9,6 +9,11 @@ import { UUITableCellElement } from './uui-table-cell.element'; */ @defineElement('uui-table-head-cell') export class UUITableHeadCellElement extends UUITableCellElement { + connectedCallback() { + super.connectedCallback(); + this.setAttribute('role', 'columnheader'); + } + static styles = [ ...UUITableCellElement.styles, css` @@ -17,11 +22,6 @@ export class UUITableHeadCellElement extends UUITableCellElement { } `, ]; - - connectedCallback() { - super.connectedCallback(); - this.setAttribute('role', 'columnheader'); - } } declare global { diff --git a/packages/uui-table/lib/uui-table-head.element.ts b/packages/uui-table/lib/uui-table-head.element.ts index 0ac3535de..feecf4a58 100644 --- a/packages/uui-table/lib/uui-table-head.element.ts +++ b/packages/uui-table/lib/uui-table-head.element.ts @@ -8,15 +8,6 @@ import { css, html, LitElement } from 'lit'; */ @defineElement('uui-table-head') export class UUITableHeadElement extends LitElement { - static styles = [ - css` - :host { - display: table-header-group; - font-weight: bold; - } - `, - ]; - connectedCallback() { super.connectedCallback(); this.setAttribute('role', 'row'); @@ -25,6 +16,15 @@ export class UUITableHeadElement extends LitElement { render() { return html``; } + + static styles = [ + css` + :host { + display: table-header-group; + font-weight: bold; + } + `, + ]; } declare global { diff --git a/packages/uui-table/lib/uui-table-row.element.ts b/packages/uui-table/lib/uui-table-row.element.ts index cd45faa05..08f34b8f3 100644 --- a/packages/uui-table/lib/uui-table-row.element.ts +++ b/packages/uui-table/lib/uui-table-row.element.ts @@ -18,32 +18,6 @@ import { UUITableCellElement } from './uui-table-cell.element'; export class UUITableRowElement extends SelectOnlyMixin( SelectableMixin(LitElement) ) { - static styles = [ - css` - :host { - display: table-row; - position: relative; - outline-offset: -3px; - } - - :host([selectable]) { - cursor: pointer; - } - - :host(:focus) { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } - :host([selected]) { - outline: 2px solid - var(--uui-table-row-color-selected, var(--uui-color-selected)); - } - :host([selected]:focus) { - outline-color: var(--uui-color-focus); - } - `, - ]; - constructor() { super(); @@ -96,6 +70,32 @@ export class UUITableRowElement extends SelectOnlyMixin( render() { return html` `; } + + static styles = [ + css` + :host { + display: table-row; + position: relative; + outline-offset: -3px; + } + + :host([selectable]) { + cursor: pointer; + } + + :host(:focus) { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + :host([selected]) { + outline: 2px solid + var(--uui-table-row-color-selected, var(--uui-color-selected)); + } + :host([selected]:focus) { + outline-color: var(--uui-color-focus); + } + `, + ]; } declare global { diff --git a/packages/uui-table/lib/uui-table.element.ts b/packages/uui-table/lib/uui-table.element.ts index d747f89d5..539820d2f 100644 --- a/packages/uui-table/lib/uui-table.element.ts +++ b/packages/uui-table/lib/uui-table.element.ts @@ -8,6 +8,17 @@ import { css, html, LitElement } from 'lit'; */ @defineElement('uui-table') export class UUITableElement extends LitElement { + /* consider select-only attribute on this level? */ + + connectedCallback() { + super.connectedCallback(); + this.setAttribute('role', 'table'); + } + + render() { + return html``; + } + static styles = [ css` :host { @@ -19,17 +30,6 @@ export class UUITableElement extends LitElement { } `, ]; - - /* consider select-only attribute on this level? */ - - connectedCallback() { - super.connectedCallback(); - this.setAttribute('role', 'table'); - } - - render() { - return html``; - } } declare global { diff --git a/packages/uui-tabs/lib/uui-tab-group.element.ts b/packages/uui-tabs/lib/uui-tab-group.element.ts index 66e7df8b0..4943e30ee 100644 --- a/packages/uui-tabs/lib/uui-tab-group.element.ts +++ b/packages/uui-tabs/lib/uui-tab-group.element.ts @@ -16,61 +16,6 @@ import { UUITabElement } from './uui-tab.element'; */ @defineElement('uui-tab-group') export class UUITabGroupElement extends LitElement { - static styles = [ - css` - :host { - display: flex; - flex-wrap: wrap; - color: var(--uui-tab-text); - background: var(--uui-tab-background, none); - height: 100%; - min-height: 48px; - } - - ::slotted(*:not(:last-of-type)) { - border-right: 1px solid var(--uui-tab-divider, none); - } - - .hidden-tab { - width: 100%; - } - - #hidden-tabs-container { - width: fit-content; - display: flex; - flex-direction: column; - background: var(--uui-color-surface); - border-radius: var(--uui-border-radius); - box-shadow: var(--uui-shadow-depth-3); - overflow: hidden; - } - :host([dropdown-direction='horizontal']) #hidden-tabs-container { - flex-direction: row; - } - - #more-button { - margin-left: auto; - position: relative; - } - #more-button::before { - content: ''; - position: absolute; - bottom: 0; - width: 100%; - background-color: var(--uui-color-current); - height: 0px; - border-radius: 3px 3px 0 0; - opacity: 0; - transition: opacity ease-in 120ms, height ease-in 120ms; - } - #more-button.active-inside::before { - opacity: 1; - height: 4px; - transition: opacity 120ms, height ease-out 120ms; - } - `, - ]; - @query('#more-button') private _moreButtonElement!: UUIButtonElement; @@ -288,6 +233,61 @@ export class UUITabGroupElement extends LitElement { `; } + + static styles = [ + css` + :host { + display: flex; + flex-wrap: wrap; + color: var(--uui-tab-text); + background: var(--uui-tab-background, none); + height: 100%; + min-height: 48px; + } + + ::slotted(*:not(:last-of-type)) { + border-right: 1px solid var(--uui-tab-divider, none); + } + + .hidden-tab { + width: 100%; + } + + #hidden-tabs-container { + width: fit-content; + display: flex; + flex-direction: column; + background: var(--uui-color-surface); + border-radius: var(--uui-border-radius); + box-shadow: var(--uui-shadow-depth-3); + overflow: hidden; + } + :host([dropdown-direction='horizontal']) #hidden-tabs-container { + flex-direction: row; + } + + #more-button { + margin-left: auto; + position: relative; + } + #more-button::before { + content: ''; + position: absolute; + bottom: 0; + width: 100%; + background-color: var(--uui-color-current); + height: 0px; + border-radius: 3px 3px 0 0; + opacity: 0; + transition: opacity ease-in 120ms, height ease-in 120ms; + } + #more-button.active-inside::before { + opacity: 1; + height: 4px; + transition: opacity 120ms, height ease-out 120ms; + } + `, + ]; } declare global { diff --git a/packages/uui-tabs/lib/uui-tab.element.ts b/packages/uui-tabs/lib/uui-tab.element.ts index f8eedf616..5c8c8270e 100644 --- a/packages/uui-tabs/lib/uui-tab.element.ts +++ b/packages/uui-tabs/lib/uui-tab.element.ts @@ -20,6 +20,83 @@ import { ifDefined } from 'lit/directives/if-defined.js'; */ @defineElement('uui-tab') export class UUITabElement extends ActiveMixin(LabelMixin('', LitElement)) { + /** + * Reflects the disabled state of the element. True if tab is disabled. Change this to switch the state programmatically. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + public disabled = false; + + /** + * Set an href, this will turns the inner button into a anchor tag. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public href?: string; + + /** + * Set an anchor tag target, only used when using href. + * @type {string} + * @attr + * @default undefined + */ + @property({ type: String }) + public target?: '_blank' | '_parent' | '_self' | '_top'; + + /** + * Set the visual orientation of this tab, this changes the look and placement of the active indication. + * @type {string} + * @attr + * @default horizontal + */ + @property({ type: String, reflect: true }) + public orientation?: 'horizontal' | 'vertical' = 'horizontal'; + + constructor() { + super(); + this.addEventListener('click', this.onHostClick); + } + + private onHostClick(e: MouseEvent) { + if (this.disabled) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + } + + render() { + return this.href + ? html` + + + ${this.renderLabel()} + + + ` + : html` + + `; + } + static styles = [ css` :host { @@ -144,83 +221,6 @@ export class UUITabElement extends ActiveMixin(LabelMixin('', LitElement)) { } `, ]; - - /** - * Reflects the disabled state of the element. True if tab is disabled. Change this to switch the state programmatically. - * @type {boolean} - * @attr - * @default false - */ - @property({ type: Boolean, reflect: true }) - public disabled = false; - - /** - * Set an href, this will turns the inner button into a anchor tag. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public href?: string; - - /** - * Set an anchor tag target, only used when using href. - * @type {string} - * @attr - * @default undefined - */ - @property({ type: String }) - public target?: '_blank' | '_parent' | '_self' | '_top'; - - /** - * Set the visual orientation of this tab, this changes the look and placement of the active indication. - * @type {string} - * @attr - * @default horizontal - */ - @property({ type: String, reflect: true }) - public orientation?: 'horizontal' | 'vertical' = 'horizontal'; - - constructor() { - super(); - this.addEventListener('click', this.onHostClick); - } - - private onHostClick(e: MouseEvent) { - if (this.disabled) { - e.preventDefault(); - e.stopImmediatePropagation(); - } - } - - render() { - return this.href - ? html` - - - ${this.renderLabel()} - - - ` - : html` - - `; - } } declare global { diff --git a/packages/uui-tag/lib/uui-tag.element.ts b/packages/uui-tag/lib/uui-tag.element.ts index 646e3c41a..c5a9b2363 100644 --- a/packages/uui-tag/lib/uui-tag.element.ts +++ b/packages/uui-tag/lib/uui-tag.element.ts @@ -13,6 +13,28 @@ import { InterfaceColor, InterfaceLook } from '@umbraco-ui/uui-base/lib/types'; @defineElement('uui-tag') export class UUITagElement extends LitElement { + /** + * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. + * @type {"default" | "positive" | "warning" | "danger"} + * @attr + * @default "default" + */ + @property({ reflect: true }) + color: InterfaceColor = 'default'; + + /** + * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. + * @type {"default" | "primary" | "secondary" | "outline" | "placeholder"} + * @attr + * @default "default" + */ + @property({ reflect: true }) + look: InterfaceLook = 'primary'; + + render() { + return html``; + } + static styles = [ css` :host { @@ -83,28 +105,6 @@ export class UUITagElement extends LitElement { } `, ]; - - /** - * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. - * @type {"default" | "positive" | "warning" | "danger"} - * @attr - * @default "default" - */ - @property({ reflect: true }) - color: InterfaceColor = 'default'; - - /** - * Changes the look of the button to one of the predefined, symbolic looks. For example - set this to positive if you want nice, green "confirm" button. - * @type {"default" | "primary" | "secondary" | "outline" | "placeholder"} - * @attr - * @default "default" - */ - @property({ reflect: true }) - look: InterfaceLook = 'primary'; - - render() { - return html``; - } } declare global { diff --git a/packages/uui-textarea/lib/uui-textarea.element.ts b/packages/uui-textarea/lib/uui-textarea.element.ts index 932c5f59a..fa9826b2f 100644 --- a/packages/uui-textarea/lib/uui-textarea.element.ts +++ b/packages/uui-textarea/lib/uui-textarea.element.ts @@ -24,93 +24,6 @@ export class UUITextareaElement extends FormControlMixin(LitElement) { */ static readonly formAssociated = true; - static styles = [ - css` - :host { - position: relative; - } - :host([error]) textarea { - border: 1px solid var(--uui-color-danger) !important; - } - :host([error]) textarea[disabled] { - border: 1px solid var(--uui-color-danger) !important; - } - :host([auto-height]) textarea { - resize: none; - } - .label { - display: inline-block; - margin-bottom: var(--uui-size-1); - font-weight: bold; - } - - textarea[readonly] { - border-color: var( - --uui-textarea-border-color-readonly, - var(--uui-color-disabled-standalone) - ); - background-color: var( - --uui-textarea-background-color-readonly, - var(--uui-color-disabled) - ); - } - textarea[disabled] { - cursor: not-allowed; - background-color: var( - --uui-textarea-background-color-disabled, - var(--uui-color-disabled) - ); - border-color: var( - --uui-textarea-border-color-disabled, - var(--uui-color-disabled) - ); - - color: var(--uui-color-disabled-contrast); - } - - textarea { - font-family: inherit; - box-sizing: border-box; - min-width: 100%; - max-width: 100%; - font-size: inherit; - padding: var(--uui-size-2); - border: 1px solid - var(--uui-textarea-border-color, var(--uui-color-border)); /** Note: Specified border size is needed and hardcoded in autoUpdateHeight() */ - border-radius: 0; - outline: none; - min-height: var(--uui-textarea-min-height); - max-height: var(--uui-textarea-max-height); - background-color: var( - --uui-textarea-background-color, - var(--uui-color-surface) - ); - } - :host(:hover) - textarea:not([readonly]):not([disabled]) - :host(:focus-within) - textarea, - :host(:focus) textarea { - border-color: var( - --uui-textarea-border-color, - var(--uui-color-border-emphasis) - ); - } - - textarea::placeholder { - transition: opacity 120ms; - } - :host(:not([readonly])) textarea:focus::placeholder { - opacity: 0; - } - - textarea:focus { - outline: calc(2px * var(--uui-show-focus-outline, 1)) solid - var(--uui-color-focus); - } - `, - ]; - /** * Defines the textarea placeholder. * @type {string} @@ -344,6 +257,93 @@ export class UUITextareaElement extends FormControlMixin(LitElement) { `; } + + static styles = [ + css` + :host { + position: relative; + } + :host([error]) textarea { + border: 1px solid var(--uui-color-danger) !important; + } + :host([error]) textarea[disabled] { + border: 1px solid var(--uui-color-danger) !important; + } + :host([auto-height]) textarea { + resize: none; + } + .label { + display: inline-block; + margin-bottom: var(--uui-size-1); + font-weight: bold; + } + + textarea[readonly] { + border-color: var( + --uui-textarea-border-color-readonly, + var(--uui-color-disabled-standalone) + ); + background-color: var( + --uui-textarea-background-color-readonly, + var(--uui-color-disabled) + ); + } + textarea[disabled] { + cursor: not-allowed; + background-color: var( + --uui-textarea-background-color-disabled, + var(--uui-color-disabled) + ); + border-color: var( + --uui-textarea-border-color-disabled, + var(--uui-color-disabled) + ); + + color: var(--uui-color-disabled-contrast); + } + + textarea { + font-family: inherit; + box-sizing: border-box; + min-width: 100%; + max-width: 100%; + font-size: inherit; + padding: var(--uui-size-2); + border: 1px solid + var(--uui-textarea-border-color, var(--uui-color-border)); /** Note: Specified border size is needed and hardcoded in autoUpdateHeight() */ + border-radius: 0; + outline: none; + min-height: var(--uui-textarea-min-height); + max-height: var(--uui-textarea-max-height); + background-color: var( + --uui-textarea-background-color, + var(--uui-color-surface) + ); + } + :host(:hover) + textarea:not([readonly]):not([disabled]) + :host(:focus-within) + textarea, + :host(:focus) textarea { + border-color: var( + --uui-textarea-border-color, + var(--uui-color-border-emphasis) + ); + } + + textarea::placeholder { + transition: opacity 120ms; + } + :host(:not([readonly])) textarea:focus::placeholder { + opacity: 0; + } + + textarea:focus { + outline: calc(2px * var(--uui-show-focus-outline, 1)) solid + var(--uui-color-focus); + } + `, + ]; } declare global { diff --git a/packages/uui-toast-notification-container/lib/uui-toast-notification-container.element.ts b/packages/uui-toast-notification-container/lib/uui-toast-notification-container.element.ts index 24217d499..f3a51b035 100644 --- a/packages/uui-toast-notification-container/lib/uui-toast-notification-container.element.ts +++ b/packages/uui-toast-notification-container/lib/uui-toast-notification-container.element.ts @@ -15,38 +15,6 @@ import { property } from 'lit/decorators.js'; */ @defineElement('uui-toast-notification-container') export class UUIToastNotificationContainerElement extends LitElement { - static styles = [ - css` - :host { - position: absolute; - overflow: hidden; - max-width: 100%; - height: 100%; - - pointer-events: none; - box-sizing: border-box; - } - - slot { - display: flex; - flex-direction: column; - align-items: end; - - height: 100%; - box-sizing: border-box; - - padding-top: var(--uui-size-space-1); - padding-bottom: var(--uui-size-space-1); - } - :host([bottom-up]) slot { - justify-content: end; - } - :host([left-align]) slot { - align-items: start; - } - `, - ]; - /** * Set an auto-close timer, the timer will be paused on mouse-hover. * @type number | null @@ -170,6 +138,38 @@ export class UUIToastNotificationContainerElement extends LitElement { render() { return html` `; } + + static styles = [ + css` + :host { + position: absolute; + overflow: hidden; + max-width: 100%; + height: 100%; + + pointer-events: none; + box-sizing: border-box; + } + + slot { + display: flex; + flex-direction: column; + align-items: end; + + height: 100%; + box-sizing: border-box; + + padding-top: var(--uui-size-space-1); + padding-bottom: var(--uui-size-space-1); + } + :host([bottom-up]) slot { + justify-content: end; + } + :host([left-align]) slot { + align-items: start; + } + `, + ]; } declare global { diff --git a/packages/uui-toast-notification-layout/lib/uui-toast-notification-layout.element.ts b/packages/uui-toast-notification-layout/lib/uui-toast-notification-layout.element.ts index 8b4722867..ea0bcdda7 100644 --- a/packages/uui-toast-notification-layout/lib/uui-toast-notification-layout.element.ts +++ b/packages/uui-toast-notification-layout/lib/uui-toast-notification-layout.element.ts @@ -12,32 +12,6 @@ import { property, state } from 'lit/decorators.js'; */ @defineElement('uui-toast-notification-layout') export class UUIToastNotificationLayoutElement extends LitElement { - static styles = [ - UUITextStyles, - css` - #message { - margin-bottom: calc(var(--uui-size-space-1) * -1); - } - #message::after { - content: ''; - display: block; - clear: both; - } - #actions { - /* - display: flex; - width: 100%; - justify-content: flex-end; - */ - display: block; - float: right; - - margin-top: var(--uui-size-space-4); - margin-bottom: calc(var(--uui-size-space-2) * -1); - } - `, - ]; - /** * Headline for this notification, can also be set via the 'headline' slot. * @attr @@ -70,6 +44,32 @@ export class UUIToastNotificationLayoutElement extends LitElement { `; } + + static styles = [ + UUITextStyles, + css` + #message { + margin-bottom: calc(var(--uui-size-space-1) * -1); + } + #message::after { + content: ''; + display: block; + clear: both; + } + #actions { + /* + display: flex; + width: 100%; + justify-content: flex-end; + */ + display: block; + float: right; + + margin-top: var(--uui-size-space-4); + margin-bottom: calc(var(--uui-size-space-2) * -1); + } + `, + ]; } declare global { diff --git a/packages/uui-toast-notification/lib/uui-toast-notification.element.ts b/packages/uui-toast-notification/lib/uui-toast-notification.element.ts index b19ada5eb..f0f8e0d0b 100644 --- a/packages/uui-toast-notification/lib/uui-toast-notification.element.ts +++ b/packages/uui-toast-notification/lib/uui-toast-notification.element.ts @@ -18,107 +18,6 @@ import { UUIToastNotificationEvent } from './UUIToastNotificationEvent'; */ @defineElement('uui-toast-notification') export class UUIToastNotificationElement extends LitElement { - static styles = [ - UUITextStyles, - css` - :host { - --uui-toast-notification-margin: var(--uui-size-space-2); - - position: relative; - display: block; - width: 100%; - max-width: 400px; - margin: 0 var(--uui-toast-notification-margin); - box-sizing: border-box; - - height: 0; - pointer-events: none; - - transition: height - var(--uui-toast-notification-animation-duration, 480ms) ease-in-out; - } - :host([is-open]) { - pointer-events: all; - transition-timing-function: cubic-bezier( - 0.19, - 1, - 0.22, - 1 - ); /* easeOutExpo */ - } - - #toast { - position: relative; - display: block; - padding: calc(var(--uui-toast-notification-margin) * 0.5) 0; - width: 100%; - max-width: 400px; - } - #toast.animate { - position: absolute; - } - - #toast > div { - position: relative; - display: block; - - box-sizing: border-box; - box-shadow: var(--uui-shadow-depth-1); - background-color: var(--uui-color-surface); - padding: var(--uui-size-layout-1); - padding-right: var(--uui-size-layout-1); - padding-left: var(--uui-size-layout-3); - border-radius: calc(var(--uui-border-radius) * 2); - - opacity: 0; - transition: opacity - var(--uui-toast-notification-animation-duration, 480ms); - } - :host([is-open]) #toast > div { - opacity: 1; - } - - #close { - float: right; - margin-top: -6px; - margin-left: var(--uui-size-space-1); - margin-bottom: -4px; - } - - #close > uui-button { - --uui-button-border-radius: 50px 50px 50px 50px; - --uui-button-padding-left-factor: 1.5; - --uui-button-padding-right-factor: 1.5; - } - - :host #toast > div { - background-color: var(--uui-color-surface); - color: var(--uui-color-text); - border-color: var(--uui-color-surface); - } - :host([color='default']) #toast > div { - background-color: var(--uui-color-default); - color: var(--uui-color-default-contrast); - border-color: var(--uui-color-default-standalone); - } - :host([color='positive']) #toast > div { - background-color: var(--uui-color-positive); - color: var(--uui-color-positive-contrast); - border-color: var(--uui-color-positive-standalone); - } - :host([color='warning']) #toast > div { - background-color: var(--uui-color-warning); - color: var(--uui-color-warning-contrast); - border-color: var(--uui-color-warning-standalone); - } - :host([color='danger']) #toast > div { - background-color: var(--uui-color-danger); - color: var(--uui-color-danger-contrast); - border-color: var(--uui-color-danger-standalone); - } - `, - ]; - /** * Changes the color of the notification to one of the predefined, symbolic colors. Example: set this to danger to indicate errors. * @type {'' | 'default' | 'positive' | 'warning' | 'danger'} @@ -353,6 +252,107 @@ export class UUIToastNotificationElement extends LitElement { `; } + + static styles = [ + UUITextStyles, + css` + :host { + --uui-toast-notification-margin: var(--uui-size-space-2); + + position: relative; + display: block; + width: 100%; + max-width: 400px; + margin: 0 var(--uui-toast-notification-margin); + box-sizing: border-box; + + height: 0; + pointer-events: none; + + transition: height + var(--uui-toast-notification-animation-duration, 480ms) ease-in-out; + } + :host([is-open]) { + pointer-events: all; + transition-timing-function: cubic-bezier( + 0.19, + 1, + 0.22, + 1 + ); /* easeOutExpo */ + } + + #toast { + position: relative; + display: block; + padding: calc(var(--uui-toast-notification-margin) * 0.5) 0; + width: 100%; + max-width: 400px; + } + #toast.animate { + position: absolute; + } + + #toast > div { + position: relative; + display: block; + + box-sizing: border-box; + box-shadow: var(--uui-shadow-depth-1); + background-color: var(--uui-color-surface); + padding: var(--uui-size-layout-1); + padding-right: var(--uui-size-layout-1); + padding-left: var(--uui-size-layout-3); + border-radius: calc(var(--uui-border-radius) * 2); + + opacity: 0; + transition: opacity + var(--uui-toast-notification-animation-duration, 480ms); + } + :host([is-open]) #toast > div { + opacity: 1; + } + + #close { + float: right; + margin-top: -6px; + margin-left: var(--uui-size-space-1); + margin-bottom: -4px; + } + + #close > uui-button { + --uui-button-border-radius: 50px 50px 50px 50px; + --uui-button-padding-left-factor: 1.5; + --uui-button-padding-right-factor: 1.5; + } + + :host #toast > div { + background-color: var(--uui-color-surface); + color: var(--uui-color-text); + border-color: var(--uui-color-surface); + } + :host([color='default']) #toast > div { + background-color: var(--uui-color-default); + color: var(--uui-color-default-contrast); + border-color: var(--uui-color-default-standalone); + } + :host([color='positive']) #toast > div { + background-color: var(--uui-color-positive); + color: var(--uui-color-positive-contrast); + border-color: var(--uui-color-positive-standalone); + } + :host([color='warning']) #toast > div { + background-color: var(--uui-color-warning); + color: var(--uui-color-warning-contrast); + border-color: var(--uui-color-warning-standalone); + } + :host([color='danger']) #toast > div { + background-color: var(--uui-color-danger); + color: var(--uui-color-danger-contrast); + border-color: var(--uui-color-danger-standalone); + } + `, + ]; } declare global { diff --git a/packages/uui-toggle/lib/uui-toggle.element.ts b/packages/uui-toggle/lib/uui-toggle.element.ts index 7416c34c5..4e554449d 100644 --- a/packages/uui-toggle/lib/uui-toggle.element.ts +++ b/packages/uui-toggle/lib/uui-toggle.element.ts @@ -32,6 +32,19 @@ export class UUIToggleElement extends UUIBooleanInputElement { */ static readonly formAssociated = true; + constructor() { + super('switch'); + } + + renderCheckbox() { + return html` +
+
${iconCheck}
+
${iconWrong}
+
+ `; + } + static styles = [ ...UUIBooleanInputElement.styles, UUIHorizontalShakeKeyframes, @@ -170,19 +183,6 @@ export class UUIToggleElement extends UUIBooleanInputElement { } `, ]; - - constructor() { - super('switch'); - } - - renderCheckbox() { - return html` -
-
${iconCheck}
-
${iconWrong}
-
- `; - } } declare global {