From a82779ed16a628f8813c0d0be77769d967bfa9f7 Mon Sep 17 00:00:00 2001 From: jaredgibb <70493366+jaredgibb@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:47:30 -0500 Subject: [PATCH 01/24] update --- src/wwElement_Trigger.vue | 14 ++++++++++- ww-config.js | 51 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/wwElement_Trigger.vue b/src/wwElement_Trigger.vue index 53e0dae..b328638 100644 --- a/src/wwElement_Trigger.vue +++ b/src/wwElement_Trigger.vue @@ -17,7 +17,8 @@
-
+ +
+ +
+ {{ displayCountText }} +
{{ data.placeholder }}
@@ -325,6 +330,12 @@ export default { emit('remove-multiselect-value', value); }; + const displayCountText = computed(() => { + const count = selectedDetails.value?.length || 0; + const template = wwLib.wwLang.getText(props.content.displayText) || 'Selected: {count}'; + return template.replace('{count}', count.toString()); + }); + return { isSingleSelect, data, @@ -349,6 +360,7 @@ export default { chipIconFromOption, isOpen, handleChipClick, + displayCountText, }; }, }; diff --git a/ww-config.js b/ww-config.js index 1312456..06020ce 100644 --- a/ww-config.js +++ b/ww-config.js @@ -194,6 +194,8 @@ export default { 'searchPlaceholder', 'initialState', 'selectType', + 'displayMode', + 'displayText', 'selectTypeWarning', ], }, @@ -521,6 +523,55 @@ export default { }, /* wwEditor:end */ }, + displayMode: { + label: 'Display mode', + type: 'TextSelect', + options: { + options: [ + { + value: 'chips', + label: 'Display chips', + }, + { + value: 'count', + label: 'Display count', + }, + ], + }, + bindable: true, + defaultValue: 'chips', + section: 'settings', + /* wwEditor:start */ + bindingValidation: { + type: 'string', + tooltip: 'A string value, either "chips" or "count"', + }, + propertyHelp: { + tooltip: 'Choose how to display selected items in multi-select mode.\nPossible values: chips, count', + }, + /* wwEditor:end */ + hidden: content => content.selectType !== 'multiple', + }, + displayText: { + label: 'Display text', + type: 'Text', + defaultValue: { en: 'Selected: {count}' }, + section: 'settings', + states: true, + bindable: true, + responsive: true, + multiLang: true, + /* wwEditor:start */ + bindingValidation: { + type: 'string', + tooltip: 'A string value with {count} placeholder: "Selected: {count}"', + }, + propertyHelp: { + tooltip: 'The text to display when using count mode. Use {count} as a placeholder for the number of selected items.', + }, + /* wwEditor:end */ + hidden: content => content.selectType !== 'multiple' || content.displayMode !== 'count', + }, disabled: { label: { en: 'Disabled' }, type: 'OnOff', From 7231ecfe0d13d4afa6c20574b45c6f65a3a1a364 Mon Sep 17 00:00:00 2001 From: jaredgibb <70493366+jaredgibb@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:46:52 -0500 Subject: [PATCH 02/24] commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary I've successfully implemented the "Sort Selected to Top" feature for your WeWeb select component. Here's what was added: 1. Configuration (ww-config.js) New Property: sortSelectedToTop Type: Toggle (On/Off) Default: false (off) Location: Settings → Options section Supports binding, states, and responsive settings 2. Component Logic (wwElement_OptionsList.vue) Injected Dependencies: Added access to selectedValue and mappingValue from the parent component Enhanced filteredOptions Computed Property: First applies search filtering (if active) Then applies sorting logic when sortSelectedToTop is enabled Compares each option's value against the selected value(s) Handles both primitive values and objects Handles both single and multiple selection modes Uses JSON comparison for complex object values with fallback to direct comparison How It Works: When disabled (default): Options display in their original order When enabled: Selected items automatically move to the top of the list Maintains relative order within selected and unselected groups Works seamlessly with search filtering Updates dynamically as items are selected/deselected The feature integrates perfectly with: Single and multiple select modes Search functionality Virtual scrolling (both heavy and dynamic modes) All option types (text, icon+text, image+text) Primitive and object-based options Users can now enable this in the WeWeb editor under Settings → Options → Sort selected to top! --- src/wwElement_OptionsList.vue | 62 +++++++++++++++++++++++++++++++++-- ww-config.js | 19 +++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/wwElement_OptionsList.vue b/src/wwElement_OptionsList.vue index eaf9041..4038837 100644 --- a/src/wwElement_OptionsList.vue +++ b/src/wwElement_OptionsList.vue @@ -99,6 +99,8 @@ export default { const searchState = inject('_wwSelect:searchState', ref(null)); const { updateSearch } = inject('_wwSelect:useSearch', {}); const registerOptionProperties = inject('_wwSelect:registerOptionProperties', () => {}); + const selectedValue = inject('_wwSelect:value', ref(null)); + const mappingValue = inject('_wwSelect:mappingValue', ref(null)); const virtualScrollMinItemSize = computed(() => props.content.virtualScrollMinItemSize); const virtualScrollBuffer = computed(() => props.content.virtualScrollBuffer); const heavyMode = computed(() => props.content.heavyMode); @@ -156,8 +158,64 @@ export default { }); const filteredOptions = computed(() => { - if (!searchState.value || !searchState.value.value) return options.value; - let filtered = memoizedFilter(options.value, searchState.value.value); + let filtered = options.value; + + // Apply search filter if active + if (searchState.value && searchState.value.value) { + filtered = memoizedFilter(options.value, searchState.value.value); + } + + // Apply sorting if sortSelectedToTop is enabled + if (props.content.sortSelectedToTop && selectedValue.value) { + filtered = [...filtered].sort((a, b) => { + // Get the value for each option + const aValue = typeof a === 'object' && a !== null + ? wwLib.resolveObjectPropertyPath(a, mappingValue.value?.code || 'value') + : a; + const bValue = typeof b === 'object' && b !== null + ? wwLib.resolveObjectPropertyPath(b, mappingValue.value?.code || 'value') + : b; + + // Check if each option is selected + const aIsSelected = Array.isArray(selectedValue.value) + ? selectedValue.value.some(v => { + try { + return JSON.stringify(v) === JSON.stringify(aValue); + } catch { + return v === aValue; + } + }) + : (() => { + try { + return JSON.stringify(selectedValue.value) === JSON.stringify(aValue); + } catch { + return selectedValue.value === aValue; + } + })(); + + const bIsSelected = Array.isArray(selectedValue.value) + ? selectedValue.value.some(v => { + try { + return JSON.stringify(v) === JSON.stringify(bValue); + } catch { + return v === bValue; + } + }) + : (() => { + try { + return JSON.stringify(selectedValue.value) === JSON.stringify(bValue); + } catch { + return selectedValue.value === bValue; + } + })(); + + // Sort selected items to top + if (aIsSelected && !bIsSelected) return -1; + if (!aIsSelected && bIsSelected) return 1; + return 0; // Keep original order for items with same selection status + }); + } + return filtered; }); diff --git a/ww-config.js b/ww-config.js index 06020ce..f7e1374 100644 --- a/ww-config.js +++ b/ww-config.js @@ -212,6 +212,7 @@ export default { 'closeOnClickOutside', 'manualTrigger', 'selectOnClick', + 'sortSelectedToTop', 'virtualScroll', 'virtualScrollBuffer', 'virtualScrollMinItemSize', @@ -1076,6 +1077,24 @@ export default { }, /* wwEditor:end */ }, + sortSelectedToTop: { + label: { en: 'Sort selected to top' }, + type: 'OnOff', + states: true, + bindable: true, + responsive: true, + defaultValue: false, + section: 'settings', + /* wwEditor:start */ + bindingValidation: { + type: 'boolean', + tooltip: 'Whether selected items should appear at the top of the options list: `true | false`', + }, + propertyHelp: { + tooltip: 'When enabled, selected options will be sorted to the top of the list.', + }, + /* wwEditor:end */ + }, unselectOnClick: { label: { en: 'Unselect on click' }, type: 'OnOff', From b7bdf63903ee359f8d36a4914da9b9382098cf89 Mon Sep 17 00:00:00 2001 From: jaredgibb <70493366+jaredgibb@users.noreply.github.com> Date: Thu, 13 Nov 2025 13:53:02 -0500 Subject: [PATCH 03/24] Update wwElement_OptionsList.vue --- src/wwElement_OptionsList.vue | 68 +++++++++++++++-------------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/src/wwElement_OptionsList.vue b/src/wwElement_OptionsList.vue index 4038837..6046794 100644 --- a/src/wwElement_OptionsList.vue +++ b/src/wwElement_OptionsList.vue @@ -52,9 +52,10 @@