Compare commits

...

25 Commits

Author SHA1 Message Date
jkunz 05e74cbe2e v3.78.2
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 23:34:56 +00:00
jkunz 8fbdbf9f64 fix(deps): bump @design.estate/dees-wcctools to ^3.9.0 2026-04-12 23:34:56 +00:00
jkunz bfbc0f108e v3.78.1
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 23:33:52 +00:00
jkunz bab7528f0b fix(dees-simple-login): use dees-tile for the login credentials container 2026-04-12 23:33:52 +00:00
jkunz 6047705e7d v3.78.0
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 20:33:19 +00:00
jkunz ab19b561c4 feat(dees-settings): add dees-settings layout component for displaying read-only settings with footer actions 2026-04-12 20:33:19 +00:00
jkunz 7ef3613e91 v3.77.0
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 20:18:05 +00:00
jkunz 940eebe29f feat(dees-table): add configurable cell flash comparison and border highlight mode 2026-04-12 20:18:05 +00:00
jkunz 8ecaffe165 refactor(demos): remove redundant input group styles from demo components 2026-04-12 19:58:01 +00:00
jkunz e5cb31ffb1 v3.76.1
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 19:04:43 +00:00
jkunz 7c2635fc13 fix(demo-inputs): wrap input component demos in dees-form containers for consistent form integration 2026-04-12 19:04:43 +00:00
jkunz eb3d396c68 v3.76.0
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 18:50:54 +00:00
jkunz 13ba5670f0 feat(input): separate label info tooltips from description text across input components 2026-04-12 18:50:54 +00:00
jkunz 961b811b7a v3.75.0
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 17:45:33 +00:00
jkunz cd491e1517 feat(dees-tile): add configurable overscroll handling to tile content and use it in modals 2026-04-12 17:45:33 +00:00
jkunz b8a03def79 v3.74.2
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 17:28:34 +00:00
jkunz 2b6798083d fix(modal,tile,input-text): move scroll handling from tile content to modal and update input text demo to use changeSubject subscriptions 2026-04-12 17:28:34 +00:00
jkunz 3c7b5dc690 v3.74.1
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 17:01:05 +00:00
jkunz 2f4afddf73 fix(dees-input-text): adjust password toggle and validation icon alignment in text input 2026-04-12 17:01:05 +00:00
jkunz 212a46894e v3.74.0
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 16:58:39 +00:00
jkunz 653ef109be feat(input-text): add validated success state and text editing context menu to text inputs 2026-04-12 16:58:39 +00:00
jkunz a0b17132ad fix(dees-input-text, dees-input-iban): enhance validation handling and improve demo for input states 2026-04-12 11:44:59 +00:00
jkunz 486ec11ce6 fix(dees-input-text): rename validation state class from 'error' to 'invalid' for consistency 2026-04-12 11:15:18 +00:00
jkunz a24d28d4e0 v3.73.2
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 1s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-12 11:10:21 +00:00
jkunz 8cc45a53e9 fix(input,label): correct validation state attribute handling in text inputs and refine label description icon styling 2026-04-12 11:10:21 +00:00
52 changed files with 1235 additions and 717 deletions
-1
View File
@@ -1 +0,0 @@
{"sessionId":"4b0f0a7f-f187-40a3-a38b-cb9a7e877011","pid":3110692,"acquiredAt":1775914414249}
+74
View File
@@ -1,5 +1,79 @@
# Changelog # Changelog
## 2026-04-12 - 3.78.2 - fix(deps)
bump @design.estate/dees-wcctools to ^3.9.0
- Updates the @design.estate/dees-wcctools dependency from ^3.8.4 to ^3.9.0 in package.json.
## 2026-04-12 - 3.78.1 - fix(dees-simple-login)
use dees-tile for the login credentials container
- replace the custom login card wrapper with a dees-tile component
- update styles to target dees-tile and its content part while preserving form layout
- add a credentials heading to the login tile
## 2026-04-12 - 3.78.0 - feat(dees-settings)
add dees-settings layout component for displaying read-only settings with footer actions
- introduces a new dees-settings element with heading, description, settings field grid, and footer action support
- exports dees-settings from the 00group-layout module index
- adds demo examples covering populated, empty, and multi-action states
## 2026-04-12 - 3.77.0 - feat(dees-table)
add configurable cell flash comparison and border highlight mode
- adds column-level flashCompare support so update highlighting can detect meaningful changes for custom cell values
- adds flashBorder styling for cells with badges, icons, or custom rendered content where text-color flashing is not visible
- avoids false-positive flash animations for non-primitive cell values unless a custom comparator is provided
## 2026-04-12 - 3.76.1 - fix(demo-inputs)
wrap input component demos in dees-form containers for consistent form integration
- Adds dees-form wrappers across multiple input demo pages including checkbox, dropdown, file upload, iban, list, multitoggle, phone, quantity selector, radio group, tags, text, toggle, and typelist demos
- Keeps horizontal and grid example layouts intact while nesting them inside form containers
- Cleans up file upload and text demo markup to better match expected dees-form structure
## 2026-04-12 - 3.76.0 - feat(input)
separate label info tooltips from description text across input components
- adds a dedicated infoText property for dees-label tooltips while keeping description available for helper text rendered below inputs
- introduces a shared renderDescription() helper in the input base component and updates multiple input components to use the unified description styling
- updates demos and consuming components to migrate tooltip content from description to infoText where appropriate
## 2026-04-12 - 3.75.0 - feat(dees-tile)
add configurable overscroll handling to tile content and use it in modals
- introduces a reflected overscroll property on dees-tile with contain, auto, and none options
- moves tile content scrolling and scrollbar styling into dees-tile instead of modal-specific styling
- updates dees-modal to enable contained overscroll through the new dees-tile API
## 2026-04-12 - 3.74.2 - fix(modal,tile,input-text)
move scroll handling from tile content to modal and update input text demo to use changeSubject subscriptions
- bump @design.estate/dees-wcctools from ^3.8.2 to ^3.8.4
- set dees-tile content overflow to hidden and apply scroll styling through dees-modal part selectors
- simplify the interactive dees-input-text demo by subscribing directly to changeSubject for live value updates
## 2026-04-12 - 3.74.1 - fix(dees-input-text)
adjust password toggle and validation icon alignment in text input
- positions the password toggle and validation icon with fixed top offsets for improved vertical alignment
- updates the validation icon styling to use a larger themed icon without the circular background
## 2026-04-12 - 3.74.0 - feat(input-text)
add validated success state and text editing context menu to text inputs
- show a delayed checkmark confirmation for successful validation and hide inline validation text afterward
- move IBAN validation handling into the shared text input validation function
- improve the email validation demo to use a stricter regex-based check
- add cut, copy, paste, and select-all context menu actions for text inputs
## 2026-04-12 - 3.73.2 - fix(input,label)
correct validation state attribute handling in text inputs and refine label description icon styling
- Change dees-input-text validationState to reflect as a string attribute and align validation selectors with the emitted host attribute
- Wrap the dees-label description icon in a dedicated container to improve sizing, hover feedback, and alignment
## 2026-04-12 - 3.73.1 - fix(dees-label) ## 2026-04-12 - 3.73.1 - fix(dees-label)
align label content and icon consistently using inline flex layout align label content and icon consistently using inline flex layout
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@design.estate/dees-catalog", "name": "@design.estate/dees-catalog",
"version": "3.73.1", "version": "3.78.2",
"private": false, "private": false,
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.", "description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
"main": "dist_ts_web/index.js", "main": "dist_ts_web/index.js",
@@ -18,7 +18,7 @@
"dependencies": { "dependencies": {
"@design.estate/dees-domtools": "^2.5.4", "@design.estate/dees-domtools": "^2.5.4",
"@design.estate/dees-element": "^2.2.4", "@design.estate/dees-element": "^2.2.4",
"@design.estate/dees-wcctools": "^3.8.2", "@design.estate/dees-wcctools": "^3.9.0",
"@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-brands-svg-icons": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0",
"@fortawesome/free-regular-svg-icons": "^7.2.0", "@fortawesome/free-regular-svg-icons": "^7.2.0",
+27 -6
View File
@@ -15,8 +15,8 @@ importers:
specifier: ^2.2.4 specifier: ^2.2.4
version: 2.2.4 version: 2.2.4
'@design.estate/dees-wcctools': '@design.estate/dees-wcctools':
specifier: ^3.8.2 specifier: ^3.9.0
version: 3.8.2 version: 3.9.0
'@fortawesome/fontawesome-svg-core': '@fortawesome/fontawesome-svg-core':
specifier: ^7.2.0 specifier: ^7.2.0
version: 7.2.0 version: 7.2.0
@@ -323,8 +323,8 @@ packages:
'@design.estate/dees-element@2.2.4': '@design.estate/dees-element@2.2.4':
resolution: {integrity: sha512-O9cA6flBMMd+pBwMQrZXwAWel9yVxgokolb+Em6gvkXxPJ0P/B5UDn4Vc2d4ts3ta55PTBm+l2dPeDVGx/bl7Q==} resolution: {integrity: sha512-O9cA6flBMMd+pBwMQrZXwAWel9yVxgokolb+Em6gvkXxPJ0P/B5UDn4Vc2d4ts3ta55PTBm+l2dPeDVGx/bl7Q==}
'@design.estate/dees-wcctools@3.8.2': '@design.estate/dees-wcctools@3.9.0':
resolution: {integrity: sha512-A55XHeWExxxojdERAmedrZeyTGeK01ax5ct46VbjMeH65HbgBiTF4EOHfS6rjdTp+9VD3vXd0efhzyOxOS6uFw==} resolution: {integrity: sha512-0vZBaGBEGIbl8hx+8BezIIea3U5T7iSHHF9VqlJZGf+nOFIW4zBAxcCljH8YzZ1Yayp6BEjxp/pQXjHN2YB3Jg==}
'@emnapi/core@1.8.1': '@emnapi/core@1.8.1':
resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
@@ -2070,6 +2070,12 @@ packages:
'@types/debug@4.1.12': '@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
'@types/dom-mediacapture-transform@0.1.11':
resolution: {integrity: sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==}
'@types/dom-webcodecs@0.1.13':
resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==}
'@types/fs-extra@11.0.4': '@types/fs-extra@11.0.4':
resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
@@ -3136,6 +3142,9 @@ packages:
mdurl@2.0.0: mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
mediabunny@1.40.1:
resolution: {integrity: sha512-HU/stGzAkdWaJIly6ypbUVgAUvT9kt39DIg0IaErR7/1fwtTmgUYs4i8uEPYcgcjPjbB9gtBmUXOLnXi6J2LDw==}
memory-pager@1.5.0: memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
@@ -4711,7 +4720,7 @@ snapshots:
dependencies: dependencies:
'@design.estate/dees-domtools': 2.5.4 '@design.estate/dees-domtools': 2.5.4
'@design.estate/dees-element': 2.2.4 '@design.estate/dees-element': 2.2.4
'@design.estate/dees-wcctools': 3.8.2 '@design.estate/dees-wcctools': 3.9.0
'@fortawesome/fontawesome-svg-core': 7.2.0 '@fortawesome/fontawesome-svg-core': 7.2.0
'@fortawesome/free-brands-svg-icons': 7.2.0 '@fortawesome/free-brands-svg-icons': 7.2.0
'@fortawesome/free-regular-svg-icons': 7.2.0 '@fortawesome/free-regular-svg-icons': 7.2.0
@@ -4787,12 +4796,13 @@ snapshots:
- supports-color - supports-color
- vue - vue
'@design.estate/dees-wcctools@3.8.2': '@design.estate/dees-wcctools@3.9.0':
dependencies: dependencies:
'@design.estate/dees-domtools': 2.5.4 '@design.estate/dees-domtools': 2.5.4
'@design.estate/dees-element': 2.2.4 '@design.estate/dees-element': 2.2.4
'@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartdelay': 3.0.5
lit: 3.3.2 lit: 3.3.2
mediabunny: 1.40.1
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
- react - react
@@ -7245,6 +7255,12 @@ snapshots:
dependencies: dependencies:
'@types/ms': 2.1.0 '@types/ms': 2.1.0
'@types/dom-mediacapture-transform@0.1.11':
dependencies:
'@types/dom-webcodecs': 0.1.13
'@types/dom-webcodecs@0.1.13': {}
'@types/fs-extra@11.0.4': '@types/fs-extra@11.0.4':
dependencies: dependencies:
'@types/jsonfile': 6.1.4 '@types/jsonfile': 6.1.4
@@ -8468,6 +8484,11 @@ snapshots:
mdurl@2.0.0: {} mdurl@2.0.0: {}
mediabunny@1.40.1:
dependencies:
'@types/dom-mediacapture-transform': 0.1.11
'@types/dom-webcodecs': 0.1.13
memory-pager@1.5.0: {} memory-pager@1.5.0: {}
micromark-core-commonmark@2.0.3: micromark-core-commonmark@2.0.3:
+19 -12
View File
@@ -59,8 +59,8 @@ For developers working on this library, please refer to the [UI Components Playb
| **Forms** | [`DeesForm`](#deesform), [`DeesInputText`](#deesinputtext), [`DeesInputCheckbox`](#deesinputcheckbox), [`DeesInputDropdown`](#deesinputdropdown), [`DeesInputRadiogroup`](#deesinputradiogroup), [`DeesInputFileupload`](#deesinputfileupload), [`DeesInputIban`](#deesinputiban), [`DeesInputPhone`](#deesinputphone), [`DeesInputQuantitySelector`](#deesinputquantityselector), [`DeesInputMultitoggle`](#deesinputmultitoggle), [`DeesInputToggle`](#deesinputtoggle), [`DeesInputTags`](#deesinputtags), [`DeesInputTypelist`](#deesinputtypelist), [`DeesInputList`](#deesinputlist), [`DeesInputProfilepicture`](#deesinputprofilepicture), [`DeesInputRichtext`](#deesinputrichtext), [`DeesInputWysiwyg`](#deesinputwysiwyg), [`DeesInputDatepicker`](#deesinputdatepicker), [`DeesInputSearchselect`](#deesinputsearchselect), [`DeesInputCode`](#deesinputcode), [`DeesFormSubmit`](#deesformsubmit) | | **Forms** | [`DeesForm`](#deesform), [`DeesInputText`](#deesinputtext), [`DeesInputCheckbox`](#deesinputcheckbox), [`DeesInputDropdown`](#deesinputdropdown), [`DeesInputRadiogroup`](#deesinputradiogroup), [`DeesInputFileupload`](#deesinputfileupload), [`DeesInputIban`](#deesinputiban), [`DeesInputPhone`](#deesinputphone), [`DeesInputQuantitySelector`](#deesinputquantityselector), [`DeesInputMultitoggle`](#deesinputmultitoggle), [`DeesInputToggle`](#deesinputtoggle), [`DeesInputTags`](#deesinputtags), [`DeesInputTypelist`](#deesinputtypelist), [`DeesInputList`](#deesinputlist), [`DeesInputProfilepicture`](#deesinputprofilepicture), [`DeesInputRichtext`](#deesinputrichtext), [`DeesInputWysiwyg`](#deesinputwysiwyg), [`DeesInputDatepicker`](#deesinputdatepicker), [`DeesInputSearchselect`](#deesinputsearchselect), [`DeesInputCode`](#deesinputcode), [`DeesFormSubmit`](#deesformsubmit) |
| **App Shell (Layout)** | [`DeesAppui`](#deesappui-), [`DeesAppuiMainmenu`](#deesappuimainmenu), [`DeesAppuiSecondarymenu`](#deesappuisecondarymenu), [`DeesAppuiMaincontent`](#deesappuimaincontent), [`DeesAppuiAppbar`](#deesappuiappbar), [`DeesAppuiActivitylog`](#deesappuiactivitylog), [`DeesAppuiBottombar`](#deesappuibottombar), [`DeesAppuiProfiledropdown`](#deesappuiprofiledropdown), [`DeesAppuiTabs`](#deesappuitabs), [`DeesMobileNavigation`](#deesmobilenavigation), [`DeesDashboardGrid`](#deesdashboardgrid) | | **App Shell (Layout)** | [`DeesAppui`](#deesappui-), [`DeesAppuiMainmenu`](#deesappuimainmenu), [`DeesAppuiSecondarymenu`](#deesappuisecondarymenu), [`DeesAppuiMaincontent`](#deesappuimaincontent), [`DeesAppuiAppbar`](#deesappuiappbar), [`DeesAppuiActivitylog`](#deesappuiactivitylog), [`DeesAppuiBottombar`](#deesappuibottombar), [`DeesAppuiProfiledropdown`](#deesappuiprofiledropdown), [`DeesAppuiTabs`](#deesappuitabs), [`DeesMobileNavigation`](#deesmobilenavigation), [`DeesDashboardGrid`](#deesdashboardgrid) |
| **Data Display** | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination), [`DeesStorageBrowser`](#deesstorgebrowser) | | **Data Display** | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination), [`DeesStorageBrowser`](#deesstorgebrowser) |
| **Media & Tiles** | [`DeesTilePdf`](#deestilepdf), [`DeesTileImage`](#deestileimage), [`DeesTileAudio`](#deestileaudio), [`DeesTileVideo`](#deestilevideo), [`DeesTileNote`](#deestilenote), [`DeesTileFolder`](#deestilefolder), [`DeesPreview`](#deespreview), [`DeesPdfViewer`](#deespdfviewer), [`DeesPdfPreview`](#deespdfpreview), [`DeesImageViewer`](#deesimageviewer), [`DeesAudioViewer`](#deesaudioviewer), [`DeesVideoViewer`](#deesvideoviewer) | | **Media & Thumbnails** | [`DeesThumbnailPdf`](#deesthumbnailpdf), [`DeesThumbnailImage`](#deesthumbnailimage), [`DeesThumbnailAudio`](#deesthumbnailaudio), [`DeesThumbnailVideo`](#deesthumbnalvideo), [`DeesThumbnailNote`](#deesthumbnailnote), [`DeesThumbnailFolder`](#deesthumbnailfolder), [`DeesPreview`](#deespreview), [`DeesPdfViewer`](#deespdfviewer), [`DeesImageViewer`](#deesimageviewer), [`DeesAudioViewer`](#deesaudioviewer), [`DeesVideoViewer`](#deesvideoviewer) |
| **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) | | **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartBar`](#deeschartbar), [`DeesChartDonut`](#deeschartdonut), [`DeesChartGauge`](#deeschartgauge), [`DeesChartRadar`](#deeschartradar), [`DeesChartLog`](#deeschartlog) |
| **Dialogs & Overlays** | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) | | **Dialogs & Overlays** | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) |
| **Navigation** | [`DeesStepper`](#deesstepper), [`DeesProgressbar`](#deesprogressbar) | | **Navigation** | [`DeesStepper`](#deesstepper), [`DeesProgressbar`](#deesprogressbar) |
| **Workspace / IDE** | [`DeesWorkspace`](#deesworkspace), [`DeesWorkspaceMonaco`](#deesworkspacemonaco), [`DeesWorkspaceDiffEditor`](#deesworkspacediffeditor), [`DeesWorkspaceFiletree`](#deesworkspacefiletree), [`DeesWorkspaceTerminal`](#deesworkspaceterminal), [`DeesWorkspaceTerminalPreview`](#deesworkspaceterminalpreview), [`DeesWorkspaceMarkdown`](#deesworkspacemarkdown), [`DeesWorkspaceMarkdownoutlet`](#deesworkspacemarkdownoutlet), [`DeesWorkspaceBottombar`](#deesworkspacebottombar) | | **Workspace / IDE** | [`DeesWorkspace`](#deesworkspace), [`DeesWorkspaceMonaco`](#deesworkspacemonaco), [`DeesWorkspaceDiffEditor`](#deesworkspacediffeditor), [`DeesWorkspaceFiletree`](#deesworkspacefiletree), [`DeesWorkspaceTerminal`](#deesworkspaceterminal), [`DeesWorkspaceTerminalPreview`](#deesworkspaceterminalpreview), [`DeesWorkspaceMarkdown`](#deesworkspacemarkdown), [`DeesWorkspaceMarkdownoutlet`](#deesworkspacemarkdownoutlet), [`DeesWorkspaceBottombar`](#deesworkspacebottombar) |
@@ -143,14 +143,13 @@ Display icons from FontAwesome and Lucide icon libraries with library prefixes.
``` ```
#### `DeesLabel` #### `DeesLabel`
Text label component with optional icon and status indicators. Text label component with optional required indicator and info tooltip. Used internally by all input components.
```typescript ```typescript
<dees-label <dees-label
text="Status" // Label text .label=${'Email Address'} // Label text
icon="info-circle" // Optional: icon name .required=${true} // Optional: shows red asterisk
type="info" // Options: default, info, success, warning, error .infoText=${'We will never share your email'} // Optional: shows hover info icon with tooltip
size="medium" // Options: small, medium, large
></dees-label> ></dees-label>
``` ```
@@ -321,7 +320,7 @@ Container component for form elements with built-in validation and data handling
``` ```
#### `DeesInputText` #### `DeesInputText`
Text input field with validation and formatting options. Text input field with validation, info tooltips, description text, and context menu (Cut/Copy/Paste/Select All).
```typescript ```typescript
<dees-input-text <dees-input-text
@@ -330,10 +329,20 @@ Text input field with validation and formatting options.
value="initial@value.com" // Initial value value="initial@value.com" // Initial value
required // Makes the field required required // Makes the field required
disabled // Disables the input disabled // Disables the input
placeholder="Enter your email" .infoText=${'Hover icon tooltip text'} // Shows ⓘ icon on label with hover tooltip
.description=${'Permanent help text below the input'} // Small text below the input
.validationFunction=${(value) => { // Auto-validates on every keystroke
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(value)) {
return { valid: true, message: 'Email is valid' };
}
return { valid: false, message: 'Please enter a valid email' };
}}
></dees-input-text> ></dees-input-text>
``` ```
> 💡 **All input components** share these common properties from `DeesInputBase`: `key`, `label`, `required`, `disabled`, `infoText`, `description`, `layoutMode`, `labelPosition`.
#### `DeesInputCheckbox` #### `DeesInputCheckbox`
Checkbox input component for boolean values. Checkbox input component for boolean values.
@@ -1780,7 +1789,7 @@ interface ITileFolderItem {
## License and Legal Information ## License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license) file. This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
@@ -1798,5 +1807,3 @@ Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc. For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works. By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@design.estate/dees-catalog', name: '@design.estate/dees-catalog',
version: '3.73.1', version: '3.78.2',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
} }
@@ -610,7 +610,7 @@ export class DeesTable<T> extends DeesElement {
<div class="searchGrid hidden"> <div class="searchGrid hidden">
<dees-input-text <dees-input-text
.label=${'lucene syntax search'} .label=${'lucene syntax search'}
.description=${` .infoText=${`
You can use the lucene syntax to search for data, e.g.: You can use the lucene syntax to search for data, e.g.:
\`\`\` \`\`\`
@@ -623,7 +623,7 @@ export class DeesTable<T> extends DeesElement {
.label=${'search mode'} .label=${'search mode'}
.options=${['table', 'data', 'server']} .options=${['table', 'data', 'server']}
.selectedOption=${'table'} .selectedOption=${'table'}
.description=${` .infoText=${`
There are three basic modes: There are three basic modes:
* table: only searches data already in the table * table: only searches data already in the table
@@ -695,6 +695,7 @@ export class DeesTable<T> extends DeesElement {
this.__editingCell?.rowId === rowId && this.__editingCell?.rowId === rowId &&
this.__editingCell?.colKey === editKey; this.__editingCell?.colKey === editKey;
const isFlashing = !!flashSet?.has(editKey); const isFlashing = !!flashSet?.has(editKey);
const useFlashBorder = isFlashing && !!col.flashBorder;
const cellClasses = [ const cellClasses = [
isEditable ? 'editable' : '', isEditable ? 'editable' : '',
isFocused && !isEditing ? 'focused' : '', isFocused && !isEditing ? 'focused' : '',
@@ -702,8 +703,13 @@ export class DeesTable<T> extends DeesElement {
] ]
.filter(Boolean) .filter(Boolean)
.join(' '); .join(' ');
const flashClass = isFlashing
? useFlashBorder
? 'innerCellContainer flashing-border'
: 'innerCellContainer flashing'
: 'innerCellContainer';
const innerHtml = html`<div const innerHtml = html`<div
class=${isFlashing ? 'innerCellContainer flashing' : 'innerCellContainer'} class=${flashClass}
> >
${isEditing ? this.renderCellEditor(itemArg, col) : content} ${isEditing ? this.renderCellEditor(itemArg, col) : content}
</div>`; </div>`;
@@ -1362,6 +1368,7 @@ export class DeesTable<T> extends DeesElement {
const effectiveColumns = this.__getEffectiveColumns(); const effectiveColumns = this.__getEffectiveColumns();
const visibleCols = effectiveColumns.filter((c) => !c.hidden); const visibleCols = effectiveColumns.filter((c) => !c.hidden);
const colByKey = new Map<string, Column<T>>(visibleCols.map((c) => [String(c.key), c]));
const nextSnapshot = new Map<string, Map<string, unknown>>(); const nextSnapshot = new Map<string, Map<string, unknown>>();
const newlyFlashing = new Map<string, Set<string>>(); const newlyFlashing = new Map<string, Set<string>>();
@@ -1376,7 +1383,26 @@ export class DeesTable<T> extends DeesElement {
const prevCells = this.__prevSnapshot?.get(rowId); const prevCells = this.__prevSnapshot?.get(rowId);
if (!prevCells) continue; // new row — not an "update" if (!prevCells) continue; // new row — not an "update"
for (const [colKey, nextVal] of cellMap) { for (const [colKey, nextVal] of cellMap) {
if (prevCells.get(colKey) !== nextVal) { const prevVal = prevCells.get(colKey);
// Look up the column definition for flash options.
const colDef = colByKey.get(colKey);
// Determine whether the cell changed.
let changed: boolean;
if (colDef?.flashCompare) {
// Explicit custom comparator — caller decides.
changed = colDef.flashCompare(prevVal, nextVal);
} else if (nextVal !== null && nextVal !== undefined && typeof nextVal === 'object') {
// Non-primitive (TemplateResult, object, array, etc.) — skip by
// default. Custom renderings don't benefit from the text-color
// flash and reference inequality causes false positives.
changed = false;
} else {
changed = prevVal !== nextVal;
}
if (changed) {
// Don't flash the cell the user is actively editing. // Don't flash the cell the user is actively editing.
if ( if (
this.__editingCell && this.__editingCell &&
@@ -404,11 +404,44 @@ export const tableStyles: CSSResult[] = [
100% { color: var(--dees-color-text-primary); } 100% { color: var(--dees-color-text-primary); }
} }
/* Border/background flash variant for cells with styled content
(badges, icons, custom components) where a text-color animation
would be invisible. Activated via flashBorder on Column. */
.innerCellContainer.flashing-border {
animation: dees-table-cell-flash-border
var(--dees-table-flash-duration, 900ms)
var(--dees-table-flash-easing);
border-radius: 3px;
}
@keyframes dees-table-cell-flash-border {
0%,
35% {
box-shadow: inset 0 0 0 1.5px var(--dees-table-flash-color);
background: ${cssManager.bdTheme(
'hsl(45 93% 62% / 0.10)',
'hsl(45 93% 62% / 0.08)'
)};
}
100% {
box-shadow: inset 0 0 0 1.5px transparent;
background: transparent;
}
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.innerCellContainer.flashing { .innerCellContainer.flashing {
animation: none; animation: none;
color: var(--dees-table-flash-color); color: var(--dees-table-flash-color);
} }
.innerCellContainer.flashing-border {
animation: none;
box-shadow: inset 0 0 0 1.5px var(--dees-table-flash-color);
background: ${cssManager.bdTheme(
'hsl(45 93% 62% / 0.10)',
'hsl(45 93% 62% / 0.08)'
)};
}
} }
/* Dev-time warning banner shown when highlight-updates="flash" but /* Dev-time warning banner shown when highlight-updates="flash" but
@@ -65,6 +65,25 @@ export interface Column<T = any> {
parse?: (editorValue: any, row: T) => any; parse?: (editorValue: any, row: T) => any;
/** Validate the parsed value before commit. Return string for error, true/void for ok. */ /** Validate the parsed value before commit. Return string for error, true/void for ok. */
validate?: (value: any, row: T) => true | string | void; validate?: (value: any, row: T) => true | string | void;
// ─── Flash highlight options ───
/**
* Custom comparison for flash-on-update diffing.
* Return `true` if the cell should flash (i.e. the values differ).
* When absent, non-primitive cell values are skipped entirely
* (only strings, numbers, booleans, null, and undefined are diffed).
*/
flashCompare?: (prevVal: any, nextVal: any) => boolean;
/**
* When `true`, flash this cell with a border/background pulse instead of
* the default text-color animation. Useful for cells containing styled
* badges, icons, or custom web-component renderings where a text-color
* change would be invisible.
* @default false
*/
flashBorder?: boolean;
} }
/** /**
@@ -92,7 +92,7 @@ export const demoFunc = () => html`
.required=${true} .required=${true}
key="firstName" key="firstName"
label="First Name" label="First Name"
.description=${'Your given name'} .infoText=${'Your given name'}
></dees-input-text> ></dees-input-text>
<dees-input-text <dees-input-text
@@ -105,7 +105,7 @@ export const demoFunc = () => html`
.required=${true} .required=${true}
key="email" key="email"
label="Email Address" label="Email Address"
.description=${'We will use this to contact you'} .infoText=${'We will use this to contact you'}
></dees-input-text> ></dees-input-text>
<dees-input-dropdown <dees-input-dropdown
@@ -126,7 +126,7 @@ export const demoFunc = () => html`
key="password" key="password"
label="Password" label="Password"
isPasswordBool isPasswordBool
.description=${'Minimum 8 characters'} .infoText=${'Minimum 8 characters'}
></dees-input-text> ></dees-input-text>
<dees-input-checkbox <dees-input-checkbox
@@ -300,7 +300,7 @@ export const demoFunc = () => html`
<dees-input-fileupload <dees-input-fileupload
key="documents" key="documents"
.label=${'Upload Documents'} .label=${'Upload Documents'}
.description=${'PDF, DOC, or DOCX files up to 10MB'} .infoText=${'PDF, DOC, or DOCX files up to 10MB'}
></dees-input-fileupload> ></dees-input-fileupload>
<dees-form-submit>Submit Application</dees-form-submit> <dees-form-submit>Submit Application</dees-form-submit>
@@ -1,6 +1,7 @@
import { import {
DeesElement, DeesElement,
property, property,
html,
css, css,
type CSSResult, type CSSResult,
cssManager, cssManager,
@@ -42,6 +43,9 @@ export abstract class DeesInputBase<T = any> extends DeesElement {
@property({ type: Boolean }) @property({ type: Boolean })
accessor disabled: boolean = false; accessor disabled: boolean = false;
@property({ type: String })
accessor infoText!: string;
@property({ type: String }) @property({ type: String })
accessor description!: string; accessor description!: string;
@@ -90,6 +94,14 @@ export abstract class DeesInputBase<T = any> extends DeesElement {
:host([label-position="none"]) dees-label { :host([label-position="none"]) dees-label {
display: none; display: none;
} }
/* Description text below input */
.descriptionText {
margin-top: 4px;
font-size: 12px;
line-height: 1.4;
color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')};
}
`, `,
]; ];
} }
@@ -155,6 +167,14 @@ export abstract class DeesInputBase<T = any> extends DeesElement {
this.disabled = false; this.disabled = false;
} }
/**
* Renders the description text below the input.
* Call ${this.renderDescription()} at the end of your render template.
*/
public renderDescription() {
return this.description ? html`<div class="descriptionText">${this.description}</div>` : '';
}
/** /**
* Abstract method that child classes must implement to get their value * Abstract method that child classes must implement to get their value
*/ */
@@ -111,7 +111,7 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'Basic Checkboxes'} .subtitle=${'Simple checkbox examples with various labels'}> <dees-panel .title=${'Basic Checkboxes'} .subtitle=${'Simple checkbox examples with various labels'}>
<div class="checkbox-group"> <dees-form>
<dees-input-checkbox <dees-input-checkbox
.label=${'I agree to the Terms and Conditions'} .label=${'I agree to the Terms and Conditions'}
.value=${true} .value=${true}
@@ -130,11 +130,11 @@ export const demoFunc = () => html`
.description=${'Receive email updates about your account'} .description=${'Receive email updates about your account'}
.key=${'notifications'} .key=${'notifications'}
></dees-input-checkbox> ></dees-input-checkbox>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Checkbox States'} .subtitle=${'Different checkbox states and configurations'}> <dees-panel .title=${'Checkbox States'} .subtitle=${'Different checkbox states and configurations'}>
<div class="checkbox-group"> <dees-form>
<dees-input-checkbox <dees-input-checkbox
.label=${'Default state'} .label=${'Default state'}
.value=${false} .value=${false}
@@ -162,10 +162,11 @@ export const demoFunc = () => html`
.required=${true} .required=${true}
.key=${'required'} .key=${'required'}
></dees-input-checkbox> ></dees-input-checkbox>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Horizontal Layout'} .subtitle=${'Checkboxes arranged horizontally for compact forms'}> <dees-panel .title=${'Horizontal Layout'} .subtitle=${'Checkboxes arranged horizontally for compact forms'}>
<dees-form>
<div class="horizontal-checkboxes"> <div class="horizontal-checkboxes">
<dees-input-checkbox <dees-input-checkbox
.label=${'Option A'} .label=${'Option A'}
@@ -195,6 +196,7 @@ export const demoFunc = () => html`
.key=${'optionD'} .key=${'optionD'}
></dees-input-checkbox> ></dees-input-checkbox>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Feature Selection Example'} .subtitle=${'Common use case for feature toggles with batch operations'}> <dees-panel .title=${'Feature Selection Example'} .subtitle=${'Common use case for feature toggles with batch operations'}>
@@ -204,7 +206,7 @@ export const demoFunc = () => html`
</div> </div>
<div class="feature-list"> <div class="feature-list">
<div class="checkbox-group"> <dees-form>
<dees-input-checkbox <dees-input-checkbox
.label=${'Dark Mode Support'} .label=${'Dark Mode Support'}
.value=${true} .value=${true}
@@ -234,7 +236,7 @@ export const demoFunc = () => html`
.value=${false} .value=${false}
.key=${'feature5'} .key=${'feature5'}
></dees-input-checkbox> ></dees-input-checkbox>
</div> </dees-form>
</div> </div>
</dees-panel> </dees-panel>
@@ -242,7 +244,7 @@ export const demoFunc = () => html`
<div class="form-section"> <div class="form-section">
<h4 class="section-title">Privacy Preferences</h4> <h4 class="section-title">Privacy Preferences</h4>
<div class="checkbox-group"> <dees-form>
<dees-input-checkbox <dees-input-checkbox
.label=${'Share analytics data'} .label=${'Share analytics data'}
.value=${true} .value=${true}
@@ -266,12 +268,12 @@ export const demoFunc = () => html`
.value=${false} .value=${false}
.description=${'Allow approved partners to access your data'} .description=${'Allow approved partners to access your data'}
></dees-input-checkbox> ></dees-input-checkbox>
</div> </dees-form>
</div> </div>
</dees-panel> </dees-panel>
<dees-panel .title=${'Interactive Example'} .subtitle=${'Click checkboxes to see value changes'}> <dees-panel .title=${'Interactive Example'} .subtitle=${'Click checkboxes to see value changes'}>
<div class="checkbox-group"> <dees-form>
<dees-input-checkbox <dees-input-checkbox
.label=${'Feature toggle'} .label=${'Feature toggle'}
.value=${false} .value=${false}
@@ -295,7 +297,7 @@ export const demoFunc = () => html`
} }
}} }}
></dees-input-checkbox> ></dees-input-checkbox>
</div> </dees-form>
<div class="interactive-section"> <div class="interactive-section">
<div id="checkbox-output" class="output-text">Feature is disabled</div> <div id="checkbox-output" class="output-text">Feature is disabled</div>
@@ -147,12 +147,6 @@ export class DeesInputCheckbox extends DeesInputBase<DeesInputCheckbox> {
color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')}; color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')};
} }
/* Description */
.description-text {
font-size: 12px;
color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')};
line-height: 1.5;
}
`, `,
]; ];
@@ -185,7 +179,7 @@ export class DeesInputCheckbox extends DeesInputBase<DeesInputCheckbox> {
</div> </div>
<div class="label-container"> <div class="label-container">
${this.label ? html`<div class="checkbox-label">${this.label}</div>` : ''} ${this.label ? html`<div class="checkbox-label">${this.label}</div>` : ''}
${this.description ? html`<div class="description-text">${this.description}</div>` : ''} ${this.renderDescription()}
</div> </div>
</div> </div>
</div> </div>
@@ -284,7 +284,7 @@ export class DeesInputCode extends DeesInputBase<string> {
} }
</style> </style>
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText} .required=${this.required}></dees-label>
<dees-tile> <dees-tile>
<div slot="header" class="toolbar"> <div slot="header" class="toolbar">
<div class="toolbar-left"> <div class="toolbar-left">
@@ -362,6 +362,7 @@ export class DeesInputCode extends DeesInputBase<string> {
</div> </div>
</div> </div>
</dees-tile> </dees-tile>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -4,7 +4,7 @@ import type { DeesInputDatepicker } from './component.js';
export const renderDatepicker = (component: DeesInputDatepicker): TemplateResult => { export const renderDatepicker = (component: DeesInputDatepicker): TemplateResult => {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${component.label} .description=${component.description} .required=${component.required}></dees-label> <dees-label .label=${component.label} .infoText=${component.infoText} .required=${component.required}></dees-label>
<div class="input-container"> <div class="input-container">
<input <input
type="text" type="text"
@@ -27,6 +27,7 @@ export const renderDatepicker = (component: DeesInputDatepicker): TemplateResult
<dees-icon class="calendar-icon" icon="lucide:calendar" iconSize="16"></dees-icon> <dees-icon class="calendar-icon" icon="lucide:calendar" iconSize="16"></dees-icon>
</div> </div>
</div> </div>
${component.renderDescription()}
</div> </div>
`; `;
@@ -31,12 +31,6 @@ export const demoFunc = () => html`
flex-wrap: wrap; flex-wrap: wrap;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.spacer { .spacer {
height: 200px; height: 200px;
display: flex; display: flex;
@@ -69,9 +63,10 @@ export const demoFunc = () => html`
} }
}}> }}>
<dees-panel .title=${'1. Basic Dropdowns'} .subtitle=${'Standard dropdown with search functionality and various options'}> <dees-panel .title=${'1. Basic Dropdowns'} .subtitle=${'Standard dropdown with search functionality and various options'}>
<div class="input-group"> <dees-form>
<dees-input-dropdown <dees-input-dropdown
.label=${'Select Country'} .label=${'Select Country'}
.description=${'Choose the country where your business is registered'}
.options=${[ .options=${[
{ option: 'United States', key: 'us' }, { option: 'United States', key: 'us' },
{ option: 'Canada', key: 'ca' }, { option: 'Canada', key: 'ca' },
@@ -94,7 +89,7 @@ export const demoFunc = () => html`
{ option: 'Guest', key: 'guest' } { option: 'Guest', key: 'guest' }
]} ]}
></dees-input-dropdown> ></dees-input-dropdown>
</div> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
@@ -135,6 +130,7 @@ export const demoFunc = () => html`
}); });
}}> }}>
<dees-panel .title=${'3. Horizontal Layout'} .subtitle=${'Multiple dropdowns in a horizontal layout for compact forms'}> <dees-panel .title=${'3. Horizontal Layout'} .subtitle=${'Multiple dropdowns in a horizontal layout for compact forms'}>
<dees-form>
<div class="horizontal-group"> <div class="horizontal-group">
<dees-input-dropdown <dees-input-dropdown
.label=${'Department'} .label=${'Department'}
@@ -169,6 +165,7 @@ export const demoFunc = () => html`
]} ]}
></dees-input-dropdown> ></dees-input-dropdown>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
@@ -184,7 +181,7 @@ export const demoFunc = () => html`
} }
}}> }}>
<dees-panel .title=${'4. States'} .subtitle=${'Different states and configurations'}> <dees-panel .title=${'4. States'} .subtitle=${'Different states and configurations'}>
<div class="input-group"> <dees-form>
<dees-input-dropdown <dees-input-dropdown
.label=${'Required Field'} .label=${'Required Field'}
.required=${true} .required=${true}
@@ -203,7 +200,7 @@ export const demoFunc = () => html`
]} ]}
.selectedOption=${{ option: 'Cannot Select', key: 'disabled' }} .selectedOption=${{ option: 'Cannot Select', key: 'disabled' }}
></dees-input-dropdown> ></dees-input-dropdown>
</div> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
@@ -168,7 +168,7 @@ export class DeesInputDropdown extends DeesInputBase<DeesInputDropdown> {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText} .required=${this.required}></dees-label>
<div class="maincontainer"> <div class="maincontainer">
<div <div
class="selectedBox ${this.isOpened ? 'open' : ''} ${this.disabled ? 'disabled' : ''}" class="selectedBox ${this.isOpened ? 'open' : ''} ${this.disabled ? 'disabled' : ''}"
@@ -179,6 +179,7 @@ export class DeesInputDropdown extends DeesInputBase<DeesInputDropdown> {
${this.selectedOption?.option || 'Select an option'} ${this.selectedOption?.option || 'Select an option'}
</div> </div>
</div> </div>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -73,7 +73,7 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label <dees-label
.label=${this.label} .label=${this.label}
.description=${this.description} .infoText=${this.infoText}
.required=${this.required} .required=${this.required}
></dees-label> ></dees-label>
<dees-tile <dees-tile
@@ -114,6 +114,7 @@ export class DeesInputFileupload extends DeesInputBase<DeesInputFileupload> {
${this.validationMessage ${this.validationMessage
? html`<div class="validation-message" aria-live="polite">${this.validationMessage}</div>` ? html`<div class="validation-message" aria-live="polite">${this.validationMessage}</div>`
: html``} : html``}
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -55,18 +55,20 @@ export const demoFunc = () => html`
.title=${'Modern file uploader'} .title=${'Modern file uploader'}
.subtitle=${'Shadcn-inspired layout with drag & drop, previews and validation'} .subtitle=${'Shadcn-inspired layout with drag & drop, previews and validation'}
> >
<dees-form>
<div class="demo-grid demo-grid--two"> <div class="demo-grid demo-grid--two">
<div class="demo-stack"> <div class="demo-stack">
<dees-input-fileupload <dees-input-fileupload
.label=${'Attachments'} .label=${'Attachments'}
.description=${'Upload supporting documents for your request'} .infoText=${'Upload supporting documents for your request'}
.description=${'Accepted formats: images, PDF, and ZIP archives up to 10MB'}
.accept=${'image/*,.pdf,.zip'} .accept=${'image/*,.pdf,.zip'}
.maxSize=${10 * 1024 * 1024} .maxSize=${10 * 1024 * 1024}
></dees-input-fileupload> ></dees-input-fileupload>
<dees-input-fileupload <dees-input-fileupload
.label=${'Brand assets'} .label=${'Brand assets'}
.description=${'Upload high-resolution imagery (JPG/PNG)'} .infoText=${'Upload high-resolution imagery (JPG/PNG)'}
.accept=${'image/jpeg,image/png'} .accept=${'image/jpeg,image/png'}
.multiple=${false} .multiple=${false}
.maxSize=${5 * 1024 * 1024} .maxSize=${5 * 1024 * 1024}
@@ -77,18 +79,19 @@ export const demoFunc = () => html`
<div class="demo-stack"> <div class="demo-stack">
<dees-input-fileupload <dees-input-fileupload
.label=${'Audio uploads'} .label=${'Audio uploads'}
.description=${'Share podcast drafts (MP3/WAV, max 25MB each)'} .infoText=${'Share podcast drafts (MP3/WAV, max 25MB each)'}
.accept=${'audio/*'} .accept=${'audio/*'}
.maxSize=${25 * 1024 * 1024} .maxSize=${25 * 1024 * 1024}
></dees-input-fileupload> ></dees-input-fileupload>
<dees-input-fileupload <dees-input-fileupload
.label=${'Disabled example'} .label=${'Disabled example'}
.description=${'Uploader is disabled while moderation is pending'} .infoText=${'Uploader is disabled while moderation is pending'}
.disabled=${true} .disabled=${true}
></dees-input-fileupload> ></dees-input-fileupload>
</div> </div>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel <dees-panel
@@ -97,10 +100,9 @@ export const demoFunc = () => html`
> >
<div class="demo-grid"> <div class="demo-grid">
<dees-form> <dees-form>
<div class="demo-stack">
<dees-input-text <dees-input-text
.label=${'Project name'} .label=${'Project name'}
.description=${'How should we refer to this project internally?'} .infoText=${'How should we refer to this project internally?'}
.required=${true} .required=${true}
.key=${'projectName'} .key=${'projectName'}
></dees-input-text> ></dees-input-text>
@@ -114,7 +116,7 @@ export const demoFunc = () => html`
<dees-input-fileupload <dees-input-fileupload
.label=${'Statement of work'} .label=${'Statement of work'}
.description=${'Upload a signed statement of work (PDF, max 15MB)'} .infoText=${'Upload a signed statement of work (PDF, max 15MB)'}
.required=${true} .required=${true}
.accept=${'application/pdf'} .accept=${'application/pdf'}
.maxSize=${15 * 1024 * 1024} .maxSize=${15 * 1024 * 1024}
@@ -124,7 +126,7 @@ export const demoFunc = () => html`
<dees-input-fileupload <dees-input-fileupload
.label=${'Creative references'} .label=${'Creative references'}
.description=${'Optional. Upload up to five visual references'} .infoText=${'Optional. Upload up to five visual references'}
.accept=${'image/*'} .accept=${'image/*'}
.maxFiles=${5} .maxFiles=${5}
.maxSize=${8 * 1024 * 1024} .maxSize=${8 * 1024 * 1024}
@@ -133,13 +135,12 @@ export const demoFunc = () => html`
<dees-input-text <dees-input-text
.label=${'Notes'} .label=${'Notes'}
.description=${'Add optional context for reviewers'} .infoText=${'Add optional context for reviewers'}
.inputType=${'textarea'} .inputType=${'textarea'}
.key=${'notes'} .key=${'notes'}
></dees-input-text> ></dees-input-text>
<dees-form-submit .text=${'Submit briefing'}></dees-form-submit> <dees-form-submit .text=${'Submit briefing'}></dees-form-submit>
</div>
</dees-form> </dees-form>
<div class="demo-note"> <div class="demo-note">
@@ -13,12 +13,6 @@ export const demoFunc = () => html`
margin: 0 auto; margin: 0 auto;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.payment-group { .payment-group {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -30,21 +24,23 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'Basic IBAN Input'} .subtitle=${'International Bank Account Number with automatic formatting'}> <dees-panel .title=${'Basic IBAN Input'} .subtitle=${'International Bank Account Number with automatic formatting'}>
<div class="input-group"> <dees-form>
<dees-input-iban <dees-input-iban
.label=${'Bank Account IBAN'} .label=${'Bank Account IBAN'}
.description=${'Enter your International Bank Account Number'} .infoText=${'Enter your International Bank Account Number'}
.description=${'Your IBAN can be found on your bank statement'}
></dees-input-iban> ></dees-input-iban>
<dees-input-iban <dees-input-iban
.label=${'Verified IBAN'} .label=${'Verified IBAN'}
.description=${'This IBAN has been verified'} .infoText=${'This IBAN has been verified'}
.value=${'DE89370400440532013000'} .value=${'DE89370400440532013000'}
></dees-input-iban> ></dees-input-iban>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Payment Information'} .subtitle=${'IBAN input with horizontal layout for payment forms'}> <dees-panel .title=${'Payment Information'} .subtitle=${'IBAN input with horizontal layout for payment forms'}>
<dees-form>
<div class="payment-group"> <div class="payment-group">
<dees-input-text <dees-input-text
.label=${'Account Holder'} .label=${'Account Holder'}
@@ -58,30 +54,31 @@ export const demoFunc = () => html`
.value=${'GB82WEST12345698765432'} .value=${'GB82WEST12345698765432'}
></dees-input-iban> ></dees-input-iban>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Validation & States'} .subtitle=${'Required fields and disabled states'}> <dees-panel .title=${'Validation & States'} .subtitle=${'Required fields and disabled states'}>
<div class="input-group"> <dees-form>
<dees-input-iban <dees-input-iban
.label=${'Payment Account'} .label=${'Payment Account'}
.description=${'Required for processing payments'} .infoText=${'Required for processing payments'}
.required=${true} .required=${true}
></dees-input-iban> ></dees-input-iban>
<dees-input-iban <dees-input-iban
.label=${'Locked IBAN'} .label=${'Locked IBAN'}
.description=${'This IBAN cannot be changed'} .infoText=${'This IBAN cannot be changed'}
.value=${'FR1420041010050500013M02606'} .value=${'FR1420041010050500013M02606'}
.disabled=${true} .disabled=${true}
></dees-input-iban> ></dees-input-iban>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Bank Transfer Form'} .subtitle=${'Complete form example with IBAN validation'}> <dees-panel .title=${'Bank Transfer Form'} .subtitle=${'Complete form example with IBAN validation'}>
<dees-form> <dees-form>
<dees-input-text .label=${'Recipient Name'} .required=${true}></dees-input-text> <dees-input-text .label=${'Recipient Name'} .required=${true}></dees-input-text>
<dees-input-iban .label=${'Recipient IBAN'} .required=${true}></dees-input-iban> <dees-input-iban .label=${'Recipient IBAN'} .required=${true}></dees-input-iban>
<dees-input-text .label=${'Transfer Reference'} .description=${'Optional reference for the transfer'}></dees-input-text> <dees-input-text .label=${'Transfer Reference'} .infoText=${'Optional reference for the transfer'}></dees-input-text>
<dees-input-text .label=${'Amount'} .inputType=${'number'} .required=${true}></dees-input-text> <dees-input-text .label=${'Amount'} .inputType=${'number'} .required=${true}></dees-input-text>
</dees-form> </dees-form>
</dees-panel> </dees-panel>
@@ -44,16 +44,27 @@ export class DeesInputIban extends DeesInputBase<DeesInputIban> {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label || 'IBAN'} .description=${this.description}></dees-label> <dees-label .label=${this.label || 'IBAN'} .infoText=${this.infoText}></dees-label>
<dees-input-text <dees-input-text
.value=${this.value} .value=${this.value}
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required} .required=${this.required}
.placeholder=${'DE89 3704 0044 0532 0130 00'} .placeholder=${'DE89 3704 0044 0532 0130 00'}
.validationFunction=${(value: string) => {
const normalized = value.replace(/ /g, '');
if (normalized.length === 0) {
return { valid: true, message: '' };
}
const isValid = ibantools.isValidIBAN(normalized);
return isValid
? { valid: true, message: 'IBAN is valid' }
: { valid: false, message: 'Please enter a valid IBAN' };
}}
@input=${(eventArg: InputEvent) => { @input=${(eventArg: InputEvent) => {
this.validateIban(eventArg); this.validateIban(eventArg);
}} }}
></dees-input-text> ></dees-input-text>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -81,10 +92,6 @@ export class DeesInputIban extends DeesInputBase<DeesInputIban> {
} }
} }
this.enteredIbanIsValid = ibantools.isValidIBAN(this.enteredString.replace(/ /g, '')); this.enteredIbanIsValid = ibantools.isValidIBAN(this.enteredString.replace(/ /g, ''));
const deesInputText = this.shadowRoot!.querySelector('dees-input-text') as any;
if (deesInputText) {
deesInputText.validationText = `IBAN is valid: ${this.enteredIbanIsValid}`;
}
} }
public getValue(): string { public getValue(): string {
@@ -109,6 +109,7 @@ export const demoFunc = () => html`
</dees-panel> </dees-panel>
<dees-panel .title=${'3. Validation & Constraints'} .subtitle=${'Lists with minimum/maximum items and duplicate prevention'}> <dees-panel .title=${'3. Validation & Constraints'} .subtitle=${'Lists with minimum/maximum items and duplicate prevention'}>
<dees-form>
<div class="grid-layout"> <div class="grid-layout">
<dees-input-list <dees-input-list
.label=${'Team Members (Min 2, Max 5)'} .label=${'Team Members (Min 2, Max 5)'}
@@ -128,6 +129,7 @@ export const demoFunc = () => html`
.description=${'Duplicate items are not allowed'} .description=${'Duplicate items are not allowed'}
></dees-input-list> ></dees-input-list>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'4. Delete Confirmation'} .subtitle=${'Require confirmation before deleting items'}> <dees-panel .title=${'4. Delete Confirmation'} .subtitle=${'Require confirmation before deleting items'}>
@@ -373,13 +373,6 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
line-height: 1.4; line-height: 1.4;
} }
.description {
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
font-size: 12px;
margin-top: 4px;
line-height: 1.4;
}
/* Scrollbar styling */ /* Scrollbar styling */
.list-items::-webkit-scrollbar { .list-items::-webkit-scrollbar {
width: 8px; width: 8px;
@@ -546,9 +539,7 @@ export class DeesInputList extends DeesInputBase<DeesInputList> {
<div class="validation-message">${this.validationText}</div> <div class="validation-message">${this.validationText}</div>
` : ''} ` : ''}
${this.description ? html` ${this.renderDescription()}
<div class="description">${this.description}</div>
` : ''}
</div> </div>
`; `;
} }
@@ -53,50 +53,52 @@ export const demoFunc = () => html`
<div class="section-title">Multi-Option Toggle</div> <div class="section-title">Multi-Option Toggle</div>
<div class="section-description">Select from multiple options with a smooth sliding indicator animation.</div> <div class="section-description">Select from multiple options with a smooth sliding indicator animation.</div>
<dees-form>
<dees-input-multitoggle <dees-input-multitoggle
.label=${'Display Mode'} .label=${'Display Mode'}
.description=${'Choose how content is displayed'} .infoText=${'Choose how content is displayed'}
.description=${'This setting affects how items appear on your dashboard'}
.options=${['List View', 'Grid View', 'Compact']} .options=${['List View', 'Grid View', 'Compact']}
.selectedOption=${'Grid View'} .selectedOption=${'Grid View'}
></dees-input-multitoggle> ></dees-input-multitoggle>
<br><br>
<dees-input-multitoggle <dees-input-multitoggle
.label=${'T-Shirt Size'} .label=${'T-Shirt Size'}
.description=${'Select your preferred size'} .infoText=${'Select your preferred size'}
.options=${['XS', 'S', 'M', 'L', 'XL', 'XXL']} .options=${['XS', 'S', 'M', 'L', 'XL', 'XXL']}
.selectedOption=${'M'} .selectedOption=${'M'}
></dees-input-multitoggle> ></dees-input-multitoggle>
</dees-form>
</div> </div>
<div class="section"> <div class="section">
<div class="section-title">Boolean Toggle</div> <div class="section-title">Boolean Toggle</div>
<div class="section-description">Simple on/off switches with customizable labels for clearer context.</div> <div class="section-description">Simple on/off switches with customizable labels for clearer context.</div>
<dees-form>
<dees-input-multitoggle <dees-input-multitoggle
.label=${'Notifications'} .label=${'Notifications'}
.description=${'Enable or disable push notifications'} .infoText=${'Enable or disable push notifications'}
.type=${'boolean'} .type=${'boolean'}
.selectedOption=${'true'} .selectedOption=${'true'}
></dees-input-multitoggle> ></dees-input-multitoggle>
<br><br>
<dees-input-multitoggle <dees-input-multitoggle
.label=${'Theme Mode'} .label=${'Theme Mode'}
.description=${'Switch between light and dark theme'} .infoText=${'Switch between light and dark theme'}
.type=${'boolean'} .type=${'boolean'}
.booleanTrueName=${'Dark'} .booleanTrueName=${'Dark'}
.booleanFalseName=${'Light'} .booleanFalseName=${'Light'}
.selectedOption=${'Dark'} .selectedOption=${'Dark'}
></dees-input-multitoggle> ></dees-input-multitoggle>
</dees-form>
</div> </div>
<div class="section"> <div class="section">
<div class="section-title">Settings Grid</div> <div class="section-title">Settings Grid</div>
<div class="section-description">Configuration options arranged in a responsive grid layout.</div> <div class="section-description">Configuration options arranged in a responsive grid layout.</div>
<dees-form>
<div class="settings-grid"> <div class="settings-grid">
<dees-input-multitoggle <dees-input-multitoggle
.label=${'Auto-Save'} .label=${'Auto-Save'}
@@ -126,6 +128,7 @@ export const demoFunc = () => html`
.selectedOption=${'Private'} .selectedOption=${'Private'}
></dees-input-multitoggle> ></dees-input-multitoggle>
</div> </div>
</dees-form>
</div> </div>
<div class="section"> <div class="section">
@@ -134,7 +137,7 @@ export const demoFunc = () => html`
<dees-input-multitoggle <dees-input-multitoggle
.label=${'Account Type'} .label=${'Account Type'}
.description=${'This setting is locked'} .infoText=${'This setting is locked'}
.options=${['Free', 'Pro', 'Enterprise']} .options=${['Free', 'Pro', 'Enterprise']}
.selectedOption=${'Enterprise'} .selectedOption=${'Enterprise'}
.disabled=${true} .disabled=${true}
@@ -146,7 +146,7 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText}></dees-label>
<div class="mainbox"> <div class="mainbox">
<div class="selections"> <div class="selections">
<div class="indicator"></div> <div class="indicator"></div>
@@ -158,6 +158,7 @@ export class DeesInputMultitoggle extends DeesInputBase<DeesInputMultitoggle> {
)} )}
</div> </div>
</div> </div>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -13,12 +13,6 @@ export const demoFunc = () => html`
margin: 0 auto; margin: 0 auto;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.horizontal-group { .horizontal-group {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -30,23 +24,25 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'Basic Phone Input'} .subtitle=${'Automatic formatting for phone numbers'}> <dees-panel .title=${'Basic Phone Input'} .subtitle=${'Automatic formatting for phone numbers'}>
<div class="input-group"> <dees-form>
<dees-input-phone <dees-input-phone
.label=${'Phone Number'} .label=${'Phone Number'}
.description=${'Enter your phone number with country code'} .infoText=${'Enter your phone number with country code'}
.description=${'Include country code for international numbers'}
.value=${'5551234567'} .value=${'5551234567'}
></dees-input-phone> ></dees-input-phone>
<dees-input-phone <dees-input-phone
.label=${'Contact Phone'} .label=${'Contact Phone'}
.description=${'Required for account verification'} .infoText=${'Required for account verification'}
.required=${true} .required=${true}
.placeholder=${'+1 (555) 000-0000'} .placeholder=${'+1 (555) 000-0000'}
></dees-input-phone> ></dees-input-phone>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Horizontal Layout'} .subtitle=${'Phone inputs arranged horizontally'}> <dees-panel .title=${'Horizontal Layout'} .subtitle=${'Phone inputs arranged horizontally'}>
<dees-form>
<div class="horizontal-group"> <div class="horizontal-group">
<dees-input-phone <dees-input-phone
.label=${'Mobile'} .label=${'Mobile'}
@@ -60,13 +56,14 @@ export const demoFunc = () => html`
.placeholder=${'+1 (800) 555-0000'} .placeholder=${'+1 (800) 555-0000'}
></dees-input-phone> ></dees-input-phone>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'International Numbers'} .subtitle=${'Supports formatting for numbers with country codes'}> <dees-panel .title=${'International Numbers'} .subtitle=${'Supports formatting for numbers with country codes'}>
<div class="input-group"> <dees-form>
<dees-input-phone <dees-input-phone
.label=${'International Contact'} .label=${'International Contact'}
.description=${'Automatically formats international numbers'} .infoText=${'Automatically formats international numbers'}
.value=${'441234567890'} .value=${'441234567890'}
></dees-input-phone> ></dees-input-phone>
@@ -75,7 +72,7 @@ export const demoFunc = () => html`
.value=${'911'} .value=${'911'}
.disabled=${true} .disabled=${true}
></dees-input-phone> ></dees-input-phone>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Form Integration'} .subtitle=${'Phone input as part of a contact form'}> <dees-panel .title=${'Form Integration'} .subtitle=${'Phone input as part of a contact form'}>
@@ -47,7 +47,7 @@ export class DeesInputPhone extends DeesInputBase<DeesInputPhone> {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText}></dees-label>
<dees-input-text <dees-input-text
.value=${this.formattedPhone} .value=${this.formattedPhone}
.disabled=${this.disabled} .disabled=${this.disabled}
@@ -55,6 +55,7 @@ export class DeesInputPhone extends DeesInputBase<DeesInputPhone> {
.placeholder=${this.placeholder} .placeholder=${this.placeholder}
@input=${(event: InputEvent) => this.handlePhoneInput(event)} @input=${(event: InputEvent) => this.handlePhoneInput(event)}
></dees-input-text> ></dees-input-text>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -14,12 +14,6 @@ export const demoFunc = () => html`
margin: 0 auto; margin: 0 auto;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.shopping-grid { .shopping-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
@@ -66,19 +60,20 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'Basic Quantity Selector'} .subtitle=${'Simple quantity input with increment/decrement buttons'}> <dees-panel .title=${'Basic Quantity Selector'} .subtitle=${'Simple quantity input with increment/decrement buttons'}>
<div class="input-group"> <dees-form>
<dees-input-quantityselector <dees-input-quantityselector
.label=${'Quantity'} .label=${'Quantity'}
.description=${'Select the desired quantity'} .infoText=${'Select the desired quantity'}
.description=${'Minimum order quantity is 1 item'}
.value=${1} .value=${1}
></dees-input-quantityselector> ></dees-input-quantityselector>
<dees-input-quantityselector <dees-input-quantityselector
.label=${'Items in Cart'} .label=${'Items in Cart'}
.description=${'Adjust the quantity of items'} .infoText=${'Adjust the quantity of items'}
.value=${3} .value=${3}
></dees-input-quantityselector> ></dees-input-quantityselector>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Shopping Cart'} .subtitle=${'Modern e-commerce product cards with interactive quantity selectors'} .runAfterRender=${async (elementArg: HTMLElement) => { <dees-panel .title=${'Shopping Cart'} .subtitle=${'Modern e-commerce product cards with interactive quantity selectors'} .runAfterRender=${async (elementArg: HTMLElement) => {
@@ -177,21 +172,21 @@ export const demoFunc = () => html`
</dees-panel> </dees-panel>
<dees-panel .title=${'Required & Disabled States'} .subtitle=${'Different states for validation and restrictions'}> <dees-panel .title=${'Required & Disabled States'} .subtitle=${'Different states for validation and restrictions'}>
<div class="input-group"> <dees-form>
<dees-input-quantityselector <dees-input-quantityselector
.label=${'Number of Licenses'} .label=${'Number of Licenses'}
.description=${'Select how many licenses you need'} .infoText=${'Select how many licenses you need'}
.required=${true} .required=${true}
.value=${1} .value=${1}
></dees-input-quantityselector> ></dees-input-quantityselector>
<dees-input-quantityselector <dees-input-quantityselector
.label=${'Fixed Quantity'} .label=${'Fixed Quantity'}
.description=${'This quantity cannot be changed'} .infoText=${'This quantity cannot be changed'}
.disabled=${true} .disabled=${true}
.value=${5} .value=${5}
></dees-input-quantityselector> ></dees-input-quantityselector>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Order Form'} .subtitle=${'Complete order form with quantity selection'}> <dees-panel .title=${'Order Form'} .subtitle=${'Complete order form with quantity selection'}>
@@ -204,7 +199,7 @@ export const demoFunc = () => html`
></dees-input-dropdown> ></dees-input-dropdown>
<dees-input-quantityselector <dees-input-quantityselector
.label=${'Quantity'} .label=${'Quantity'}
.description=${'Number of licenses'} .infoText=${'Number of licenses'}
.value=${1} .value=${1}
></dees-input-quantityselector> ></dees-input-quantityselector>
<dees-input-text <dees-input-text
@@ -129,7 +129,7 @@ export class DeesInputQuantitySelector extends DeesInputBase<DeesInputQuantitySe
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
${this.label ? html`<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label>` : ''} ${this.label ? html`<dees-label .label=${this.label} .infoText=${this.infoText} .required=${this.required}></dees-label>` : ''}
<div <div
class="quantity-container ${this.disabled ? 'disabled' : ''}" class="quantity-container ${this.disabled ? 'disabled' : ''}"
data-min="${this.value <= 0}" data-min="${this.value <= 0}"
@@ -162,6 +162,7 @@ export class DeesInputQuantitySelector extends DeesInputBase<DeesInputQuantitySe
aria-label="Increase quantity" aria-label="Increase quantity"
>+</div> >+</div>
</div> </div>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -23,12 +23,6 @@ export const demoFunc = () => html`
margin-bottom: 0; margin-bottom: 0;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.demo-grid { .demo-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
@@ -48,6 +42,7 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'1. Basic Radio Groups'} .subtitle=${'Simple string options for common use cases'}> <dees-panel .title=${'1. Basic Radio Groups'} .subtitle=${'Simple string options for common use cases'}>
<dees-form>
<div class="demo-grid"> <div class="demo-grid">
<dees-input-radiogroup <dees-input-radiogroup
.label=${'Subscription Plan'} .label=${'Subscription Plan'}
@@ -63,10 +58,11 @@ export const demoFunc = () => html`
.required=${true} .required=${true}
></dees-input-radiogroup> ></dees-input-radiogroup>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'2. Horizontal Layout'} .subtitle=${'Radio groups with horizontal arrangement'}> <dees-panel .title=${'2. Horizontal Layout'} .subtitle=${'Radio groups with horizontal arrangement'}>
<div class="input-group"> <dees-form>
<dees-input-radiogroup <dees-input-radiogroup
.label=${'Do you agree with the terms?'} .label=${'Do you agree with the terms?'}
.options=${['Yes', 'No', 'Maybe']} .options=${['Yes', 'No', 'Maybe']}
@@ -81,7 +77,7 @@ export const demoFunc = () => html`
.selectedOption=${'Intermediate'} .selectedOption=${'Intermediate'}
.description=${'Select your experience level with web development'} .description=${'Select your experience level with web development'}
></dees-input-radiogroup> ></dees-input-radiogroup>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'3. Advanced Options'} .subtitle=${'Using object format with keys and payloads'}> <dees-panel .title=${'3. Advanced Options'} .subtitle=${'Using object format with keys and payloads'}>
@@ -106,6 +102,7 @@ export const demoFunc = () => html`
</dees-panel> </dees-panel>
<dees-panel .title=${'4. Survey Example'} .subtitle=${'Multiple radio groups for surveys and forms'}> <dees-panel .title=${'4. Survey Example'} .subtitle=${'Multiple radio groups for surveys and forms'}>
<dees-form>
<div class="demo-grid"> <div class="demo-grid">
<dees-input-radiogroup <dees-input-radiogroup
.label=${'How satisfied are you?'} .label=${'How satisfied are you?'}
@@ -119,9 +116,11 @@ export const demoFunc = () => html`
.selectedOption=${'Probably'} .selectedOption=${'Probably'}
></dees-input-radiogroup> ></dees-input-radiogroup>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'5. States & Validation'} .subtitle=${'Different states and validation examples'}> <dees-panel .title=${'5. States & Validation'} .subtitle=${'Different states and validation examples'}>
<dees-form>
<div class="demo-grid"> <div class="demo-grid">
<dees-input-radiogroup <dees-input-radiogroup
.label=${'Required Selection'} .label=${'Required Selection'}
@@ -137,10 +136,11 @@ export const demoFunc = () => html`
.disabled=${true} .disabled=${true}
></dees-input-radiogroup> ></dees-input-radiogroup>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'6. Settings Example'} .subtitle=${'Common patterns in application settings'}> <dees-panel .title=${'6. Settings Example'} .subtitle=${'Common patterns in application settings'}>
<div class="input-group"> <dees-form>
<dees-input-radiogroup <dees-input-radiogroup
.label=${'Theme Preference'} .label=${'Theme Preference'}
.options=${[ .options=${[
@@ -165,7 +165,7 @@ export const demoFunc = () => html`
.selectedOption=${'English'} .selectedOption=${'English'}
.direction=${'horizontal'} .direction=${'horizontal'}
></dees-input-radiogroup> ></dees-input-radiogroup>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'7. Form Integration'} .subtitle=${'Works seamlessly with dees-form'}> <dees-panel .title=${'7. Form Integration'} .subtitle=${'Works seamlessly with dees-form'}>
@@ -189,14 +189,6 @@ export class DeesInputRadiogroup extends DeesInputBase<string | object> {
line-height: 20px; line-height: 20px;
} }
.description-text {
font-size: 13px;
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
margin-top: 10px;
line-height: 1.5;
letter-spacing: -0.003em;
}
/* Validation styles */ /* Validation styles */
:host([validationState="invalid"]) .radio-circle { :host([validationState="invalid"]) .radio-circle {
border-color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')}; border-color: ${cssManager.bdTheme('hsl(0 72.2% 50.6%)', 'hsl(0 62.8% 30.6%)')};
@@ -256,7 +248,7 @@ export class DeesInputRadiogroup extends DeesInputBase<string | object> {
`; `;
})} })}
</div> </div>
${this.description ? html`<div class="description-text">${this.description}</div>` : ''} ${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -278,13 +278,6 @@ export const richtextStyles = [
border-color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 93.9%)')}; border-color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 93.9%)')};
} }
.description {
margin-top: 8px;
font-size: 12px;
color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')};
line-height: 1.4;
}
:host([disabled]) dees-tile { :host([disabled]) dees-tile {
opacity: 0.6; opacity: 0.6;
cursor: not-allowed; cursor: not-allowed;
@@ -26,7 +26,7 @@ export const renderRichtext = (component: DeesInputRichtext): TemplateResult =>
` `
: ''} : ''}
</dees-tile> </dees-tile>
${component.description ? html`<div class="description">${component.description}</div>` : ''} ${component.renderDescription()}
</div> </div>
`; `;
@@ -115,6 +115,7 @@ export const demoFunc = () => html`
</dees-panel> </dees-panel>
<dees-panel .title=${'3. Limited Tags'} .subtitle=${'Restrict the number of tags users can add'}> <dees-panel .title=${'3. Limited Tags'} .subtitle=${'Restrict the number of tags users can add'}>
<dees-form>
<div class="grid-layout"> <div class="grid-layout">
<dees-input-tags <dees-input-tags
.label=${'Top 3 Skills'} .label=${'Top 3 Skills'}
@@ -133,6 +134,7 @@ export const demoFunc = () => html`
.description=${'Choose up to 5 categories'} .description=${'Choose up to 5 categories'}
></dees-input-tags> ></dees-input-tags>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'4. Required & Validation'} .subtitle=${'Tags input with validation requirements'}> <dees-panel .title=${'4. Required & Validation'} .subtitle=${'Tags input with validation requirements'}>
@@ -210,14 +210,6 @@ export class DeesInputTags extends DeesInputBase<DeesInputTags> {
line-height: 1.5; line-height: 1.5;
} }
/* Description styles */
.description {
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
font-size: 13px;
margin-top: 6px;
line-height: 1.5;
}
/* Scrollbar styling */ /* Scrollbar styling */
.suggestions-dropdown::-webkit-scrollbar { .suggestions-dropdown::-webkit-scrollbar {
width: 8px; width: 8px;
@@ -302,9 +294,7 @@ export class DeesInputTags extends DeesInputBase<DeesInputTags> {
<div class="validation-message">${this.validationText}</div> <div class="validation-message">${this.validationText}</div>
` : ''} ` : ''}
${this.description ? html` ${this.renderDescription()}
<div class="description">${this.description}</div>
` : ''}
</div> </div>
`; `;
} }
@@ -30,12 +30,6 @@ export const demoFunc = () => html`
flex-wrap: wrap; flex-wrap: wrap;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.grid-layout { .grid-layout {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
@@ -89,17 +83,18 @@ export const demoFunc = () => html`
} }
}}> }}>
<dees-panel .title=${'Basic Text Inputs'} .subtitle=${'Standard text inputs with labels and descriptions'}> <dees-panel .title=${'Basic Text Inputs'} .subtitle=${'Standard text inputs with labels and descriptions'}>
<div class="input-group"> <dees-form>
<dees-input-text <dees-input-text
.label=${'Username'} .label=${'Username'}
.value=${'johndoe'} .value=${'johndoe'}
.key=${'username'} .key=${'username'}
.description=${'Your username will be visible to other users'}
></dees-input-text> ></dees-input-text>
<dees-input-text <dees-input-text
.label=${'Email Address'} .label=${'Email Address'}
.value=${'john@example.com'} .value=${'john@example.com'}
.description=${'We will never share your email with anyone'} .infoText=${'We will never share your email with anyone'}
.key=${'email'} .key=${'email'}
></dees-input-text> ></dees-input-text>
@@ -109,7 +104,7 @@ export const demoFunc = () => html`
.value=${'secret123'} .value=${'secret123'}
.key=${'password'} .key=${'password'}
></dees-input-text> ></dees-input-text>
</div> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
@@ -139,6 +134,7 @@ export const demoFunc = () => html`
} }
}}> }}>
<dees-panel .title=${'Horizontal Layout'} .subtitle=${'Multiple inputs arranged horizontally for compact forms'}> <dees-panel .title=${'Horizontal Layout'} .subtitle=${'Multiple inputs arranged horizontally for compact forms'}>
<dees-form>
<div class="horizontal-group"> <div class="horizontal-group">
<dees-input-text <dees-input-text
.label=${'First Name'} .label=${'First Name'}
@@ -161,6 +157,7 @@ export const demoFunc = () => html`
.key=${'age'} .key=${'age'}
></dees-input-text> ></dees-input-text>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
@@ -180,7 +177,7 @@ export const demoFunc = () => html`
} }
}}> }}>
<dees-panel .title=${'Label Positions'} .subtitle=${'Different label positioning options for various layouts'}> <dees-panel .title=${'Label Positions'} .subtitle=${'Different label positioning options for various layouts'}>
<div class="input-group"> <dees-form>
<dees-input-text <dees-input-text
.label=${'Label on Top (Default)'} .label=${'Label on Top (Default)'}
.value=${'Standard layout'} .value=${'Standard layout'}
@@ -206,45 +203,13 @@ export const demoFunc = () => html`
.labelPosition=${'left'} .labelPosition=${'left'}
></dees-input-text> ></dees-input-text>
</div> </div>
</div> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => { <dees-demowrapper>
// Demonstrate validation states
const requiredInput = elementArg.querySelector('dees-input-text[required]') as DeesInputText;
const disabledInput = elementArg.querySelector('dees-input-text[disabled]') as DeesInputText;
const errorInput = elementArg.querySelector('dees-input-text[validationState="invalid"]') as DeesInputText;
if (requiredInput) {
// Show validation on blur for empty required field
requiredInput.addEventListener('blur', () => {
if (!requiredInput.getValue()) {
console.log('Required field is empty!');
}
});
}
if (disabledInput) {
console.log('Disabled input cannot be edited');
}
if (errorInput) {
console.log('Error input shows validation message:', errorInput.validationText);
// Simulate fixing the error
errorInput.addEventListener('changeSubject', () => {
const value = errorInput.getValue();
if (value.includes('@') && value.includes('.')) {
errorInput.validationState = 'valid';
errorInput.validationText = '';
console.log('Email validation passed!');
}
});
}
}}>
<dees-panel .title=${'Validation & States'} .subtitle=${'Different validation states and input configurations'}> <dees-panel .title=${'Validation & States'} .subtitle=${'Different validation states and input configurations'}>
<div class="input-group"> <dees-form>
<dees-input-text <dees-input-text
.label=${'Required Field'} .label=${'Required Field'}
.required=${true} .required=${true}
@@ -258,12 +223,17 @@ export const demoFunc = () => html`
></dees-input-text> ></dees-input-text>
<dees-input-text <dees-input-text
.label=${'Field with Error'} .label=${'Email with Validation'}
.value=${'invalid@'} .value=${'invalid@'}
.validationText=${'Please enter a valid email address'} .validationFunction=${(value: string) => {
.validationState=${'invalid'} const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (emailRegex.test(value)) {
return { valid: true, message: 'Email address is valid' };
}
return { valid: false, message: 'Please enter a valid email address' };
}}
></dees-input-text> ></dees-input-text>
</div> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
@@ -292,54 +262,35 @@ export const demoFunc = () => html`
}); });
}}> }}>
<dees-panel .title=${'Advanced Features'} .subtitle=${'Password visibility toggle and other advanced features'}> <dees-panel .title=${'Advanced Features'} .subtitle=${'Password visibility toggle and other advanced features'}>
<div class="input-group"> <dees-form>
<dees-input-text <dees-input-text
.label=${'Password with Toggle'} .label=${'Password with Toggle'}
.isPasswordBool=${true} .isPasswordBool=${true}
.value=${'mySecurePassword123'} .value=${'mySecurePassword123'}
.description=${'Click the eye icon to show/hide password'} .infoText=${'Click the eye icon to show/hide password'}
></dees-input-text> ></dees-input-text>
<dees-input-text <dees-input-text
.label=${'API Key'} .label=${'API Key'}
.isPasswordBool=${true} .isPasswordBool=${true}
.value=${'sk-1234567890abcdef'} .value=${'sk-1234567890abcdef'}
.description=${'Keep this key secure and never share it'} .infoText=${'Keep this key secure and never share it'}
></dees-input-text> ></dees-input-text>
</div> </dees-form>
</dees-panel> </dees-panel>
</dees-demowrapper> </dees-demowrapper>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => { <dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
// Set up interactive example const dynamicInput = elementArg.querySelector('dees-input-text') as DeesInputText;
const dynamicInput = elementArg.querySelector('dees-input-text');
const output = elementArg.querySelector('#text-input-output'); const output = elementArg.querySelector('#text-input-output');
if (dynamicInput && output) { if (dynamicInput && output) {
// Update output on every change dynamicInput.changeSubject.subscribe(() => {
dynamicInput.addEventListener('changeSubject', ((event: CustomEvent) => { output.textContent = `Current value: "${dynamicInput.getValue()}"`;
const value = (event.detail as DeesInputText).getValue();
output.textContent = `Current value: "${value}"`;
}) as EventListener);
// Also track focus/blur events
dynamicInput.addEventListener('focus', () => {
console.log('Input focused');
});
dynamicInput.addEventListener('blur', () => {
console.log('Input blurred');
});
// Track keypress events
let keypressCount = 0;
dynamicInput.addEventListener('keydown', () => {
keypressCount++;
console.log(`Keypress count: ${keypressCount}`);
}); });
} }
}}> }}>
<dees-panel .title=${'Interactive Example'} .subtitle=${'Try typing in the inputs to see real-time value changes'}> <dees-panel .title=${'Interactive Example'} .subtitle=${'Try typing in the input to see real-time value changes'}>
<dees-input-text <dees-input-text
.label=${'Dynamic Input'} .label=${'Dynamic Input'}
.placeholder=${'Type something here...'} .placeholder=${'Type something here...'}
@@ -7,11 +7,13 @@ import {
customElement, customElement,
type TemplateResult, type TemplateResult,
property, property,
state,
html, html,
cssManager, cssManager,
css, css,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { themeDefaultStyles } from '../../00theme.js'; import { themeDefaultStyles } from '../../00theme.js';
import '../../00group-overlay/dees-contextmenu/dees-contextmenu.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@@ -44,7 +46,7 @@ export class DeesInputText extends DeesInputBase {
accessor showPasswordBool = false; accessor showPasswordBool = false;
@property({ @property({
type: Boolean, type: String,
reflect: true, reflect: true,
}) })
accessor validationState!: 'valid' | 'warn' | 'invalid'; accessor validationState!: 'valid' | 'warn' | 'invalid';
@@ -54,8 +56,8 @@ export class DeesInputText extends DeesInputBase {
}) })
accessor validationText: string = ''; accessor validationText: string = '';
@property({}) @property({ attribute: false })
accessor validationFunction!: (value: string) => boolean; accessor validationFunction!: (value: string) => { valid: boolean; message?: string };
@property({ @property({
type: Boolean, type: Boolean,
@@ -63,6 +65,11 @@ export class DeesInputText extends DeesInputBase {
}) })
accessor vintegrated: boolean = false; accessor vintegrated: boolean = false;
@property({ attribute: false })
accessor validConfirmed: boolean = false;
private validTimeout: ReturnType<typeof setTimeout> | undefined;
public static styles = [ public static styles = [
themeDefaultStyles, themeDefaultStyles,
...DeesInputBase.baseStyles, ...DeesInputBase.baseStyles,
@@ -127,7 +134,7 @@ export class DeesInputText extends DeesInputBase {
.showPassword { .showPassword {
position: absolute; position: absolute;
right: 1px; right: 1px;
top: 50%; top: 20px;
transform: translateY(-50%); transform: translateY(-50%);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -145,6 +152,29 @@ export class DeesInputText extends DeesInputBase {
color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 93.9%)')}; color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 93.9%)')};
} }
/* Valid checkmark icon */
.validIcon {
position: absolute;
right: 10px;
top: 20px;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')};
}
.validIcon.show {
opacity: 1;
}
.validIcon dees-icon {
font-size: 18px;
}
/* Validation styles */ /* Validation styles */
.validationContainer { .validationContainer {
margin-top: 4px; margin-top: 4px;
@@ -156,7 +186,7 @@ export class DeesInputText extends DeesInputBase {
overflow: hidden; overflow: hidden;
} }
.validationContainer.error { .validationContainer.invalid {
background: ${cssManager.bdTheme('hsl(0 84.2% 60.2% / 0.1)', 'hsl(0 72.2% 50.6% / 0.1)')}; background: ${cssManager.bdTheme('hsl(0 84.2% 60.2% / 0.1)', 'hsl(0 72.2% 50.6% / 0.1)')};
color: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 72.2% 50.6%)')}; color: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 72.2% 50.6%)')};
} }
@@ -172,31 +202,31 @@ export class DeesInputText extends DeesInputBase {
} }
/* Error state for input */ /* Error state for input */
:host([validation-state="invalid"]) input { :host([validationstate="invalid"]) input {
border-color: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 72.2% 50.6%)')}; border-color: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 72.2% 50.6%)')};
} }
:host([validation-state="invalid"]) input:focus { :host([validationstate="invalid"]) input:focus {
border-color: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 72.2% 50.6%)')}; border-color: ${cssManager.bdTheme('hsl(0 84.2% 60.2%)', 'hsl(0 72.2% 50.6%)')};
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 84.2% 60.2% / 0.05)', 'hsl(0 72.2% 50.6% / 0.05)')}; box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 84.2% 60.2% / 0.05)', 'hsl(0 72.2% 50.6% / 0.05)')};
} }
/* Warning state for input */ /* Warning state for input */
:host([validation-state="warn"]) input { :host([validationstate="warn"]) input {
border-color: ${cssManager.bdTheme('hsl(25 95% 53%)', 'hsl(25 95% 63%)')}; border-color: ${cssManager.bdTheme('hsl(25 95% 53%)', 'hsl(25 95% 63%)')};
} }
:host([validation-state="warn"]) input:focus { :host([validationstate="warn"]) input:focus {
border-color: ${cssManager.bdTheme('hsl(25 95% 53%)', 'hsl(25 95% 63%)')}; border-color: ${cssManager.bdTheme('hsl(25 95% 53%)', 'hsl(25 95% 63%)')};
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(25 95% 53% / 0.05)', 'hsl(25 95% 63% / 0.05)')}; box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(25 95% 53% / 0.05)', 'hsl(25 95% 63% / 0.05)')};
} }
/* Valid state for input */ /* Valid state for input */
:host([validation-state="valid"]) input { :host([validationstate="valid"]) input {
border-color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')}; border-color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')};
} }
:host([validation-state="valid"]) input:focus { :host([validationstate="valid"]) input:focus {
border-color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')}; border-color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')};
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(142.1 76.2% 36.3% / 0.05)', 'hsl(142.1 70.6% 45.3% / 0.05)')}; box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(142.1 76.2% 36.3% / 0.05)', 'hsl(142.1 70.6% 45.3% / 0.05)')};
} }
@@ -239,9 +269,9 @@ export class DeesInputText extends DeesInputBase {
input { input {
font-family: ${this.isPasswordBool ? cssMonoFontFamily : 'inherit'}; font-family: ${this.isPasswordBool ? cssMonoFontFamily : 'inherit'};
letter-spacing: ${this.isPasswordBool ? '0.5px' : 'normal'}; letter-spacing: ${this.isPasswordBool ? '0.5px' : 'normal'};
padding-right: ${this.isPasswordBool ? '48px' : '12px'}; padding-right: ${this.isPasswordBool ? '48px' : this.validConfirmed ? '40px' : '12px'};
} }
${this.validationText ${this.validationText && !this.validConfirmed
? css` ? css`
.validationContainer { .validationContainer {
height: auto; height: auto;
@@ -259,7 +289,7 @@ export class DeesInputText extends DeesInputBase {
`} `}
</style> </style>
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText} .required=${this.required}></dees-label>
<div class="maincontainer"> <div class="maincontainer">
<input <input
type="${this.isPasswordBool && !this.showPasswordBool ? 'password' : 'text'}" type="${this.isPasswordBool && !this.showPasswordBool ? 'password' : 'text'}"
@@ -275,28 +305,114 @@ export class DeesInputText extends DeesInputBase {
</div> </div>
` `
: html``} : html``}
<div class="validIcon ${this.validConfirmed ? 'show' : ''}">
<dees-icon .icon=${'lucide:Check'}></dees-icon>
</div>
${this.validationText ${this.validationText
? html` ? html`
<div class="validationContainer ${this.validationState || 'error'}"> <div class="validationContainer ${this.validationState || 'invalid'}">
${this.validationText} ${this.validationText}
</div> </div>
` `
: html`<div class="validationContainer"></div>`} : html`<div class="validationContainer"></div>`}
${this.renderDescription()}
</div> </div>
</div> </div>
`; `;
} }
firstUpdated() { firstUpdated() {
// Input event handling is already done in updateValue method if (this.validationFunction && this.value) {
const result = this.validationFunction(this.value);
this.validationState = result.valid ? 'valid' : 'invalid';
this.validationText = result.message || '';
if (result.valid) {
this.validConfirmed = false;
this.validTimeout = setTimeout(() => {
this.validConfirmed = true;
this.validationState = undefined as any;
}, 500);
}
}
} }
public async updateValue(eventArg: Event) { public async updateValue(eventArg: Event) {
const target: any = eventArg.target; const target: any = eventArg.target;
this.value = target.value; this.value = target.value;
if (this.validationFunction) {
const result = this.validationFunction(this.value);
this.validationState = result.valid ? 'valid' : 'invalid';
this.validationText = result.message || '';
if (result.valid) {
this.validConfirmed = false;
clearTimeout(this.validTimeout);
this.validTimeout = setTimeout(() => {
this.validConfirmed = true;
this.validationState = undefined as any;
}, 500);
} else {
clearTimeout(this.validTimeout);
this.validConfirmed = false;
}
}
this.changeSubject.next(this); this.changeSubject.next(this);
} }
public getContextMenuItems() {
const input = this.shadowRoot!.querySelector('input')!;
const hasSelection = input.selectionStart !== input.selectionEnd;
return [
{
name: 'Cut',
iconName: 'lucide:Scissors',
shortcut: 'Cmd+X',
disabled: !hasSelection,
action: async () => {
const selected = this.value.substring(input.selectionStart!, input.selectionEnd!);
await navigator.clipboard.writeText(selected);
const start = input.selectionStart!;
this.value = this.value.substring(0, start) + this.value.substring(input.selectionEnd!);
input.value = this.value;
input.setSelectionRange(start, start);
this.changeSubject.next(this);
},
},
{
name: 'Copy',
iconName: 'lucide:Copy',
shortcut: 'Cmd+C',
disabled: !hasSelection,
action: async () => {
const selected = this.value.substring(input.selectionStart!, input.selectionEnd!);
await navigator.clipboard.writeText(selected);
},
},
{
name: 'Paste',
iconName: 'lucide:ClipboardPaste',
shortcut: 'Cmd+V',
action: async () => {
const text = await navigator.clipboard.readText();
const start = input.selectionStart!;
const end = input.selectionEnd!;
this.value = this.value.substring(0, start) + text + this.value.substring(end);
input.value = this.value;
input.setSelectionRange(start + text.length, start + text.length);
this.changeSubject.next(this);
},
},
{ divider: true },
{
name: 'Select All',
iconName: 'lucide:TextCursorInput',
shortcut: 'Cmd+A',
action: async () => {
input.select();
},
},
];
}
public getValue(): string { public getValue(): string {
return this.value; return this.value;
} }
@@ -116,7 +116,7 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'Basic Toggle'} .subtitle=${'Simple on/off toggle switch with drag support'}> <dees-panel .title=${'Basic Toggle'} .subtitle=${'Simple on/off toggle switch with drag support'}>
<div class="toggle-group"> <dees-form>
<dees-input-toggle <dees-input-toggle
.label=${'Enable feature'} .label=${'Enable feature'}
.value=${false} .value=${false}
@@ -135,12 +135,12 @@ export const demoFunc = () => html`
.description=${'This toggle has additional helper text explaining its purpose'} .description=${'This toggle has additional helper text explaining its purpose'}
.key=${'withDesc'} .key=${'withDesc'}
></dees-input-toggle> ></dees-input-toggle>
</div> </dees-form>
<p class="drag-hint">Tip: You can drag the toggle knob to switch states</p> <p class="drag-hint">Tip: You can drag the toggle knob to switch states</p>
</dees-panel> </dees-panel>
<dees-panel .title=${'Toggle States'} .subtitle=${'Different toggle states and configurations'}> <dees-panel .title=${'Toggle States'} .subtitle=${'Different toggle states and configurations'}>
<div class="toggle-group"> <dees-form>
<dees-input-toggle <dees-input-toggle
.label=${'Default (off)'} .label=${'Default (off)'}
.value=${false} .value=${false}
@@ -169,10 +169,11 @@ export const demoFunc = () => html`
.required=${true} .required=${true}
.description=${'This toggle cannot be turned off'} .description=${'This toggle cannot be turned off'}
></dees-input-toggle> ></dees-input-toggle>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Horizontal Layout'} .subtitle=${'Toggles arranged horizontally for compact interfaces'}> <dees-panel .title=${'Horizontal Layout'} .subtitle=${'Toggles arranged horizontally for compact interfaces'}>
<dees-form>
<div class="horizontal-toggles"> <div class="horizontal-toggles">
<dees-input-toggle <dees-input-toggle
.label=${'WiFi'} .label=${'WiFi'}
@@ -198,13 +199,14 @@ export const demoFunc = () => html`
.layoutMode=${'horizontal'} .layoutMode=${'horizontal'}
></dees-input-toggle> ></dees-input-toggle>
</div> </div>
</dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Settings Example'} .subtitle=${'Toggles in a typical settings context'}> <dees-panel .title=${'Settings Example'} .subtitle=${'Toggles in a typical settings context'}>
<div class="settings-section"> <div class="settings-section">
<h4 class="section-title">Notification Settings</h4> <h4 class="section-title">Notification Settings</h4>
<div class="toggle-group"> <dees-form>
<dees-input-toggle <dees-input-toggle
.label=${'Push notifications'} .label=${'Push notifications'}
.value=${true} .value=${true}
@@ -232,7 +234,7 @@ export const demoFunc = () => html`
.description=${'Vibrate for notifications'} .description=${'Vibrate for notifications'}
.key=${'vibration'} .key=${'vibration'}
></dees-input-toggle> ></dees-input-toggle>
</div> </dees-form>
</div> </div>
</dees-panel> </dees-panel>
@@ -243,7 +245,7 @@ export const demoFunc = () => html`
</div> </div>
<div class="feature-toggles"> <div class="feature-toggles">
<div class="toggle-group"> <dees-form>
<dees-input-toggle <dees-input-toggle
.label=${'Dark Mode'} .label=${'Dark Mode'}
.value=${true} .value=${true}
@@ -273,12 +275,12 @@ export const demoFunc = () => html`
.value=${false} .value=${false}
.key=${'beta'} .key=${'beta'}
></dees-input-toggle> ></dees-input-toggle>
</div> </dees-form>
</div> </div>
</dees-panel> </dees-panel>
<dees-panel .title=${'Interactive Example'} .subtitle=${'Toggle to see value changes in real-time'}> <dees-panel .title=${'Interactive Example'} .subtitle=${'Toggle to see value changes in real-time'}>
<div class="toggle-group"> <dees-form>
<dees-input-toggle <dees-input-toggle
.label=${'Airplane mode'} .label=${'Airplane mode'}
.value=${false} .value=${false}
@@ -300,7 +302,7 @@ export const demoFunc = () => html`
} }
}} }}
></dees-input-toggle> ></dees-input-toggle>
</div> </dees-form>
<div class="interactive-section"> <div class="interactive-section">
<div id="airplane-output" class="output-text">Airplane mode: OFF</div> <div id="airplane-output" class="output-text">Airplane mode: OFF</div>
@@ -170,12 +170,6 @@ export class DeesInputToggle extends DeesInputBase<DeesInputToggle> {
color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')}; color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')};
} }
/* Description */
.description-text {
font-size: 12px;
color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')};
line-height: 1.5;
}
`, `,
]; ];
@@ -199,7 +193,7 @@ export class DeesInputToggle extends DeesInputBase<DeesInputToggle> {
</div> </div>
<div class="label-container"> <div class="label-container">
${this.label ? html`<div class="toggle-label">${this.label}</div>` : ''} ${this.label ? html`<div class="toggle-label">${this.label}</div>` : ''}
${this.description ? html`<div class="description-text">${this.description}</div>` : ''} ${this.renderDescription()}
</div> </div>
</div> </div>
</div> </div>
@@ -13,12 +13,6 @@ export const demoFunc = () => html`
margin: 0 auto; margin: 0 auto;
} }
.input-group {
display: flex;
flex-direction: column;
gap: 16px;
}
.horizontal-group { .horizontal-group {
display: flex; display: flex;
gap: 24px; gap: 24px;
@@ -45,26 +39,27 @@ export const demoFunc = () => html`
<div class="demo-container"> <div class="demo-container">
<dees-panel .title=${'Basic Type List'} .subtitle=${'Add and remove items from a list'}> <dees-panel .title=${'Basic Type List'} .subtitle=${'Add and remove items from a list'}>
<div class="input-group"> <dees-form>
<dees-input-typelist <dees-input-typelist
.label=${'Tags'} .label=${'Tags'}
.description=${'Add tags by typing and pressing Enter'} .infoText=${'Add tags by typing and pressing Enter'}
.description=${'Tags help categorize and filter your content'}
.value=${['javascript', 'typescript', 'web-components']} .value=${['javascript', 'typescript', 'web-components']}
></dees-input-typelist> ></dees-input-typelist>
<dees-input-typelist <dees-input-typelist
.label=${'Team Members'} .label=${'Team Members'}
.description=${'Add email addresses of team members'} .infoText=${'Add email addresses of team members'}
.value=${['alice@example.com', 'bob@example.com']} .value=${['alice@example.com', 'bob@example.com']}
></dees-input-typelist> ></dees-input-typelist>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Skills & Keywords'} .subtitle=${'Manage lists of skills and keywords'}> <dees-panel .title=${'Skills & Keywords'} .subtitle=${'Manage lists of skills and keywords'}>
<div class="input-group"> <dees-form>
<dees-input-typelist <dees-input-typelist
.label=${'Your Skills'} .label=${'Your Skills'}
.description=${'List your professional skills'} .infoText=${'List your professional skills'}
.value=${['HTML', 'CSS', 'JavaScript', 'Node.js', 'React']} .value=${['HTML', 'CSS', 'JavaScript', 'Node.js', 'React']}
></dees-input-typelist> ></dees-input-typelist>
@@ -81,25 +76,25 @@ export const demoFunc = () => html`
.value=${['innovation', 'startup', 'growth']} .value=${['innovation', 'startup', 'growth']}
></dees-input-typelist> ></dees-input-typelist>
</div> </div>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Required & Disabled States'} .subtitle=${'Different input states for validation'}> <dees-panel .title=${'Required & Disabled States'} .subtitle=${'Different input states for validation'}>
<div class="input-group"> <dees-form>
<dees-input-typelist <dees-input-typelist
.label=${'Project Dependencies'} .label=${'Project Dependencies'}
.description=${'List all required npm packages'} .infoText=${'List all required npm packages'}
.required=${true} .required=${true}
.value=${['@design.estate/dees-element', '@design.estate/dees-domtools']} .value=${['@design.estate/dees-element', '@design.estate/dees-domtools']}
></dees-input-typelist> ></dees-input-typelist>
<dees-input-typelist <dees-input-typelist
.label=${'System Tags'} .label=${'System Tags'}
.description=${'These tags are managed by the system'} .infoText=${'These tags are managed by the system'}
.disabled=${true} .disabled=${true}
.value=${['system', 'protected', 'readonly']} .value=${['system', 'protected', 'readonly']}
></dees-input-typelist> ></dees-input-typelist>
</div> </dees-form>
</dees-panel> </dees-panel>
<dees-panel .title=${'Article Publishing Form'} .subtitle=${'Complete form with tag management'}> <dees-panel .title=${'Article Publishing Form'} .subtitle=${'Complete form with tag management'}>
@@ -108,16 +103,16 @@ export const demoFunc = () => html`
<dees-input-text <dees-input-text
.label=${'Summary'} .label=${'Summary'}
.inputType=${'textarea'} .inputType=${'textarea'}
.description=${'Brief description of the article'} .infoText=${'Brief description of the article'}
></dees-input-text> ></dees-input-text>
<dees-input-typelist <dees-input-typelist
.label=${'Tags'} .label=${'Tags'}
.description=${'Add relevant tags for better discoverability'} .infoText=${'Add relevant tags for better discoverability'}
.value=${['tutorial', 'web-development']} .value=${['tutorial', 'web-development']}
></dees-input-typelist> ></dees-input-typelist>
<dees-input-typelist <dees-input-typelist
.label=${'Co-Authors'} .label=${'Co-Authors'}
.description=${'Add email addresses of co-authors'} .infoText=${'Add email addresses of co-authors'}
></dees-input-typelist> ></dees-input-typelist>
</dees-form> </dees-form>
@@ -153,7 +153,7 @@ export class DeesInputTypelist extends DeesInputBase<DeesInputTypelist> {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText}></dees-label>
<div class="mainbox"> <div class="mainbox">
<div class="tags" @click=${() => { <div class="tags" @click=${() => {
this.shadowRoot!.querySelector('input')!.focus(); this.shadowRoot!.querySelector('input')!.focus();
@@ -188,6 +188,7 @@ export class DeesInputTypelist extends DeesInputBase<DeesInputTypelist> {
.disabled=${this.disabled} .disabled=${this.disabled}
/> />
</div> </div>
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -292,7 +292,7 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
return html` return html`
<dees-label <dees-label
.label="${this.label}" .label="${this.label}"
.description="${this.description}" .infoText="${this.infoText}"
.required="${this.required}" .required="${this.required}"
></dees-label> ></dees-label>
<div class="wysiwyg-container"> <div class="wysiwyg-container">
@@ -303,6 +303,7 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
<!-- Blocks will be rendered programmatically --> <!-- Blocks will be rendered programmatically -->
</div> </div>
</div> </div>
${this.renderDescription()}
`; `;
} }
@@ -273,7 +273,7 @@ export class DeesInputProfilePicture extends DeesInputBase<DeesInputProfilePictu
render(): TemplateResult { render(): TemplateResult {
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label> <dees-label .label=${this.label} .infoText=${this.infoText} .required=${this.required}></dees-label>
<div <div
class="profile-container" class="profile-container"
@@ -329,6 +329,7 @@ export class DeesInputProfilePicture extends DeesInputBase<DeesInputProfilePictu
accept="${this.acceptedFormats.join(',')}" accept="${this.acceptedFormats.join(',')}"
@change=${this.handleFileSelect} @change=${this.handleFileSelect}
/> />
${this.renderDescription()}
</div> </div>
`; `;
} }
@@ -85,31 +85,31 @@ export const demoFunc = () => html`
</div> </div>
<div class="demo-section"> <div class="demo-section">
<h3>Description (Info Icon)</h3> <h3>Info Text (Info Icon)</h3>
<p>When <code>description</code> is set, an info icon appears next to the label. Hover over it to see the tooltip.</p> <p>When <code>infoText</code> is set, an info icon appears next to the label. Hover over it to see the tooltip.</p>
<div class="label-grid"> <div class="label-grid">
<div class="label-row"> <div class="label-row">
<span class="annotation">description="..."</span> <span class="annotation">infoText="..."</span>
<dees-label .label=${'API Key'} .description=${'Your API key can be found in the developer settings dashboard.'}></dees-label> <dees-label .label=${'API Key'} .infoText=${'Your API key can be found in the developer settings dashboard.'}></dees-label>
</div> </div>
<div class="label-row"> <div class="label-row">
<span class="annotation">short description</span> <span class="annotation">short infoText</span>
<dees-label .label=${'Region'} .description=${'Select your nearest datacenter.'}></dees-label> <dees-label .label=${'Region'} .infoText=${'Select your nearest datacenter.'}></dees-label>
</div> </div>
</div> </div>
</div> </div>
<div class="demo-section"> <div class="demo-section">
<h3>Required + Description</h3> <h3>Required + Info Text</h3>
<p>Both indicators can be combined. The asterisk appears first, then the info icon.</p> <p>Both indicators can be combined. The asterisk appears first, then the info icon.</p>
<div class="label-grid"> <div class="label-grid">
<div class="label-row"> <div class="label-row">
<span class="annotation">required + description</span> <span class="annotation">required + infoText</span>
<dees-label .label=${'Password'} .required=${true} .description=${'Must be at least 8 characters with one uppercase letter and one number.'}></dees-label> <dees-label .label=${'Password'} .required=${true} .infoText=${'Must be at least 8 characters with one uppercase letter and one number.'}></dees-label>
</div> </div>
<div class="label-row"> <div class="label-row">
<span class="annotation">required + description</span> <span class="annotation">required + infoText</span>
<dees-label .label=${'Email Address'} .required=${true} .description=${'We will send a verification link to this address.'}></dees-label> <dees-label .label=${'Email Address'} .required=${true} .infoText=${'We will send a verification link to this address.'}></dees-label>
</div> </div>
</div> </div>
</div> </div>
@@ -32,7 +32,7 @@ export class DeesLabel extends DeesElement {
type: String, type: String,
reflect: true, reflect: true,
}) })
accessor description!: string; accessor infoText!: string;
@property({ @property({
type: Boolean, type: Boolean,
@@ -67,11 +67,26 @@ export class DeesLabel extends DeesElement {
margin-left: 2px; margin-left: 2px;
} }
dees-icon { .description-icon {
font-size: 12px; display: inline-flex;
margin-left: 4px; align-items: center;
justify-content: center;
width: 24px;
height: 24px;
margin: -6px 0 -6px 2px;
border-radius: 4px;
cursor: default;
transition: background 0.15s ease, color 0.15s ease;
color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')}; color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')};
cursor: help; }
.description-icon:hover {
background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.06)', 'hsl(0 0% 100% / 0.08)')};
color: ${cssManager.bdTheme('hsl(0 0% 25%)', 'hsl(0 0% 80%)')};
}
.description-icon dees-icon {
font-size: 14px;
} }
`, `,
]; ];
@@ -83,10 +98,12 @@ export class DeesLabel extends DeesElement {
<div class="label"> <div class="label">
${this.label} ${this.label}
${this.required ? html`<span class="required">*</span>` : ''} ${this.required ? html`<span class="required">*</span>` : ''}
${this.description ${this.infoText
? html` ? html`
<div class="description-icon">
<dees-icon .icon=${'lucide:info'}></dees-icon> <dees-icon .icon=${'lucide:info'}></dees-icon>
<dees-speechbubble .text=${this.description}></dees-speechbubble> </div>
<dees-speechbubble .text=${this.infoText}></dees-speechbubble>
` `
: html``} : html``}
</div> </div>
@@ -0,0 +1,65 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './dees-settings.js';
import type { ISettingsField, ISettingsAction } from './dees-settings.js';
export const demoFunc = () => {
const acmeFields: ISettingsField[] = [
{ key: 'email', label: 'Account email', value: 'admin@example.com' },
{ key: 'status', label: 'Status', value: 'enabled' },
{ key: 'mode', label: 'Mode', value: 'production' },
{ key: 'autoRenew', label: 'Auto-renew', value: 'on' },
{ key: 'threshold', label: 'Renewal threshold', value: '30 days' },
];
const acmeActions: ISettingsAction[] = [
{ name: 'Edit', action: () => console.log('Edit clicked') },
];
const emptyActions: ISettingsAction[] = [
{ name: 'Configure', action: () => console.log('Configure clicked') },
];
const multiActions: ISettingsAction[] = [
{ name: 'Reset', action: () => console.log('Reset clicked') },
{ name: 'Edit', action: () => console.log('Edit clicked') },
];
return html`
<dees-demowrapper>
<style>
${css`
.demoBox {
background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(0 0% 9%)')};
padding: 40px;
display: flex;
flex-direction: column;
gap: 24px;
max-width: 600px;
}
`}
</style>
<div class="demoBox">
<dees-settings
.heading=${'ACME Settings'}
.settingsFields=${acmeFields}
.actions=${acmeActions}
></dees-settings>
<dees-settings
.heading=${'ACME Settings'}
.description=${'No ACME configuration yet. Click Configure to set up automated TLS certificate issuance.'}
.actions=${emptyActions}
></dees-settings>
<dees-settings
.heading=${'Server Config'}
.settingsFields=${[
{ key: 'host', label: 'Hostname', value: 'proxy.example.com' },
{ key: 'port', label: 'Port', value: '443' },
]}
.actions=${multiActions}
></dees-settings>
</div>
</dees-demowrapper>
`;
};
@@ -0,0 +1,196 @@
import {
customElement,
DeesElement,
html,
css,
cssManager,
property,
type TemplateResult,
} from '@design.estate/dees-element';
import { demoFunc } from './dees-settings.demo.js';
import { cssGeistFontFamily } from '../../00fonts.js';
import { themeDefaultStyles } from '../../00theme.js';
import '../../00group-layout/dees-tile/dees-tile.js';
declare global {
interface HTMLElementTagNameMap {
'dees-settings': DeesSettings;
}
}
export interface ISettingsField {
key: string;
label: string;
value: string | TemplateResult;
}
export interface ISettingsAction {
name: string;
action: () => void | Promise<void>;
}
/**
* dees-settings — a read-only settings display tile with modal-style footer actions.
*
* Renders a dees-tile with a heading, a grid of label/value fields,
* and a footer action bar. When an action is clicked the component
* dispatches a `settings-action` CustomEvent with the action name.
*/
@customElement('dees-settings')
export class DeesSettings extends DeesElement {
public static demo = demoFunc;
public static demoGroups = ['Layout'];
@property({ type: String })
accessor heading: string = '';
@property({ type: String })
accessor description: string = '';
@property({ attribute: false })
accessor settingsFields: ISettingsField[] = [];
@property({ attribute: false })
accessor actions: ISettingsAction[] = [];
public static styles = [
themeDefaultStyles,
cssManager.defaultStyles,
css`
:host {
display: block;
font-family: ${cssGeistFontFamily};
}
.settingsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px 24px;
padding: 16px;
}
.settingsField {
display: flex;
flex-direction: column;
gap: 2px;
}
.fieldLabel {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--dees-color-text-muted);
}
.fieldValue {
font-size: 13px;
color: var(--dees-color-text-primary);
}
.settingsDescription {
padding: 16px;
font-size: 13px;
line-height: 1.5;
color: var(--dees-color-text-muted);
}
.bottomButtons {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: 0;
height: 36px;
width: 100%;
box-sizing: border-box;
}
.bottomButtons .bottomButton {
padding: 0 16px;
height: 100%;
text-align: center;
font-size: 12px;
font-weight: 500;
cursor: pointer;
user-select: none;
transition: all 0.15s ease;
background: transparent;
border: none;
border-left: 1px solid var(--dees-color-border-subtle);
color: var(--dees-color-text-muted);
white-space: nowrap;
display: flex;
align-items: center;
}
.bottomButtons .bottomButton:first-child {
border-left: none;
}
.bottomButtons .bottomButton:hover {
background: var(--dees-color-hover);
color: var(--dees-color-text-primary);
}
.bottomButtons .bottomButton:active {
background: ${cssManager.bdTheme('hsl(0 0% 92%)', 'hsl(0 0% 13%)')};
}
.bottomButtons .bottomButton.primary {
color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')};
font-weight: 600;
}
.bottomButtons .bottomButton.primary:hover {
background: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8% / 0.08)', 'hsl(213.1 93.9% 67.8% / 0.08)')};
color: ${cssManager.bdTheme('hsl(217.2 91.2% 50%)', 'hsl(213.1 93.9% 75%)')};
}
.bottomButtons .bottomButton.primary:active {
background: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8% / 0.15)', 'hsl(213.1 93.9% 67.8% / 0.15)')};
}
`,
];
public render(): TemplateResult {
const hasFields = this.settingsFields.length > 0;
const hasActions = this.actions.length > 0;
return html`
<dees-tile .heading=${this.heading}>
${hasFields
? html`
<div class="settingsGrid">
${this.settingsFields.map(
(field) => html`
<div class="settingsField">
<span class="fieldLabel">${field.label}</span>
<span class="fieldValue">${field.value}</span>
</div>
`,
)}
</div>
`
: html`
<div class="settingsDescription">${this.description}</div>
`}
${hasActions
? html`
<div slot="footer" class="bottomButtons">
${this.actions.map(
(actionArg, index) => html`
<div
class="bottomButton ${index === this.actions.length - 1 ? 'primary' : ''}"
@click=${() => actionArg.action()}
>
${actionArg.name}
</div>
`,
)}
</div>
`
: ''}
</dees-tile>
`;
}
}
@@ -0,0 +1 @@
export * from './dees-settings.js';
@@ -45,6 +45,9 @@ export class DeesTile extends DeesElement {
@property({ type: String }) @property({ type: String })
accessor heading: string = ''; accessor heading: string = '';
@property({ type: String, reflect: true })
accessor overscroll: 'contain' | 'auto' | 'none' = 'auto';
@state() @state()
accessor hasFooter: boolean = false; accessor hasFooter: boolean = false;
@@ -102,11 +105,14 @@ export class DeesTile extends DeesElement {
border-bottom: 1px solid var(--dees-color-border-subtle); border-bottom: 1px solid var(--dees-color-border-subtle);
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: var(--dees-color-scrollbar-thumb) transparent; scrollbar-color: var(--dees-color-scrollbar-thumb) transparent;
} }
:host([overscroll="contain"]) .tile-content {
overscroll-behavior: contain;
}
.tile-content.no-footer { .tile-content.no-footer {
border-bottom: none; border-bottom: none;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
+1
View File
@@ -5,5 +5,6 @@ export * from './dees-heading/index.js';
export * from './dees-label/index.js'; export * from './dees-label/index.js';
export * from './dees-pagination/index.js'; export * from './dees-pagination/index.js';
export * from './dees-panel/index.js'; export * from './dees-panel/index.js';
export * from './dees-settings/index.js';
export * from './dees-stepper/index.js'; export * from './dees-stepper/index.js';
export * from './dees-tile/index.js'; export * from './dees-tile/index.js';
@@ -343,7 +343,7 @@ export class DeesModal extends DeesElement {
${minWidthStyle ? `dees-tile { min-width: ${minWidthStyle}; }` : ''} ${minWidthStyle ? `dees-tile { min-width: ${minWidthStyle}; }` : ''}
</style> </style>
<div class="modalContainer" @click=${this.handleOutsideClick} style="z-index: ${this.modalZIndex}"> <div class="modalContainer" @click=${this.handleOutsideClick} style="z-index: ${this.modalZIndex}">
<dees-tile class="${widthClass} ${mobileFullscreenClass}"> <dees-tile class="${widthClass} ${mobileFullscreenClass}" .overscroll=${'contain'}>
<div slot="header" class="heading"> <div slot="header" class="heading">
<div class="heading-text">${this.heading}</div> <div class="heading-text">${this.heading}</div>
<div class="header-buttons"> <div class="header-buttons">
@@ -10,6 +10,7 @@ import {
css, css,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { themeDefaultStyles } from '../../00theme.js'; import { themeDefaultStyles } from '../../00theme.js';
import '../../00group-layout/dees-tile/dees-tile.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@@ -90,24 +91,25 @@ export class DeesSimpleLogin extends DeesElement {
color: var(--dees-color-text-muted); color: var(--dees-color-text-muted);
} }
.login-card { dees-tile {
background: var(--dees-color-bg-primary); width: 100%;
border: 1px solid var(--dees-color-border-default); }
border-radius: 8px;
dees-tile::part(content) {
padding: 24px; padding: 24px;
} }
.login-card dees-form { dees-tile dees-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
} }
.login-card dees-input-text { dees-tile dees-input-text {
width: 100%; width: 100%;
} }
.login-card dees-form-submit { dees-tile dees-form-submit {
margin-top: 8px; margin-top: 8px;
width: 100%; width: 100%;
} }
@@ -122,13 +124,13 @@ export class DeesSimpleLogin extends DeesElement {
<div class="header">Sign in</div> <div class="header">Sign in</div>
<div class="subheader">Enter your credentials to access ${this.name}</div> <div class="subheader">Enter your credentials to access ${this.name}</div>
</div> </div>
<div class="login-card"> <dees-tile .heading=${'Credentials'}>
<dees-form> <dees-form>
<dees-input-text key="username" label="Username" required></dees-input-text> <dees-input-text key="username" label="Username" required></dees-input-text>
<dees-input-text key="password" label="Password" isPasswordBool required></dees-input-text> <dees-input-text key="password" label="Password" isPasswordBool required></dees-input-text>
<dees-form-submit>Sign in</dees-form-submit> <dees-form-submit>Sign in</dees-form-submit>
</dees-form> </dees-form>
</div> </dees-tile>
</div> </div>
</div> </div>
<div class="slotContainer"> <div class="slotContainer">