Prepare 1.0.0-beta.6

This commit is contained in:
Ronan Berder
2026-06-23 18:14:19 +08:00
parent fbf3f47b75
commit a17eaa20d3
63 changed files with 2398 additions and 1502 deletions
+27 -1
View File
@@ -22,6 +22,22 @@
&[data-side='right'] {
@apply -translate-x-2;
}
&[data-side='inline-start'] {
@apply translate-x-2;
}
&[data-side='inline-start']:dir(rtl) {
@apply -translate-x-2;
}
&[data-side='inline-end'] {
@apply -translate-x-2;
}
&[data-side='inline-end']:dir(rtl) {
@apply translate-x-2;
}
}
&:not([data-side]),
@@ -41,6 +57,14 @@
@apply ms-1 start-full;
}
&[data-side='inline-start'] {
@apply me-1 end-full;
}
&[data-side='inline-end'] {
@apply ms-1 start-full;
}
&:not([data-side]),
&[data-side='bottom'],
&[data-side='top'] {
@@ -59,7 +83,9 @@
}
&[data-side='left'],
&[data-side='right'] {
&[data-side='right'],
&[data-side='inline-start'],
&[data-side='inline-end'] {
&:not([data-align]),
&[data-align='start'] {
@apply top-0;
+9 -1
View File
@@ -767,7 +767,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-full shadow-sm not-dark:bg-clip-padding;
@apply bg-background rounded-full shadow-sm not-dark:bg-clip-padding;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -747,7 +747,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-full;
@apply bg-background rounded-full;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -755,7 +755,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-full;
@apply bg-background rounded-full;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -759,7 +759,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-full;
@apply bg-background rounded-full;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -755,7 +755,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-full;
@apply bg-background rounded-full;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -767,7 +767,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-2xl shadow-sm not-dark:bg-clip-padding;
@apply bg-background rounded-2xl shadow-sm not-dark:bg-clip-padding;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -771,7 +771,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground;
@apply bg-background;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+9 -1
View File
@@ -755,7 +755,15 @@
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):before {
@apply bg-background dark:not-checked:bg-foreground dark:checked:bg-primary-foreground rounded-full;
@apply bg-background rounded-full;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not(:checked):before {
@apply bg-foreground;
}
.dark :is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):checked:before {
@apply bg-primary-foreground;
}
:is(.field > input[type='checkbox'][role='switch'], .input[type='checkbox'][role='switch']):not([data-size='sm']):before {
+4 -4
View File
@@ -5,7 +5,7 @@
@param selected {string|array} [optional] - The initially selected value(s).
@param name {string} [optional] - The name attribute for the hidden input storing the selected value.
@param multiple {boolean} [optional] [default=false] - Enables multiple selection mode.
@param placeholder {string} [optional] - Placeholder text shown when no options are selected (multiple mode only).
@param placeholder {string} [optional] - Placeholder text shown when no option is selected.
@param close_on_select {boolean} [optional] [default=false] - Closes the popover when selecting an option in multiple mode.
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
@param trigger_attrs {object} [optional] - Additional HTML attributes for the trigger button.
@@ -62,14 +62,14 @@
{% endfor %}
{% endif %}
{% set default_option = (selected_options.items[0] if selected_options.items else first_option.item) %}
{% set default_option = (selected_options.items[0] if selected_options.items else (none if placeholder else first_option.item)) %}
{% set labels = [] %}
{% if multiple and selected_options.items %}
{% for opt in selected_options.items %}
{% set labels = labels + [opt.label] %}
{% endfor %}
{% endif %}
{% set default_label = (', '.join(labels) if (multiple and selected_options.items) else ((placeholder or '') if (multiple and not selected_options.items) else (default_option.label if default_option else ''))) %}
{% set default_label = (', '.join(labels) if (multiple and selected_options.items) else ((placeholder or '') if (placeholder and not selected_options.items) else (default_option.label if default_option else ''))) %}
{% set selected_payload = namespace(items=[]) %}
{% if multiple and selected_options.items %}
{% for opt in selected_options.items %}
@@ -83,7 +83,7 @@
id="{{ id }}"
class="select {{ main_attrs.class }}"
{% if format == "object" %}data-format="object"{% endif %}
{% if multiple and placeholder %}data-placeholder="{{ placeholder }}"{% endif %}
{% if placeholder %}data-placeholder="{{ placeholder }}"{% endif %}
{% if multiple and close_on_select %}data-close-on-select="true"{% endif %}
{% for key, value in main_attrs.items() %}
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}
+46 -13
View File
@@ -59,6 +59,12 @@
return state.isMultiple ? JSON.stringify(value) : (value[0] || '');
};
const showPlaceholder = (state) => {
state.selectedLabel.textContent = state.placeholder || '';
state.selectedLabel.classList.toggle('text-muted-foreground', Boolean(state.placeholder));
state.input.value = state.isMultiple ? serializeSelection(state, []) : '';
};
const scrollOptionIntoListbox = (state, option) => {
const optionRect = option.getBoundingClientRect();
const listboxRect = state.listbox.getBoundingClientRect();
@@ -114,19 +120,26 @@
state.input.value = serializeSelection(state, selectedDetail);
} else {
const option = optionOrOptions;
if (!option) return;
if (option.dataset.label) {
state.selectedLabel.textContent = option.dataset.label;
if (!option) {
state.options.forEach(option => option.removeAttribute('aria-selected'));
showPlaceholder(state);
selectedDetail = null;
value = '';
} else {
state.selectedLabel.innerHTML = option.innerHTML;
if (option.dataset.label) {
state.selectedLabel.textContent = option.dataset.label;
} else {
state.selectedLabel.innerHTML = option.innerHTML;
}
state.selectedLabel.classList.remove('text-muted-foreground');
selectedDetail = toSelected(option);
value = selectedDetail.value;
state.input.value = serializeSelection(state, [selectedDetail]);
}
selectedDetail = toSelected(option);
value = selectedDetail.value;
state.input.value = serializeSelection(state, [selectedDetail]);
}
state.options.forEach(option => {
const isSelected = state.isMultiple ? state.selectedOptions.has(option) : option === optionOrOptions;
const isSelected = state.isMultiple ? state.selectedOptions.has(option) : optionOrOptions && option === optionOrOptions;
if (isSelected) {
option.setAttribute('aria-selected', 'true');
} else {
@@ -171,7 +184,7 @@
state.visibleOptions = [...state.options];
state.isMultiple = state.listbox.getAttribute('aria-multiselectable') === 'true';
state.format = getFormat(root);
state.placeholder = state.isMultiple ? (root.dataset.placeholder || '') : null;
state.placeholder = root.dataset.placeholder || '';
state.closeOnSelect = root.dataset.closeOnSelect === 'true';
if (state.isMultiple) {
@@ -183,10 +196,12 @@
updateValue(root, selected, false);
} else {
const value = parseStoredValues(previousValue, state);
const selected = state.options.find(option => getValue(option) === value)
const selected = value === '' && state.placeholder
? null
: state.options.find(option => getValue(option) === value)
|| state.options.find(option => option.getAttribute('aria-selected') === 'true');
state.options.forEach(option => option.removeAttribute('aria-selected'));
if (selected) updateValue(root, selected, false);
updateValue(root, selected || null, false);
}
const selectedOption = state.listbox.querySelector('[role="option"][aria-selected="true"]');
@@ -213,6 +228,11 @@
} else {
const option = state.options.find(opt => getValue(opt) === value);
if (!option) return;
if (state.placeholder && getValue(option) === '') {
updateValue(root, null);
closePopover(state);
return;
}
if (root.value !== value) updateValue(root, option);
closePopover(state);
}
@@ -255,7 +275,11 @@
toggleMultipleValue(root, option);
if (state.closeOnSelect) root.close();
} else {
if (root.value !== getValue(option)) updateValue(root, option);
if (state.placeholder && getValue(option) === '') {
updateValue(root, null);
} else if (root.value !== getValue(option)) {
updateValue(root, option);
}
root.close();
}
}
@@ -337,7 +361,11 @@
state.trigger.focus();
}
} else {
if (root.value !== getValue(option)) updateValue(root, option);
if (state.placeholder && getValue(option) === '') {
updateValue(root, null);
} else if (root.value !== getValue(option)) {
updateValue(root, option);
}
root.close();
}
};
@@ -385,6 +413,11 @@
const values = Array.isArray(value) ? value : (value != null ? [value] : []);
updateValue(root, values.map(v => state.options.find(option => getValue(option) === v)).filter(Boolean));
} else {
if (value == null || value === '') {
updateValue(root, null);
root.close();
return;
}
const option = state.options.find(opt => getValue(opt) === value);
if (option) {
updateValue(root, option);
+4 -4
View File
@@ -5,7 +5,7 @@
@param selected {string|array} [optional] - The initially selected value(s).
@param name {string} [optional] - The name attribute for the hidden input storing the selected value.
@param multiple {boolean} [optional] [default=false] - Enables multiple selection mode.
@param placeholder {string} [optional] - Placeholder text shown when no options are selected (multiple mode only).
@param placeholder {string} [optional] - Placeholder text shown when no option is selected.
@param close_on_select {boolean} [optional] [default=false] - Closes the popover when selecting an option in multiple mode.
@param format {string} [optional] [default="value"] - Hidden input serialization format: "value" or "object".
@param main_attrs {object} [optional] - Additional HTML attributes for the main container div.
@@ -63,14 +63,14 @@
{% endfor %}
{% endif %}
{% set default_option = (selected_options[0] if selected_options[0] else (first_option[0] if first_option[0] else None)) %}
{% set default_option = (selected_options[0] if selected_options[0] else (None if placeholder else (first_option[0] if first_option[0] else None))) %}
{% set labels = [] %}
{% if multiple and selected_options %}
{% for opt in selected_options %}
{% set labels = (labels.push(opt.label), labels) %}
{% endfor %}
{% endif %}
{% set default_label = (labels | join(', ') if (multiple and selected_options) else ((placeholder or '') if (multiple and not selected_options) else (default_option.label if default_option else ''))) %}
{% set default_label = (labels | join(', ') if (multiple and selected_options) else ((placeholder or '') if (placeholder and not selected_options) else (default_option.label if default_option else ''))) %}
{% set selected_payload = [] %}
{% if multiple and selected_options %}
{% for opt in selected_options %}
@@ -84,7 +84,7 @@
id="{{ id }}"
class="select {{ main_attrs.class }}"
{% if format == "object" %}data-format="object"{% endif %}
{% if multiple and placeholder %}data-placeholder="{{ placeholder }}"{% endif %}
{% if placeholder %}data-placeholder="{{ placeholder }}"{% endif %}
{% if multiple and close_on_select %}data-close-on-select="true"{% endif %}
{% for key, value in main_attrs %}
{% if key != 'class' %}{{ key }}="{{ value }}"{% endif %}