Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

multiselect-combo-box

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

multiselect-combo-box - npm Package Compare versions

Comparing version 2.5.0-beta.3 to 3.0.0-alpha

multiselect-combo-box.d.ts

1

multiselect-combo-box.js
import './theme/lumo/multiselect-combo-box.js';
export * from './src/multiselect-combo-box.js';

123

package.json
{
"description": "A multi select combo box web component based on Polymer 3 and the vaadin-combo-box",
"keywords": [
"multiselect-combo-box",
"web-components",
"vaadin",
"polymer-3",
"polymer"
],
"name": "multiselect-combo-box",
"version": "3.0.0-alpha",
"description": "A multiselect combo box web component compatible with the Vaadin Web Components",
"license": "Apache-2.0",
"repository": {

@@ -14,14 +10,19 @@ "type": "git",

},
"name": "multiselect-combo-box",
"version": "2.5.0-beta.3",
"main": "multiselect-combo-box.js",
"directories": {
"test": "test"
},
"author": "Goran",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/gatanaso/multiselect-combo-box/issues"
},
"main": "multiselect-combo-box.js",
"module": "multiselect-combo-box.js",
"scripts": {
"build": "rimraf build && rollup -c rollup.config.js",
"lint": "npm-run-all --parallel lint:*",
"lint:css": "stylelint src/*.js theme/**/*-styles.js",
"lint:js": "eslint --ext .js,.ts *.js src test theme",
"lint:types": "tsc",
"start": "web-dev-server --node-resolve --open",
"test": "web-test-runner test/*.test.js --node-resolve"
},
"files": [
"multiselect-*.d.ts",
"multiselect-*.js",

@@ -31,46 +32,64 @@ "src",

],
"resolutions": {
"inherits": "2.0.3",
"samsam": "1.1.3",
"supports-color": "3.1.2",
"type-detect": "1.0.0"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
},
"scripts": {
"test": "npm run lint && wct --npm",
"lint": "npm-run-all --parallel lint:*",
"lint:css": "stylelint --syntax html src theme demo test",
"lint:html": "eslint *.html demo test --ext .html",
"lint:js": "eslint *.js src",
"lint:polymer": "polymer lint --rules=polymer-3 --input ./src/*.js ./theme/**/*.js",
"start": "npm run lint && wct --npm && polymer serve --npm --open"
},
"keywords": [
"multiselect-combo-box",
"web-components",
"vaadin",
"polymer-3",
"polymer"
],
"dependencies": {
"@polymer/iron-resizable-behavior": "^3.0.1",
"@open-wc/dedupe-mixin": "^1.3.0",
"@polymer/polymer": "^3.0.0",
"@vaadin/vaadin-combo-box": "^5.0.9",
"@vaadin/vaadin-control-state-mixin": "^2.1.3",
"@vaadin/vaadin-lumo-styles": "^1.5.0",
"@vaadin/vaadin-material-styles": "^1.2.3",
"@vaadin/vaadin-text-field": "^2.4.8",
"@vaadin/vaadin-themable-mixin": "^1.4.4"
"@vaadin/combo-box": "^22.0.0",
"@vaadin/component-base": "^22.0.0",
"@vaadin/field-base": "^22.0.0",
"@vaadin/input-container": "^22.0.0",
"@vaadin/polymer-legacy-adapter": "^22.0.2",
"@vaadin/vaadin-lumo-styles": "^22.0.0",
"@vaadin/vaadin-material-styles": "^22.0.0",
"@vaadin/vaadin-themable-mixin": "^22.0.0"
},
"devDependencies": {
"@esm-bundle/chai": "^4.3.4",
"@polymer/iron-component-page": "^4.0.1",
"@polymer/iron-demo-helpers": "^3.0.0",
"@webcomponents/webcomponentsjs": "^2.0.0",
"eslint": "^6.0.1",
"eslint-config-vaadin": "^0.2.7",
"eslint-plugin-html": "^6.0.0",
"husky": "^3.0.8",
"@rollup/plugin-node-resolve": "^11.2.1",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"@vaadin/testing-helpers": "^0.3.2",
"@web/dev-server": "^0.1.28",
"@web/rollup-plugin-html": "^1.10.1",
"@web/test-runner": "^0.13.22",
"@web/test-runner-commands": "^0.6.0",
"@web/test-runner-playwright": "^0.8.8",
"eslint": "^8.3.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-simple-import-sort": "7.0.0",
"husky": "^7.0.4",
"lint-staged": "^12.1.2",
"npm-run-all": "^4.1.5",
"stylelint": "^10.1.0",
"stylelint-config-vaadin": "^0.1.4",
"wct-browser-legacy": "^1.0.2",
"wct-istanbul": "^0.14.3",
"web-component-tester": "^6.9.2"
"postcss-lit": "^0.2.0",
"prettier": "^2.4.1",
"prettier-plugin-package": "^1.3.0",
"replace-in-file": "^6.3.2",
"rimraf": "^3.0.2",
"rollup": "^2.66.1",
"rollup-plugin-terser": "^7.0.2",
"simple-git-hooks": "^2.7.0",
"sinon": "^12.0.1",
"stylelint": "^14.1.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-vaadin": "^0.3.0",
"typescript": "^4.5.2"
},
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
]
},
"simple-git-hooks": {
"pre-commit": "npx lint-staged"
}
}

@@ -1,42 +0,30 @@

[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/multiselect-combo-box)
[![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/gatanasomultiselect-combo-box)
[![Build Status](https://travis-ci.org/gatanaso/multiselect-combo-box.svg?branch=master)](https://travis-ci.org/gatanaso/multiselect-combo-box)
[![codecov](https://codecov.io/gh/gatanaso/multiselect-combo-box/branch/master/graph/badge.svg)](https://codecov.io/gh/gatanaso/multiselect-combo-box)
[![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/gatanasomultiselect-combo-box.svg)](https://vaadin.com/directory/component/gatanasomultiselect-combo-box)
[![Tests](https://github.com/gatanaso/multiselect-combo-box/actions/workflows/tests.yml/badge.svg)](https://github.com/gatanaso/multiselect-combo-box/actions/workflows/tests.yml)
# \<multiselect-combo-box\>
A multiselect combo box web component based on [Polymer](https://github.com/Polymer/polymer) and the [`vaadin-combo-box`](https://github.com/vaadin/vaadin-combo-box).
A multiselect combo box web component compatible with the [Vaadin Web Components](https://github.com/vaadin/web-components)
*Compatible with the core set of [Vaadin Components](https://github.com/vaadin/vaadin-core).*
#### [Demo ↗](https://multiselect-combo-box.web.app) | [Material Theme Demo ↗](https://multiselect-combo-box-material.web.app)
#### [Live Demo ↗](https://multiselect-combo-box.web.app/demo/) | [Material Theme Live Demo ↗](https://multiselect-combo-box-material.web.app/demo/material)
## Getting started
## Getting started
### Polymer 3 (npm)
Install the `multiselect-combo-box`:
```
npm install multiselect-combo-box --save
```
Once installed, import in your applicaiton:
```
import 'multiselect-combo-box/multiselect-combo-box.js';
```
Add to your page:
```
<multiselect-combo-box label="Select items"></multiselect-combo-box>
```
### Polymer 2.x (bower)
Install the `multiselect-combo-box`:
```
bower install --save gatanaso/multiselect-combo-box#1.1.0
```
Once installed, import in your applicaiton:
```
<link rel="import" href="bower_components/multiselect-combo-box/multiselect-combo-box.html">
```
Add to your page:
```
<multiselect-combo-box label="Select items"></multiselect-combo-box>
```

@@ -48,8 +36,9 @@ > For more usage examples, see the [demo](https://multiselect-combo-box.firebaseapp.com/demo/).

1. Fork the `multiselect-combo-box` repository and clone it locally.
1. Make sure you have the [Polymer CLI](https://www.npmjs.com/package/polymer-cli) and npm (packaged with [Node.js](https://nodejs.org)) installed locally.
1. Make sure you have npm (packaged with [Node.js](https://nodejs.org)) installed locally.
1. To install the element's dependencies, in the `multiselect-combo-box` directory, run: `npm install`
* if using the Polymer 2.x version, also run: `bower install`
### Running the project locally
To perform linting, run the tests and automatically open the demo page, run:
```

@@ -61,6 +50,7 @@ npm run start

* `master` the latest (Polymer 3) version of the `multiselect-combo-box`
* `polymer-2` the Polymer 2.x version of the `multiselect-combo-box`
- `master` the latest version of the `multiselect-combo-box`
- `polymer-2` the Polymer 2.x version of the `multiselect-combo-box` which is not maintained anymore.
## Java API
The Vaadin Flow Java compatible version of this component is available on the [Vaadin Directory](https://vaadin.com/directory/component/multiselect-combo-box) and [GitHub](https://github.com/gatanaso/multiselect-combo-box-flow).
/**
* @polymerMixin
*/
export const MultiselectComboBoxMixin = (base) => class extends base {
export const MultiselectComboBoxMixin = (base) =>
class extends base {
static get properties() {
return {
/**
* A full set of items to filter the visible options from.
* The items can be of either `String` or `Object` type.
*/
items: {
type: Array
},
static get properties() {
return {
/**
* The list of items.
*/
items: Array,
/**
* The item property used for a visual representation of the item.
* @attr {string} item-label-path
*/
itemLabelPath: {
type: String
}
};
}
/**
* The input placeholder.
*/
placeholder: {
type: String,
value: ''
},
/**
* This attribute indicates that the component has a value.
*/
hasValue: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* This attribute indicates that the component has a label.
*/
hasLabel: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* This attribute indicates that the component is rendered in 'compact mode'.
* In this mode, the component displays the number of items currently selected.
*/
compactMode: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* Custom function for generating the display label when in compact mode.
*
* This function receives the array of selected items and should return
* a string value that will be used as the display label.
*/
compactModeLabelGenerator: Function,
/**
* The item property to be used as the `label` in combo-box.
*/
itemLabelPath: String,
/**
* The item property to be used as the `value` of combo-box.
*/
itemValuePath: String,
/**
* Path for the id of the item. If `items` is an array of objects,
* the `itemIdPath` is used to compare and identify the same item
* in `selectedItem`.
*/
itemIdPath: String,
/**
* The theme name attribute.
* Used to communicate theme information to
* component internals (currently used for the material theme).
*/
theme: String,
/**
* Set to true to disable this element.
*/
disabled: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* Set to true to display the clear icon which clears the input.
*/
clearButtonVisible: {
type: Boolean,
value: false
}
};
}
/**
* Returns the item display label.
* @protected
*/
_getItemLabel(item, itemLabelPath) {
return item && item.hasOwnProperty(itemLabelPath) ? item[itemLabelPath] : item;
}
/**
* Retrieves the component display label when in compact mode.
* @protected
*/
_getCompactModeLabel(items) {
if (this.compactModeLabelGenerator && typeof this.compactModeLabelGenerator === 'function') {
return this.compactModeLabelGenerator(items);
} else {
const suffix = (items.length === 0 || items.length > 1) ? 'values' : 'value';
return `${items.length} ${suffix}`;
/**
* Returns the item display label.
* @protected
*/
_getItemLabel(item, itemLabelPath) {
return item && Object.prototype.hasOwnProperty.call(item, itemLabelPath) ? item[itemLabelPath] : item;
}
}
};
};

@@ -1,516 +0,644 @@

import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import {ControlStateMixin} from '@vaadin/vaadin-control-state-mixin/vaadin-control-state-mixin.js';
import {ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import {ThemePropertyMixin} from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
import {ComboBoxPlaceholder} from '@vaadin/vaadin-combo-box/src/vaadin-combo-box-placeholder.js';
import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
import {IronResizableBehavior} from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';
import {MultiselectComboBoxMixin} from './multiselect-combo-box-mixin.js';
import '@polymer/polymer/lib/elements/dom-repeat.js';
import '@vaadin/polymer-legacy-adapter/template-renderer.js';
import './multiselect-combo-box-chip.js';
import './multiselect-combo-box-container.js';
import './multiselect-combo-box-internal.js';
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
import { processTemplates } from '@vaadin/component-base/src/templates.js';
import { InputControlMixin } from '@vaadin/field-base/src/input-control-mixin.js';
import { InputController } from '@vaadin/field-base/src/input-controller.js';
import { LabelledInputController } from '@vaadin/field-base/src/labelled-input-controller.js';
import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
import { css, registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { MultiselectComboBoxMixin } from './multiselect-combo-box-mixin.js';
import '@vaadin/vaadin-combo-box/src/vaadin-combo-box-light.js';
import './multiselect-combo-box-input.js';
const multiselectComboBox = css`
[hidden] {
display: none !important;
}
{
/**
* `multiselect-combo-box`
*
* Multi select combo box based on the vaadin-combo-box
*
* @customElement
* @polymer
* @demo demo/index.html
* @appliesMixin MultiselectComboBoxMixin
*/
class MultiselectComboBox extends
ControlStateMixin(
ThemePropertyMixin(
ThemableMixin(
MultiselectComboBoxMixin(mixinBehaviors([IronResizableBehavior], PolymerElement))))) {
:host([has-value]) ::slotted(input:placeholder-shown) {
color: transparent !important;
}
static get template() {
return html`
<style>
:host {
display: inline-flex;
}
:host([has-value]:not([readonly])) [class$='container'] {
width: auto;
}
:host([hidden]) {
display: none !important;
}
[part='compact-mode-prefix'] {
display: flex;
align-items: center;
}
:host::before {
content: "\\2003";
width: 0;
box-sizing: border-box;
display: inline-flex;
align-items: center;
}
::slotted(input) {
flex-basis: 80px;
}
`;
.multiselect-combo-box-container {
display: flex;
flex-direction: column;
min-width: 100%;
max-width: 100%;
}
registerStyles('multiselect-combo-box', [inputFieldShared, multiselectComboBox], {
moduleId: 'multiselect-combo-box-styles'
});
[part="label"]:empty {
display: none;
}
/**
* `<multiselect-combo-box>` is a web component that wraps `<vaadin-combo-box>` and extends
* its functionality to allow selecting multiple items, in addition to basic features.
*
* ```html
* <multiselect-combo-box id="comboBox"></multiselect-combo-box>
* ```
*
* ```js
* const comboBox = document.querySelector('#comboBox');
* comboBox.items = ['apple', 'banana', 'lemon', 'orange'];
* comboBox.selectedItems = ['lemon', 'orange'];
* ```
*
* ### Styling
*
* The following shadow DOM parts are available for styling:
*
* Part name | Description
* -----------------------|----------------
* `chip` | Chip shown for every selected item in default mode
* `compact-mode-prefix` | The selected items counter shown in compact mode
* `label` | The label element
* `input-field` | The element that wraps prefix, value and suffix
* `clear-button` | The clear button
* `error-message` | The error message element
* `helper-text` | The helper text element wrapper
* `required-indicator` | The `required` state indicator element
* `toggle-button` | The toggle button
*
* The following state attributes are available for styling:
*
* Attribute | Description
* -----------------------|-----------------
* `compact-mode` | Set when the element uses compact mode
* `disabled` | Set to a disabled element
* `has-value` | Set when the element has a value
* `has-label` | Set when the element has a label
* `has-helper` | Set when the element has helper text or slot
* `has-error-message` | Set when the element has an error message
* `invalid` | Set when the element is invalid
* `focused` | Set when the element is focused
* `focus-ring` | Set when the element is keyboard focused
* `opened` | Set when the dropdown is open
* `ordered` | Set when the element uses ordered mode
* `readonly` | Set to a readonly element
*
* ### Internal components
*
* In addition to `<multiselect-combo-box>` itself, the following internal
* components are themable:
*
* - `<multiselect-combo-box-overlay>` - has the same API as `<vaadin-overlay>`.
* - `<multiselect-combo-box-item>` - has the same API as `<vaadin-item>`.
* - `<multiselect-combo-box-container>` - has the same API as `<vaadin-input-container>`.
*
* Note: the `theme` attribute value set on `<multiselect-combo-box>` is
* propagated to these components.
*
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
*
* @fires {Event} change - Fired when the user commits a value change.
* @fires {CustomEvent} custom-values-set - Fired when the user sets a custom value.
* @fires {CustomEvent} filter-value-changed - Fired when the `filterValue` property changes.
* @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
* @fires {CustomEvent} selected-items-changed - Fired when the `selectedItems` property changes.
*
* @extends HTMLElement
* @mixes DirMixin
* @mixes ThemableMixin
* @mixes InputControlMixin
* @mixes MultiselectComboBoxMixin
*/
class MultiselectComboBox extends MultiselectComboBoxMixin(InputControlMixin(ThemableMixin(DirMixin(PolymerElement)))) {
static get is() {
return 'multiselect-combo-box';
}
[part="combo-box"] {
display: block;
width: 100%;
min-width: 0;
}
static get template() {
return html`
<div class="multiselect-combo-box-container">
<div part="label">
<slot name="label"></slot>
<span part="required-indicator" aria-hidden="true" on-click="focus"></span>
</div>
[part="combo-box"][hidden] {
display: none;
}
<multiselect-combo-box-internal
id="comboBox"
items="[[items]]"
item-id-path="[[itemIdPath]]"
item-label-path="[[itemLabelPath]]"
item-value-path="[[itemValuePath]]"
disabled="[[disabled]]"
readonly="[[readonly]]"
auto-open-disabled="[[autoOpenDisabled]]"
allow-custom-value="[[allowCustomValues]]"
data-provider="[[dataProvider]]"
filter="{{filterValue}}"
filtered-items="[[filteredItems]]"
opened="{{opened}}"
renderer="[[renderer]]"
theme$="[[theme]]"
suppress-template-warning
on-combo-box-item-selected="_onComboBoxItemSelected"
on-change="_onComboBoxChange"
on-custom-value-set="_onCustomValueSet"
>
<multiselect-combo-box-container
part="input-field"
readonly="[[readonly]]"
disabled="[[disabled]]"
invalid="[[invalid]]"
theme$="[[theme]]"
>
<div
part="compact-mode-prefix"
hidden$="[[_isCompactModeHidden(readonly, compactMode, _hasValue)]]"
slot="prefix"
>
[[_getCompactModeLabel(selectedItems, compactModeLabelGenerator)]]
</div>
<template id="repeat" is="dom-repeat" items="[[selectedItems]]" slot="prefix">
<multiselect-combo-box-chip
slot="prefix"
part="chip"
item="[[item]]"
label="[[_getItemLabel(item, itemLabelPath)]]"
hidden$="[[_isTokensHidden(readonly, compactMode, _hasValue)]]"
on-item-removed="_onItemRemoved"
on-mousedown="_preventBlur"
></multiselect-combo-box-chip>
</template>
<slot name="input"></slot>
<div id="clearButton" part="clear-button" slot="suffix"></div>
<div id="toggleButton" class="toggle-button" part="toggle-button" slot="suffix"></div>
</multiselect-combo-box-container>
</multiselect-combo-box-internal>
[part="input-field"] {
width: 100%;
min-width: 0;
position: relative;
}
<div part="helper-text">
<slot name="helper"></slot>
</div>
:host([disabled]) [part="label"] {
pointer-events: none;
}
<div part="error-message">
<slot name="error-message"></slot>
</div>
</div>
`;
}
</style>
static get properties() {
return {
/**
* Set true to prevent the overlay from opening automatically.
* @attr {boolean} auto-open-disabled
*/
autoOpenDisabled: Boolean,
<div class="multiselect-combo-box-container">
/**
* When true, the component does not render chips for every selected value.
* Instead, only the number of currently selected items is shown.
* @attr {boolean} compact-mode
*/
compactMode: {
type: Boolean,
reflectToAttribute: true
},
<label part="label">[[label]]</label>
/**
* Custom function for generating the display label when in compact mode.
*
* This function receives the array of selected items and should return
* a string value that will be used as the display label.
*/
compactModeLabelGenerator: {
type: Object
},
<div part="readonly-container" hidden\$="[[!readonly]]">
[[_getReadonlyValue(selectedItems, itemLabelPath, compactMode, readonlyValueSeparator)]]
</div>
/**
* Path for the value of the item. If `items` is an array of objects,
* this property is used as a string value for the selected item.
* @attr {string} item-value-path
*/
itemValuePath: String,
<vaadin-combo-box-light
id="comboBox"
part="combo-box"
hidden\$="[[readonly]]"
items="[[items]]"
item-id-path="[[itemIdPath]]"
item-label-path="[[itemLabelPath]]"
item-value-path="[[itemValuePath]]"
on-change="_comboBoxValueChanged"
disabled="[[disabled]]"
page-size="[[pageSize]]"
filter="{{filterValue}}"
filtered-items="[[filteredItems]]"
allow-custom-value="[[allowCustomValues]]"
on-custom-value-set="_handleCustomValueSet">
/**
* Path for the id of the item, used to detect whether the item is selected.
* @attr {string} item-id-path
*/
itemIdPath: {
type: String
},
<multiselect-combo-box-input
id="input"
class="input"
part="input-field"
placeholder="[[placeholder]]"
item-label-path="[[itemLabelPath]]"
items="[[selectedItems]]"
compact-mode="[[compactMode]]"
compact-mode-label-generator="[[compactModeLabelGenerator]]"
on-item-removed="_handleItemRemoved"
on-remove-all-items="_handleRemoveAllItems"
has-value="[[hasValue]]"
has-label="[[hasLabel]]"
theme\$="[[theme]]"
disabled="[[disabled]]"
clear-button-visible="[[clearButtonVisible]]">
</multiselect-combo-box-input>
</vaadin-combo-box-light>
/**
* The list of selected items.
* Note: modifying the selected items creates a new array each time.
*/
selectedItems: {
type: Array,
value: () => [],
notify: true
},
<div part="error-message" hidden\$="[[!invalid]]">[[errorMessage]]</div>
/**
* True if the dropdown is open, false otherwise.
*/
opened: {
type: Boolean,
notify: true,
value: false,
reflectToAttribute: true
},
</div>
`;
}
/**
* When true, the list of selected items is kept ordered in ascending lexical order.
*
* When `itemLabelPath` is specified, corresponding property is used for ordering.
* Otherwise the items themselves are compared using `localCompare`.
*/
ordered: {
type: Boolean,
value: false,
reflectToAttribute: true
},
static get is() {
return 'multiselect-combo-box';
}
/**
* Number of items fetched at a time from the data provider.
* @attr {number} page-size
*/
pageSize: {
type: Number,
value: 50,
observer: '_pageSizeChanged'
},
constructor() {
super();
this._boundCustomOverlaySelectedItemChanged = this._customOverlaySelectedItemChanged.bind(this);
}
/**
* Function that provides items lazily. Receives two arguments:
*
* - `params` - Object with the following properties:
* - `params.page` Requested page index
* - `params.pageSize` Current page size
* - `params.filter` Currently applied filter
*
* - `callback(items, size)` - Callback function with arguments:
* - `items` Current page of items
* - `size` Total number of items.
*/
dataProvider: {
type: Object,
observer: '_dataProviderChanged'
},
ready() {
super.ready();
/**
* The join separator used for the 'display value' when in read-only mode.
* @attr {string} readonly-value-separator
*/
readonlyValueSeparator: {
type: String,
value: ', '
},
// replace listener to modify default behavior
this.$.comboBox.$.overlay.removeEventListener('selection-changed', this.$.comboBox._boundOverlaySelectedItemChanged);
this.$.comboBox.$.overlay.addEventListener('selection-changed', this._boundCustomOverlaySelectedItemChanged);
/**
* When true, the user can input a value that is not present in the items list.
* @attr {boolean} allow-custom-values
*/
allowCustomValues: {
type: Boolean,
value: false
},
// modify check to allow custom renderers
this.$.comboBox.$.overlay._isItemSelected = this._customIsSelected.bind(this);
/**
* Custom function for rendering the content of every item.
* Receives three arguments:
*
* - `root` The `<multiselect-combo-box-item>` internal container DOM element.
* - `comboBox` The reference to the underlying `<vaadin-combo-box>` element.
* - `model` The object with the properties related with the rendered
* item, contains:
* - `model.index` The index of the rendered item.
* - `model.item` The item.
*/
renderer: Function,
this._observer = new FlattenedNodesObserver(this, (info) => {
this._setTemplateFromNodes(info.addedNodes);
});
/**
* Filtering string the user has typed into the input field.
* @attr {string} filter-value
*/
filterValue: {
type: String,
value: '',
notify: true
},
this._notifyReady(); // only relevant when used with Vaadin Flow
}
/**
* A subset of items, filtered based on the user input. Filtered items
* can be assigned directly to omit the internal filtering functionality.
* The items can be of either `String` or `Object` type.
*/
filteredItems: Array,
static get properties() {
return {
/**
* The component label.
*/
label: {
type: String,
value: '',
observer: '_labelChanged'
},
/** @protected */
_hasValue: {
type: Boolean,
value: false
}
};
}
/**
* This attribute indicates that the component has a label.
*/
hasLabel: {
type: Boolean,
value: false,
reflectToAttribute: true
},
static get observers() {
return [
'_selectedItemsChanged(selectedItems, selectedItems.*)',
'_updateReadOnlyMode(inputElement, readonly, itemLabelPath, compactMode, readonlyValueSeparator, selectedItems, selectedItems.*)',
'_updateItems(ordered, compactMode, itemLabelPath, selectedItems, selectedItems.*)'
];
}
/**
* The title attribute.
*/
title: {
type: String,
value: '',
reflectToAttribute: true
},
/**
* Used by `ClearButtonMixin` as a reference to the clear button element.
* @protected
* @return {!HTMLElement}
*/
get clearElement() {
return this.$.clearButton;
}
/**
* The list of selected items.
*
* Note: modifying the selected items creates a new array each time.
*/
selectedItems: {
type: Array,
value: () => [],
notify: true
},
/** @protected */
ready() {
super.ready();
/**
* This attribute specifies if the list of selected items should be kept ordered in ascending lexical order.
*
* If the `itemLabelPath` is specified, that value is used for ordering, otherwise the items themselves are
* compared using `localCompare`.
*/
ordered: {
type: Boolean,
value: false,
reflectToAttribute: true
},
this.addController(
new InputController(this, (input) => {
this._setInputElement(input);
this._setFocusElement(input);
this.stateTarget = input;
this.ariaTarget = input;
})
);
this.addController(new LabelledInputController(this.inputElement, this._labelController));
/**
* Number of items fetched at a time from the dataprovider.
*
* This property is delegated to the underlying `vaadin-combo-box`.
*/
pageSize: {
type: Number,
value: 50,
observer: '_pageSizeObserver'
},
processTemplates(this);
}
/**
* The `readonly` attribute.
*/
readonly: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* Returns true if the current input value satisfies all constraints (if any).
* @return {boolean}
*/
checkValidity() {
return this.required ? this._hasValue : true;
}
/**
* The `required` attribute.
*/
required: {
type: Boolean,
value: false,
reflectToAttribute: true
},
/**
* Override method inherited from `InputMixin` to forward the input to combo-box.
* @protected
* @override
*/
_inputElementChanged(input) {
super._inputElementChanged(input);
/**
* The `invalid` attribute.
*/
invalid: {
type: Boolean,
value: false,
reflectToAttribute: true,
notify: true
},
if (input) {
this.$.comboBox._setInputElement(input);
}
}
/**
* The `invalid` state error-message.
*/
errorMessage: String,
/**
* Override method inherited from `InputMixin`
* to keep attribute after clearing the input.
* @protected
* @override
*/
_toggleHasValue() {
super._toggleHasValue(this._hasValue);
}
/**
* The join separator used for the 'display value' when in read-only mode.
*/
readonlyValueSeparator: {
type: String,
value: ', ' // default value
},
/**
* Override method inherited from `FocusMixin` to validate on blur.
* @param {boolean} focused
* @protected
*/
_setFocused(focused) {
super._setFocused(focused);
/**
* If `true`, the user can input a value that is not present in the items list.
* `value` property will be set to the input value in this case.
*
* This property is delegated to the underlying `vaadin-combo-box`.
*/
allowCustomValues: {
type: Boolean,
value: false
},
if (!focused) {
this.validate();
}
}
/**
* Custom function for rendering the content of every item.
* Receives three arguments:
*
* - `root` The `<vaadin-combo-box-item>` internal container DOM element.
* - `comboBox` The reference to the `<vaadin-combo-box>` element.
* - `model` The object with the properties related with the rendered
* item, contains:
* - `model.index` The index of the rendered item.
* - `model.item` The item.
*/
renderer: Function,
/** @private */
_isCompactModeHidden(readonly, compactMode, hasValue) {
return readonly || !compactMode || !hasValue;
}
_itemTemplate: Object,
/** @private */
_isTokensHidden(readonly, compactMode, hasValue) {
return readonly || compactMode || !hasValue;
}
/**
* Filtering string the user has typed into the input field.
*/
filterValue: {
type: String,
value: '',
notify: true
},
/** @private */
_updateItems(ordered, compactMode, itemLabelPath, selectedItems) {
// Set title when in compact mode to indicate which items are selected.
this.title = compactMode ? this._getDisplayValue(selectedItems, itemLabelPath, ', ') : undefined;
/**
* A subset of items, filtered based on the user input. Filtered items
* can be assigned directly to omit the internal filtering functionality.
* The items can be of either `String` or `Object` type.
*/
filteredItems: Array
};
if (ordered && !compactMode) {
this._sortSelectedItems(selectedItems);
}
}
static get observers() {
return [
'_selectedItemsObserver(selectedItems, selectedItems.*)',
'_templateOrRendererChanged(_itemTemplate, renderer)',
'_observeOffsetHeight(errorMessage, invalid, label)'
];
/** @private */
_updateReadOnlyMode(inputElement, readonly, itemLabelPath, compactMode, separator, selectedItems) {
if (inputElement) {
inputElement.value = readonly ? this._getReadonlyValue(selectedItems, itemLabelPath, compactMode, separator) : '';
}
}
/**
* Validates the component value.
*
* This method will set the components `valid` and `invalid` properties accordingly.
*/
validate() {
const valid = this.required ? this.hasValue : true;
this.invalid = !valid;
return valid;
/** @private */
_pageSizeChanged(pageSize, oldPageSize) {
if (Math.floor(pageSize) !== pageSize || pageSize <= 0) {
this.pageSize = oldPageSize;
console.error('"pageSize" value must be an integer > 0');
}
_selectedItemsObserver(selectedItems) {
this.hasValue = selectedItems && selectedItems.length > 0;
this.$.comboBox.pageSize = this.pageSize;
}
if (this.ordered && !this.compactMode) {
this._sortSelectedItems(selectedItems);
}
/** @private */
_selectedItemsChanged(selectedItems) {
this._hasValue = Boolean(selectedItems && selectedItems.length);
this.compactMode && (this.title = this._getDisplayValue(selectedItems, this.itemLabelPath, ', '));
this._toggleHasValue();
// manually force a render
this.$.comboBox.$.overlay._selectedItem = {};
// Re-render chips
this.__updateChips();
setTimeout(() => this._notifyResizeIfNeeded(), 0);
}
// Re-render scroller
this.$.comboBox.$.dropdown._scroller.__virtualizer.update();
_templateOrRendererChanged(template, renderer) {
this.$.comboBox._itemTemplate = template;
this.$.comboBox.renderer = renderer;
}
// Wait for chips to render
requestAnimationFrame(() => {
this.$.comboBox.$.dropdown._setOverlayWidth();
});
}
_observeOffsetHeight() {
this._notifyResizeIfNeeded();
/** @private */
_getCompactModeLabel(items) {
if (typeof this.compactModeLabelGenerator === 'function') {
return this.compactModeLabelGenerator(items);
}
_dispatchChangeEvent() {
this.dispatchEvent(new CustomEvent('change', {bubbles: true}));
}
const suffix = items.length === 0 || items.length > 1 ? 'values' : 'value';
return `${items.length} ${suffix}`;
}
_comboBoxValueChanged(event, selectedItem) {
const item = selectedItem || this.$.comboBox.selectedItem;
if (!item) {
return;
}
/** @private */
_getReadonlyValue(selectedItems, itemLabelPath, compactMode, readonlyValueSeparator) {
return compactMode
? this._getCompactModeLabel(selectedItems)
: this._getDisplayValue(selectedItems, itemLabelPath, readonlyValueSeparator);
}
const update = this.selectedItems.slice(0);
const index = this._findIndex(item, this.selectedItems, this.itemIdPath);
if (index !== -1) {
update.splice(index, 1);
} else {
update.push(item);
}
/** @private */
_getDisplayValue(selectedItems, itemLabelPath, valueSeparator) {
return selectedItems.map((item) => this._getItemLabel(item, itemLabelPath)).join(valueSeparator);
}
if (!selectedItem) {
this.$.comboBox.value = null;
/** @private */
_findIndex(item, selectedItems, itemIdPath) {
if (itemIdPath && item) {
for (let index = 0; index < selectedItems.length; index++) {
if (selectedItems[index] && selectedItems[index][itemIdPath] === item[itemIdPath]) {
return index;
}
}
this.selectedItems = update;
if (this.validate()) {
this._dispatchChangeEvent();
}
// reset the focus index, so a value-change event
// is not fired when the overlay is closed
this.$.comboBox._focusedIndex = -1;
return -1;
}
_handleCustomValueSet(event) {
event.preventDefault();
if (event.detail) {
this.$.input.value = null; // clear input
const customValuesSetEvent = new CustomEvent('custom-values-set', {
detail: event.detail,
composed: true,
cancelable: true,
bubbles: true
});
this.dispatchEvent(customValuesSetEvent);
}
}
return selectedItems.indexOf(item);
}
_customIsSelected(item, selectedItem, itemIdPath) {
if (item instanceof ComboBoxPlaceholder) {
return false;
}
return this._isSelected(item, this.selectedItems, itemIdPath);
}
/** @private */
__clearFilter() {
this.$.comboBox.clear();
}
_isSelected(item, selectedItems, itemIdPath) {
return this._findIndex(item, selectedItems, itemIdPath) !== -1;
}
/** @private */
__removeItem(item) {
const itemsCopy = [...this.selectedItems];
itemsCopy.splice(itemsCopy.indexOf(item), 1);
this.__updateSelection(itemsCopy);
}
_findIndex(item, selectedItems, itemIdPath) {
if (itemIdPath && item) {
for (let index = 0; index < selectedItems.length; index++) {
if (selectedItems[index] && selectedItems[index][itemIdPath] === item[itemIdPath]) {
return index;
}
}
return -1;
} else {
return selectedItems.indexOf(item);
}
}
/** @private */
__selectItem(item) {
const itemsCopy = [...this.selectedItems];
_handleItemRemoved(event) {
const item = event.detail.item;
const update = this.selectedItems.slice(0);
update.splice(update.indexOf(item), 1);
this.selectedItems = update;
if (this.validate()) {
this._dispatchChangeEvent();
}
const index = this._findIndex(item, itemsCopy, this.itemIdPath);
if (index !== -1) {
itemsCopy.splice(index, 1);
} else {
itemsCopy.push(item);
}
_handleRemoveAllItems() {
this.set('selectedItems', []);
if (this.validate()) {
this._dispatchChangeEvent();
}
}
this.__updateSelection(itemsCopy);
_getReadonlyValue(selectedItems, itemLabelPath, compactMode, readonlyValueSeparator) {
return compactMode ?
this._getCompactModeLabel(selectedItems) :
this._getDisplayValue(selectedItems, itemLabelPath, readonlyValueSeparator);
}
// Reset the overlay focused index.
this.$.comboBox._focusedIndex = -1;
_getDisplayValue(selectedItems, itemLabelPath, valueSeparator) {
return selectedItems.map(item => this._getItemLabel(item, itemLabelPath)).join(valueSeparator);
}
// Suppress `value-changed` event.
this.__clearFilter();
}
get inputElement() {
return this.$.input;
}
/** @private */
_sortSelectedItems(selectedItems) {
this.selectedItems = selectedItems.sort((item1, item2) => {
const item1Str = String(this._getItemLabel(item1, this.itemLabelPath));
const item2Str = String(this._getItemLabel(item2, this.itemLabelPath));
return item1Str.localeCompare(item2Str);
});
/**
* Focusable element used by vaadin-control-state-mixin
*/
get focusElement() {
return this.inputElement;
}
this.__updateChips();
}
_labelChanged(label) {
this.set('hasLabel', label !== '' && label != null);
}
/** @private */
__updateSelection(selectedItems) {
this.selectedItems = selectedItems;
_sortSelectedItems(selectedItems) {
selectedItems.sort((item1, item2) => {
const item1Str = String(this._getItemLabel(item1, this.itemLabelPath));
const item2Str = String(this._getItemLabel(item2, this.itemLabelPath));
return item1Str.localeCompare(item2Str);
});
}
this.validate();
_pageSizeObserver(pageSize, oldPageSize) {
if (Math.floor(pageSize) !== pageSize || pageSize <= 0) {
this.pageSize = oldPageSize;
throw new Error('`pageSize` value must be an integer > 0');
}
this.$.comboBox.pageSize = pageSize;
}
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
}
_customOverlaySelectedItemChanged(event) {
event.stopPropagation();
/** @private */
__updateChips() {
this.$.repeat.render();
}
if (event.detail.item instanceof ComboBoxPlaceholder) {
return;
}
/**
* Override method inherited from `ClearButtonMixin` and clear items.
* @protected
* @override
*/
_onClearButtonClick(event) {
event.stopPropagation();
if (this.$.comboBox.opened) {
this._comboBoxValueChanged(event, event.detail.item);
this.__updateSelection([]);
}
// When custom values are allowed, we need to clear the input,
// so we don't fire a custom values event
if (this.allowCustomValues) {
this.$.input.value = null;
}
}
/**
* Override an event listener from `KeyboardMixin`.
* @param {KeyboardEvent} event
* @protected
* @override
*/
_onKeyDown(event) {
const items = this.items || [];
if (!this.compactMode && event.key === 'Backspace' && items.length && this.inputElement.value === '') {
this.__removeItem(items[items.length - 1]);
}
}
_setTemplateFromNodes(nodes) {
this._itemTemplate = nodes.filter(node => node.localName && node.localName === 'template')[0] || this._itemTemplate;
/** @private */
_onComboBoxChange() {
const item = this.$.comboBox.selectedItem;
if (item) {
this.__selectItem(item);
}
}
_notifyReady() {
this.$server && this.$server.notifyReady();
}
/** @private */
_onComboBoxItemSelected(event) {
this.__selectItem(event.detail.item);
}
/** @private */
_onCustomValueSet(event) {
// Do not set combo-box value
event.preventDefault();
_notifyResizeIfNeeded() {
if (this.__previousHeight !== undefined && this.__previousHeight !== this.offsetHeight) {
this.notifyResize(); // allows the items drop-down to reposition itself if needed
this.dispatchEvent(new CustomEvent('iron-resize', {bubbles: true})); // allows i.e. vaadin-grid to resize itself
}
this.__previousHeight = this.offsetHeight;
}
this.__clearFilter();
this.dispatchEvent(
new CustomEvent('custom-values-set', {
detail: event.detail,
composed: true,
bubbles: true
})
);
}
customElements.define(MultiselectComboBox.is, MultiselectComboBox);
/** @private */
_onItemRemoved(event) {
this.__removeItem(event.detail.item);
}
/** @private */
_preventBlur(event) {
// Prevent mousedown event to keep the input focused
// and keep the overlay opened when clicking a chip.
event.preventDefault();
}
}
customElements.define(MultiselectComboBox.is, MultiselectComboBox);
export { MultiselectComboBox };

@@ -0,127 +1,40 @@

import '@vaadin/vaadin-lumo-styles/color.js';
import '@vaadin/vaadin-lumo-styles/font-icons.js';
import '@vaadin/vaadin-lumo-styles/mixins/required-field.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import '@vaadin/vaadin-lumo-styles/style.js';
import '@vaadin/vaadin-lumo-styles/typography.js';
import { inputFieldShared } from '@vaadin/vaadin-lumo-styles/mixins/input-field-shared.js';
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
const $_documentContainer = // eslint-disable-line camelcase
html`
<dom-module id="lumo-multiselect-combo-box" theme-for="multiselect-combo-box">
<template>
<style include="lumo-required-field">
const multiselectComboBox = css`
:host([has-value]:not([compact-mode]):not([readonly])) {
padding-inline-start: 0;
}
:host {
outline: none;
--lumo-text-field-size: var(--lumo-size-m);
color: var(--lumo-body-text-color);
font-size: var(--lumo-font-size-m);
font-family: var(--lumo-font-family);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: transparent;
padding: var(--lumo-space-xs) 0;
}
[part='chip']:not(:last-of-type) {
margin-inline-end: var(--lumo-space-xs);
}
:host::before {
content: "\\2003";
width: 0;
box-sizing: border-box;
display: inline-flex;
align-items: center;
}
[part='compact-mode-prefix'] {
box-sizing: border-box;
min-width: 70px;
padding: 0 0.25em;
color: var(--lumo-body-text-color);
font-family: var(--lumo-font-family);
font-weight: 500;
cursor: var(--lumo-clickable-cursor);
}
:host([focused]:not([readonly]):not([disabled])) [part="label"] {
color: var(--lumo-primary-text-color);
}
:host([disabled]) [part='compact-mode-prefix'] {
color: var(--lumo-disabled-text-color);
-webkit-text-fill-color: var(--lumo-disabled-text-color);
pointer-events: none;
}
:host(:hover:not([readonly]):not([focused]):not([disabled])) [part="label"] {
color: var(--lumo-body-text-color);
}
:host([disabled]) [part="label"] {
color: var(--lumo-disabled-text-color);
-webkit-text-fill-color: var(--lumo-disabled-text-color);
}
[part="input-field"] {
background-color: var(--lumo-contrast-10pct);
}
:host(:hover:not([readonly]):not([focused]):not([disabled])) [part="input-field"]::after {
opacity: 0.1;
}
[part="input-field"]::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: inherit;
pointer-events: none;
background-color: var(--lumo-contrast-50pct);
opacity: 0;
transition: transform 0.15s, opacity 0.2s;
transform-origin: 100% 0;
}
:host([invalid]) [part="input-field"] {
background-color: var(--lumo-error-color-10pct);
}
/* Trigger when not focusing using the keyboard */
:host([focused]:not([focus-ring]):not([readonly])) [part="input-field"]::after {
transform: scaleX(0);
transition-duration: 0.2s, 1s;
}
[part="readonly-container"]:not([hidden]) {
display: inline-flex;
align-items: center;
color: var(--lumo-secondary-text-color);
border: 1px dashed var(--lumo-contrast-30pct);
border-radius: var(--lumo-border-radius);
padding: 0 calc(0.375em + var(--lumo-border-radius) / 4 - 1px);
font-weight: 500;
min-height: var(--lumo-text-field-size);
cursor: default;
}
</style>
</template>
</dom-module>
<dom-module id="lumo-combo-box-item-theme" theme-for="vaadin-combo-box-item">
<template>
<style>
[part="content"] {
font-size: var(--lumo-font-size-s);
}
</style>
</template>
<dom-module>
<dom-module id="lumo-input-field-theme" theme-for="vaadin-text-field">
<template>
<style>
:host(.multiselect) [part="input-field"],
:host(.multiselect) [part="input-field"]::after {
background-color: transparent !important;
box-shadow: none;
}
:host(.multiselect[compact-mode]) [part="input-field"] {
cursor: default;
}
:host(.multiselect[compact-mode]) [part="input-field"]::after {
border: none;
}
:host(.multiselect[compact-mode]) [part="input-field"] [part="value"] {
visibility: hidden;
}
</style>
</template>
</dom-module>
[part='toggle-button']::before {
content: var(--lumo-icons-dropdown);
}
`;
document.head.appendChild($_documentContainer.content);
registerStyles('multiselect-combo-box', [inputFieldShared, multiselectComboBox], {
moduleId: 'lumo-multiselect-combo-box'
});

@@ -1,5 +0,6 @@

import '@vaadin/vaadin-text-field/theme/lumo/vaadin-text-field.js';
import '@vaadin/vaadin-combo-box/theme/lumo/vaadin-combo-box-light.js';
import '@vaadin/combo-box/theme/lumo/vaadin-combo-box-item-styles.js';
import '@vaadin/combo-box/theme/lumo/vaadin-combo-box-dropdown-styles.js';
import '@vaadin/input-container/theme/lumo/vaadin-input-container.js';
import './multiselect-combo-box-chip-styles.js';
import './multiselect-combo-box-styles.js';
import './multiselect-combo-box-input.js';
import '../../src/multiselect-combo-box.js';

@@ -0,158 +1,41 @@

import '@vaadin/vaadin-material-styles/color.js';
import '@vaadin/vaadin-material-styles/font-icons.js';
import '@vaadin/vaadin-material-styles/mixins/required-field.js';
import '@vaadin/vaadin-material-styles/color.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
import '@vaadin/vaadin-material-styles/typography.js';
import { inputFieldShared } from '@vaadin/vaadin-material-styles/mixins/input-field-shared.js';
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
const $_documentContainer = // eslint-disable-line camelcase
html`
<dom-module id="material-multiselect-combo-box" theme-for="multiselect-combo-box">
<template>
<style include="material-required-field">
:host {
outline: none;
position: relative;
}
const multiSelectComboBox = css`
[part='input-field'] {
height: auto;
min-height: 32px;
}
:host([has-label]) {
padding-top: 24px;
}
[part='input-field'] ::slotted(input) {
padding: 6px 0;
}
:host([focused]:not([readonly]):not([invalid]):not([disabled])) [part="label"] {
color: var(--material-primary-text-color);
}
[part='compact-mode-prefix'] {
color: var(--material-body-text-color);
font-family: var(--material-font-family);
font-size: var(--material-body-font-size);
cursor: default;
}
:host(:hover:not([readonly]):not([focused]):not([invalid]):not([disabled])) [part="input-field"]::after {
opacity: 0.1;
}
:host([disabled]) [part='compact-mode-prefix'] {
color: var(--material-disabled-text-color);
-webkit-text-fill-color: var(--material-disabled-text-color);
pointer-events: none;
}
:host(:hover:not([readonly]):not([invalid]):not([disabled])) [part="input-field"]::before {
opacity: var(--_material-text-field-input-line-hover-opacity, 0.87);
}
[part='toggle-button']::before {
content: var(--material-icons-dropdown);
}
:host([focused]) [part="input-field"]::after,
:host([invalid]) [part="input-field"]::after {
opacity: 1;
transform: none;
transition: transform 0.175s, opacity 0.175s;
}
:host([invalid]) [part="input-field"]::after {
background-color: var(--material-error-color);
}
:host([has-label]:not([has-value]):not([focused]):not([invalid]):not([theme="always-float-label"]):not([compact-mode]):not([disabled])) [part="label"] {
transform: scale(1) translateY(24px);
transition-timing-function: ease, ease, step-start;
pointer-events: none;
left: auto;
transition-delay: 0.1s;
}
[part="label"] {
transition: transform 0.175s, color 0.175s, width 0.175s;
transition-timing-function: ease, ease, step-end;
}
[part="label"]:empty::before {
content: " ";
position: absolute;
}
[part="readonly-container"]:not([hidden]) {
display: inline-flex;
align-items: center;
color: var(--material-body-text-color);
border: 1px dashed var(--material-text-field-input-line-background-color, #000);
padding: 0 var(--material-space-s);
min-height: 32px;
cursor: default;
}
</style>
</template>
</dom-module>
<dom-module id="material-combo-box-item-theme" theme-for="vaadin-combo-box-item">
<template>
<style>
[part="content"] {
font-size: var(--material-small-font-size);
}
</style>
</template>
<dom-module>
<dom-module id="material-input-field-theme" theme-for="vaadin-text-field">
<template>
<style>
:host(.multiselect) [part="input-field"],
:host(.multiselect) [part="input-field"]::after {
background-color: transparent;
font-size: var(--material-small-font-size);
}
:host(.multiselect) [part="input-field"]::before {
display: none;
}
:host(.multiselect[compact-mode]) [part="input-field"] {
cursor: default;
}
:host(.multiselect[compact-mode]) [part="input-field"] [part="value"] {
visibility: hidden;
}
/* placeholder styles */
:host(.multiselect) [part="input-field"] [part="value"]::-webkit-input-placeholder {
color: var(--material-disabled-text-color);
transition: opacity 0.175s 0.05s;
opacity: 1;
}
:host(.multiselect) [part="input-field"] [part="value"]::-moz-placeholder {
color: var(--material-disabled-text-color);
transition: opacity 0.175s 0.05s;
opacity: 1;
}
:host(.multiselect) [part="input-field"] [part="value"]:-ms-input-placeholder {
color: var(--material-disabled-text-color);
transition: opacity 0.175s 0.05s;
opacity: 1;
}
:host(.multiselect) [part="input-field"] [part="value"]::placeholder {
color: var(--material-disabled-text-color);
transition: opacity 0.175s 0.05s;
opacity: 1;
}
:host(.multiselect[multiselect-has-label]:not([focused]):not([invalid]):not([theme="always-float-label"])) [part="input-field"] [part="value"]::-webkit-input-placeholder,
:host(.multiselect[multiselect-has-value]) [part="input-field"] [part="value"]::-webkit-input-placeholder {
opacity: 0;
transition-delay: 0;
}
:host(.multiselect[multiselect-has-label]:not([focused]):not([invalid]):not([theme="always-float-label"])) [part="input-field"] [part="value"]::-moz-placeholder,
:host(.multiselect[multiselect-has-value]) [part="input-field"] [part="value"]::-moz-placeholder {
opacity: 0;
transition-delay: 0;
}
:host(.multiselect[multiselect-has-label]:not([focused]):not([invalid]):not([theme="always-float-label"])) [part="input-field"] [part="value"]:-ms-input-placeholder,
:host(.multiselect[multiselect-has-value]) [part="input-field"] [part="value"]:-ms-input-placeholder {
opacity: 0;
transition-delay: 0;
}
:host(.multiselect[multiselect-has-label]:not([focused]):not([invalid]):not([theme="always-float-label"])) [part="input-field"] [part="value"]::placeholder,
:host(.multiselect[multiselect-has-value]) [part="input-field"] [part="value"]::placeholder {
opacity: 0;
transition-delay: 0;
}
</style>
</template>
</dom-module>
:host([opened]) [part='toggle-button'] {
transform: rotate(180deg);
}
`;
document.head.appendChild($_documentContainer.content);
registerStyles('multiselect-combo-box', [inputFieldShared, multiSelectComboBox], {
moduleId: 'multiselect-combo-box'
});

@@ -1,5 +0,6 @@

import '@vaadin/vaadin-text-field/theme/material/vaadin-text-field.js';
import '@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light.js';
import '@vaadin/combo-box/theme/material/vaadin-combo-box-item-styles.js';
import '@vaadin/combo-box/theme/material/vaadin-combo-box-dropdown-styles.js';
import '@vaadin/input-container/theme/material/vaadin-input-container.js';
import './multiselect-combo-box-chip-styles.js';
import './multiselect-combo-box-styles.js';
import './multiselect-combo-box-input.js';
import '../../src/multiselect-combo-box.js';
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc