diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 855f10e41d..73eca9b8e5 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -86,7 +86,7 @@ "typescript": "^5.5.3", "typescript-eslint": "^8.0.1", "typescript-json-schema": "^0.64.0", - "vite": "^5.3.4", + "vite": "^5.4.6", "vite-plugin-static-copy": "^1.0.6", "vite-tsconfig-paths": "^4.3.2", "web-component-analyzer": "^2.0.0" @@ -18564,9 +18564,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "node_modules/picomatch": { @@ -18785,9 +18785,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -18805,8 +18805,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -20389,9 +20389,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -22098,14 +22098,14 @@ } }, "node_modules/vite": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz", - "integrity": "sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dev": true, "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c538da2b4c..ff08714ffc 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -262,7 +262,7 @@ "typescript": "^5.5.3", "typescript-eslint": "^8.0.1", "typescript-json-schema": "^0.64.0", - "vite": "^5.3.4", + "vite": "^5.4.6", "vite-plugin-static-copy": "^1.0.6", "vite-tsconfig-paths": "^4.3.2", "web-component-analyzer": "^2.0.0" diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts index 11b9325f8b..a76e4e56c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-input.element.ts @@ -175,7 +175,7 @@ export class UmbMultipleColorPickerInputElement extends UUIFormControlMixin(UmbL
${repeat( this._items, - (item) => item.value, + (item, index) => index, (item, index) => html` this.#onChange(event, index)} @delete=${(event: UmbDeleteEvent) => this.#deleteItem(event, index)}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts index 2513799e64..6652dce46e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts @@ -84,12 +84,25 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin( this.dispatchEvent(new UmbInputEvent()); } + #onLabelKeydown(event: KeyboardEvent) { + event.stopPropagation(); + const target = event.currentTarget as UUIInputElement; + if (event.key === 'Enter' && target.value) { + this.dispatchEvent(new CustomEvent('enter')); + } + } + #onLabelChange(event: UUIInputEvent) { event.stopPropagation(); this.label = event.target.value as string; this.dispatchEvent(new UmbChangeEvent()); } + #onValueKeydown(event: KeyboardEvent) { + event.stopPropagation(); + if (event.key === 'Enter') this.#onColorClick(); + } + #onValueChange(event: UUIInputEvent) { event.stopPropagation(); this.value = event.target.value as string; @@ -145,6 +158,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin( placeholder=${this.localize.term('general_value')} required=${this.required} required-message="Value is missing" + @keydown=${this.#onValueKeydown} @input=${this.#onValueInput} @change=${this.#onValueChange}> { it('has a clearSelection method', () => { expect(manager).to.have.property('clearSelection').that.is.a('function'); }); + + it('has a setAllowLimitation method', () => { + expect(manager).to.have.property('setAllowLimitation').that.is.a('function'); + }); }); }); @@ -150,6 +154,15 @@ describe('UmbSelectionManager', () => { manager.select('3'); expect(manager.getSelection()).to.deep.equal([]); }); + + it('can not select an item if it does not pass the allow function', () => { + manager.setAllowLimitation((item) => item !== '2'); + expect(() => manager.select('2')).to.throw(); + expect(manager.getSelection()).to.deep.equal([]); + + manager.select('1'); + expect(manager.getSelection()).to.deep.equal(['1']); + }); }); describe('Deselect', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts index d1127cbb8d..3e1cd91ca1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts @@ -18,6 +18,9 @@ export class UmbSelectionManager true; + constructor(host: UmbControllerHost) { super(host); } @@ -109,6 +112,9 @@ export class UmbSelectionManager boolean): void { + this.#allow = compareFn; + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu/workspace-action-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu/workspace-action-menu.element.ts index 40dd9778db..9f1e456f55 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu/workspace-action-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu/workspace-action-menu.element.ts @@ -78,8 +78,8 @@ export class UmbWorkspaceActionMenuElement extends UmbLitElement { } #popover-trigger { - --uui-button-padding-top-factor: 0.5; - --uui-button-padding-bottom-factor: 0.1; + --uui-button-padding-top-factor: 0; + --uui-button-padding-bottom-factor: 0.125; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index 643285640c..dc850eb0ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -41,6 +41,15 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< this.#selectionManager.setMultiple(true); this.#selectionManager.setSelectable(true); + const pickableFilter = this.data?.pickableFilter; + + if (pickableFilter) { + this.#selectionManager.setAllowLimitation((unique) => { + const option = this.data?.options.find((o) => o.unique === unique); + return option ? pickableFilter(option) : true; + }); + } + // Only display variants that are relevant to pick from, i.e. variants that are draft or published with pending changes: this._options = this.data?.options.filter( diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts index 52d69df997..ccfd6c36d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts @@ -1,5 +1,14 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; -import { css, customElement, html, nothing, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; +import { + css, + customElement, + html, + nothing, + property, + repeat, + state, + type PropertyValues, +} from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; @@ -37,6 +46,17 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { @property({ attribute: false }) public pickableFilter?: (item: UmbDocumentVariantOptionModel) => boolean; + protected override updated(_changedProperties: PropertyValues): void { + super.updated(_changedProperties); + + if (this.selectionManager && this.pickableFilter) { + this.#selectionManager.setAllowLimitation((unique) => { + const option = this.variantLanguageOptions.find((o) => o.unique === unique); + return option ? this.pickableFilter!(option) : true; + }); + } + } + override render() { return this.variantLanguageOptions.length ? repeat( diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index a560e4405f..9f11620d47 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -536,11 +536,15 @@ export class UmbDocumentWorkspaceContext const selected = activeVariantIds.concat(changedVariantIds); // Selected can contain entries that are not part of the options, therefor the modal filters selection based on options. + const readOnlyCultures = this.readOnlyState.getStates().map((s) => s.variantId.culture); + const selectedCultures = selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i); + const writable = selectedCultures.filter((x) => readOnlyCultures.includes(x) === false); + const options = await firstValueFrom(this.variantOptions); return { options, - selected: selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i), + selected: writable, }; }