diff --git a/README.md b/README.md
index 1ab27b7..af232f6 100644
--- a/README.md
+++ b/README.md
@@ -1 +1 @@
-# tp-element
+# tp-dropdown
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..141eeb1
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,123 @@
+{
+ "name": "@tp/tp-dropdown",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@lit/reactive-element": {
+ "version": "1.3.0",
+ "resolved": "https://verdaccio.codeblob.work/@lit%2freactive-element/-/reactive-element-1.3.0.tgz",
+ "integrity": "sha512-0TKSIuJHXNLM0k98fi0AdMIdUoHIYlDHTP+0Vruc2SOs4T6vU1FinXgSvYd8mSrkt+8R+qdRAXvjpqrMXMyBgw=="
+ },
+ "@tp/helpers": {
+ "version": "1.0.1",
+ "resolved": "https://verdaccio.codeblob.work/@tp%2fhelpers/-/helpers-1.0.1.tgz",
+ "integrity": "sha512-f6pDPw4QpjWnmVkYgOHjMXQXtGB4vbA45eZV9DjCF9OoCXsa+Pz32H2rLQRKbdpsfFllywOBI+GMGPYDJyrG/Q=="
+ },
+ "@tp/tp-icon": {
+ "version": "1.0.0",
+ "resolved": "https://verdaccio.codeblob.work/@tp%2ftp-icon/-/tp-icon-1.0.0.tgz",
+ "integrity": "sha512-/ETh6OPsDmU38niE68ngpZEMc/yaGhbvpuvZW67a1noQHiHOjW+kznhiqNYgV/x4AIxuslgvYquiGfWq+00a4Q==",
+ "requires": {
+ "@tp/tp-tooltip": "^1.0.0",
+ "lit": "^2.2.0"
+ },
+ "dependencies": {
+ "lit": {
+ "version": "2.2.0",
+ "resolved": "https://verdaccio.codeblob.work/lit/-/lit-2.2.0.tgz",
+ "integrity": "sha512-FDyxUuczo6cJJY/2Bkgfh1872U4ikUvmK1Cb6+lYC1CW+QOo8CaWXCpvPKFzYsz0ojUxoruBLVrECc7VI2f1dQ==",
+ "requires": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-element": "^3.2.0",
+ "lit-html": "^2.2.0"
+ }
+ }
+ }
+ },
+ "@tp/tp-input": {
+ "version": "1.0.3",
+ "resolved": "https://verdaccio.codeblob.work/@tp%2ftp-input/-/tp-input-1.0.3.tgz",
+ "integrity": "sha512-PeygdnQt56pk89FJ6wU2iaKzxkNObrvDGHl/2WXPiQ67AX1kNFxBkJLgaGnTfTXqUYWxuGiY6fE8RilgkWZoyA==",
+ "requires": {
+ "@tp/helpers": "^1.0.0",
+ "lit": "^2.2.0"
+ },
+ "dependencies": {
+ "lit": {
+ "version": "2.2.0",
+ "resolved": "https://verdaccio.codeblob.work/lit/-/lit-2.2.0.tgz",
+ "integrity": "sha512-FDyxUuczo6cJJY/2Bkgfh1872U4ikUvmK1Cb6+lYC1CW+QOo8CaWXCpvPKFzYsz0ojUxoruBLVrECc7VI2f1dQ==",
+ "requires": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-element": "^3.2.0",
+ "lit-html": "^2.2.0"
+ }
+ }
+ }
+ },
+ "@tp/tp-media-query": {
+ "version": "1.0.0",
+ "resolved": "https://verdaccio.codeblob.work/@tp%2ftp-media-query/-/tp-media-query-1.0.0.tgz",
+ "integrity": "sha512-hxwkqgLDGXFmjQEMWxExJW/If1ppFerK9S5+I/P7qCKm3PMRDgafFvAPTpFLtqH3GYQ9z+uAAi2ZSNKqJ9Z7QQ==",
+ "requires": {
+ "lit": "^2.2.0"
+ },
+ "dependencies": {
+ "lit": {
+ "version": "2.2.0",
+ "resolved": "https://verdaccio.codeblob.work/lit/-/lit-2.2.0.tgz",
+ "integrity": "sha512-FDyxUuczo6cJJY/2Bkgfh1872U4ikUvmK1Cb6+lYC1CW+QOo8CaWXCpvPKFzYsz0ojUxoruBLVrECc7VI2f1dQ==",
+ "requires": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-element": "^3.2.0",
+ "lit-html": "^2.2.0"
+ }
+ }
+ }
+ },
+ "@tp/tp-tooltip": {
+ "version": "1.0.0",
+ "resolved": "https://verdaccio.codeblob.work/@tp%2ftp-tooltip/-/tp-tooltip-1.0.0.tgz",
+ "integrity": "sha512-UtrIK5KWcEiC+HnHOVbgg90j4RjHn3e9ehOBYPZsm6zO+tT7pQJJYFOtJqBW+DDV7jVfH3AvGKCxtzNiJXYvDw==",
+ "requires": {
+ "@tp/helpers": "^1.0.0",
+ "lit": "^2.2.0"
+ },
+ "dependencies": {
+ "lit": {
+ "version": "2.2.0",
+ "resolved": "https://verdaccio.codeblob.work/lit/-/lit-2.2.0.tgz",
+ "integrity": "sha512-FDyxUuczo6cJJY/2Bkgfh1872U4ikUvmK1Cb6+lYC1CW+QOo8CaWXCpvPKFzYsz0ojUxoruBLVrECc7VI2f1dQ==",
+ "requires": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-element": "^3.2.0",
+ "lit-html": "^2.2.0"
+ }
+ }
+ }
+ },
+ "@types/trusted-types": {
+ "version": "2.0.2",
+ "resolved": "https://verdaccio.codeblob.work/@types%2ftrusted-types/-/trusted-types-2.0.2.tgz",
+ "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
+ },
+ "lit-element": {
+ "version": "3.2.0",
+ "resolved": "https://verdaccio.codeblob.work/lit-element/-/lit-element-3.2.0.tgz",
+ "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==",
+ "requires": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-html": "^2.2.0"
+ }
+ },
+ "lit-html": {
+ "version": "2.2.0",
+ "resolved": "https://verdaccio.codeblob.work/lit-html/-/lit-html-2.2.0.tgz",
+ "integrity": "sha512-dJnevgV8VkCuOXLWrjQopDE8nSy8CzipZ/ATfYQv7z7Dct4abblcKecf50gkIScuwCTzKvRLgvTgV0zzagW4gA==",
+ "requires": {
+ "@types/trusted-types": "^2.0.2"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index c39fdff..99dedcf 100644
--- a/package.json
+++ b/package.json
@@ -1,18 +1,22 @@
{
- "name": "@tp/tp-element",
- "version": "0.0.1",
+ "name": "@tp/tp-dropdown",
+ "version": "1.0.0",
"description": "",
- "main": "tp-element.js",
+ "main": "tp-dropdown.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
- "url": "https://gitea.codeblob.work/tp-elements/tp-element.git"
+ "url": "https://gitea.codeblob.work/tp-elements/tp-dropdown.git"
},
"author": "trading_peter",
"license": "Apache-2.0",
"dependencies": {
+ "@tp/helpers": "^1.0.1",
+ "@tp/tp-icon": "^1.0.0",
+ "@tp/tp-input": "^1.0.3",
+ "@tp/tp-media-query": "^1.0.0",
"lit": "^2.2.0"
}
}
diff --git a/tp-dropdown.js b/tp-dropdown.js
new file mode 100644
index 0000000..719577e
--- /dev/null
+++ b/tp-dropdown.js
@@ -0,0 +1,823 @@
+/**
+@license
+Copyright (c) 2022 trading_peter
+This program is available under Apache License Version 2.0
+*/
+
+import '@tp/tp-icon/tp-icon.js';
+import '@tp/tp-input/tp-input.js';
+import '@tp/tp-media-query/tp-media-query.js';
+import { LitElement, html, svg, css } from 'lit';
+import { FormElement } from '@tp/helpers/form-element.js';
+import { Position } from '@tp/helpers/position.js';
+import { DomQuery } from '@tp/helpers/dom-query.js';
+import { EventHelpers } from '@tp/helpers/event-helpers.js';
+import { closest } from '@tp/helpers/closest.js';
+import { ControlState } from '@tp/helpers/control-state.js';
+import { isDefined } from '@tp/helpers/isDefined.js';
+
+const mixins = [
+ FormElement,
+ Position,
+ DomQuery,
+ EventHelpers,
+ ControlState
+];
+
+const BaseElement = mixins.reduce((baseClass, mixin) => {
+ return mixin(baseClass);
+}, LitElement);
+
+class TpDropdown extends BaseElement {
+ static get styles() {
+ return [
+ css`
+ :host {
+ display: block;
+ position: relative;
+ cursor: pointer;
+ outline: none;
+
+ --shadow-2dp: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 5px 0 rgba(0, 0, 0, 0.12),
+ 0 3px 1px -2px rgba(0, 0, 0, 0.2);
+ }
+
+ [hidden] {
+ display: none;
+ }
+
+ .selector {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ border: solid 1px #9E9E9E;
+ border-radius: 2px;
+ }
+
+ :host([invalid]) .selector {
+ border: solid 1px #B71C1C;
+ }
+
+ :host([focused]) .selector {
+ border: solid 1px #039BE5;
+ }
+
+ :host([no-overflow]) #selItem {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .selector #selItem {
+ flex: 1;
+ padding-left: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ #filter {
+ background: #f7f7f7;
+ padding: 7px 10px;
+ border-bottom: solid 1px #ececec;
+ }
+
+ #filter tp-icon {
+ --tp-icon-width: 16px;
+ --tp-icon-height: 16px;
+ opacity: 0.5;
+ transition: opacity 0.3s;
+ }
+
+ #filter tp-icon:hover {
+ opacity: 1;
+ }
+
+ .toggle-icon-wrap {
+ /* Prevents the toggle icon from overflowing out of the control while it's rotated. */
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ #toggleIcon {
+ transition: opacity 0.3s, transform 0.2s;
+ transform-origin: center center;
+ transform: rotate(0deg);
+ opacity: 0.5;
+ }
+
+ #toggleIcon[open] {
+ transform: rotate(180deg) !important;
+ opacity: 1;
+ }
+
+ :host(:hover) #toggleIcon {
+ opacity: 1;
+ }
+
+ #list {
+ pointer-events: none;
+ transition: opacity 0ms;
+ opacity: 0;
+ position: fixed;
+ z-index: 1;
+ height: auto;
+ }
+
+ #list[open] {
+ pointer-events: all;
+ transition: opacity 180ms;
+ opacity: 1;
+ border-radius: 2px;
+ background: #FAFAFA;
+ height: auto;
+ box-shadow: var(--shadow-2dp);
+ }
+
+ #itemList {
+ display: block;
+ overflow-y: auto;
+ }
+
+ div[role="option"] {
+ padding: 5px 10px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ div[role="option"]:hover {
+ background: #E0F7FA;
+ }
+
+ div[role="option"]:focus {
+ background: #EEEEEE;
+ outline: none;
+ }
+
+ div[role="option"][selected] {
+ background: #4FC3F7;
+ color: #FFFFFF;
+ }
+
+ div[role="option"]:first-of-type {
+ margin-top: 10px;
+ }
+
+ div[role="option"]:last-of-type {
+ margin-bottom: 10px;
+ }
+
+ .add-item-label {
+ padding: 10px;
+ background: #FFFFFF;
+ color: #616161;
+ }
+
+ .add-item-label:hover {
+ background: #039BE5;
+ }
+
+ .error-message {
+ position: absolute;
+ bottom: 2px;
+ left: 5px;
+ right: 0;
+ font-size: 10px;
+ color: #B71C1C;
+ transition: opacity 0.3s;
+ opacity: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ pointer-events: none;
+ }
+
+ :host([invalid]) .error-message {
+ opacity: 1;
+ }
+
+ @media all and (min-width: 0) and (max-width: 480px) {
+
+ :host(:not([not-responsive])) #list {
+ top: 40px !important;
+ bottom: 40px !important;
+ left: 40px !important;
+ right: 40px !important;
+ max-width: none !important;
+ min-width: auto !important;
+ padding-bottom: 10px;
+ display: flex;
+ flex-direction: column;
+ }
+
+ :host(:not([not-responsive])) #itemList {
+ max-height: none !important;
+ display: flex;
+ }
+
+ :host(:not([not-responsive])) #filter {
+ font-size: 16px;
+ padding: 12px 10px;
+ }
+
+ :host(:not([not-responsive])) div[role="option"] {
+ padding: 10px 10px;
+ }
+
+ :host(:not([not-responsive])) div[role="option"]:last-of-type {
+ margin-bottom: 0;
+ }
+ }
+ `
+ ];
+ }
+
+ render() {
+ const { label, isOpen, errorMessage, filterPlaceholder, extensible, filterable, items} = this;
+
+ return html`
+