compote-ui
Version:
An opinionated UI component library for Svelte, built on top of [Ark UI](https://ark-ui.com) with additional components and features not available in the core Ark UI library.
82 lines (81 loc) • 3.05 kB
JavaScript
/**
* Value handling for `type: 'select'` data-table columns. A select cell holds either one
* value or an array of values (multi-value / tag columns); both facet, filter and render
* through the same helpers so the Funnel works the same for either shape.
*/
/**
* Filter value standing for "no value". It is `null`, not a string, so it can never
* collide with a real option — a cell whose value is the string `'null'` stays a regular
* option. Every other selected option is the `String()` of the cell value.
*/
export const EMPTY_SELECT_VALUE = null;
function isEmpty(value) {
return value == null || value === '';
}
/**
* The values a select cell contributes to facets and filters: each distinct item of an
* array, or the scalar itself. Items are distinct by their filter value (`String()`), so a
* row counts once per option even when its array repeats an item. A missing cell (null,
* undefined, '' or an empty array) is one `EMPTY_SELECT_VALUE`, so it facets and filters
* as a single "empty" option.
*/
export function selectCellValues(value) {
if (!Array.isArray(value))
return [isEmpty(value) ? EMPTY_SELECT_VALUE : value];
const seen = new Set();
const items = [];
for (const item of value) {
if (isEmpty(item))
continue;
const key = String(item);
if (seen.has(key))
continue;
seen.add(key);
items.push(item);
}
return items.length ? items : [EMPTY_SELECT_VALUE];
}
function toFilterValue(item) {
return item === EMPTY_SELECT_VALUE ? EMPTY_SELECT_VALUE : String(item);
}
/** One-of filter: the cell matches when any of its values is among the selected options. */
export function matchesSelectFilter(value, filterValue) {
return selectCellValues(value).some((item) => filterValue.includes(toFilterValue(item)));
}
/**
* Filter options from a column's faceted unique values: sorted by label, with the empty
* option (labelled `emptyLabel`) last.
*/
export function selectFilterOptions(facets, emptyLabel = '(empty)') {
const counts = new Map();
let emptyCount = 0;
for (const [key, count] of facets) {
if (isEmpty(key)) {
emptyCount += count;
continue;
}
const value = String(key);
counts.set(value, (counts.get(value) ?? 0) + count);
}
const options = [...counts].map(([value, count]) => ({
value,
label: value,
count
}));
options.sort((a, b) => a.label.localeCompare(b.label));
if (emptyCount > 0) {
options.push({ value: EMPTY_SELECT_VALUE, label: emptyLabel, count: emptyCount });
}
return options;
}
/**
* Cell text for a select value: the same distinct items the Funnel lists, joined with `, `;
* undefined when empty.
*/
export function formatSelectValue(value) {
if (Array.isArray(value)) {
const items = selectCellValues(value);
return items[0] === EMPTY_SELECT_VALUE ? undefined : items.join(', ');
}
return isEmpty(value) ? undefined : value;
}