Add support for multiselect type
This commit is contained in:
parent
654c348e4e
commit
e26163e274
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import OptionsCell from "./OptionsCell.svelte"
|
||||
</script>
|
||||
|
||||
<OptionsCell {...$$props} multi />
|
|
@ -5,12 +5,14 @@
|
|||
export let schema
|
||||
export let selected = false
|
||||
export let onChange
|
||||
export let multi = false
|
||||
|
||||
const options = schema?.constraints?.inclusion || []
|
||||
|
||||
let open = false
|
||||
|
||||
$: color = getColor(value)
|
||||
$: values = Array.isArray(value) ? value : [value].filter(x => x != null)
|
||||
$: unselectedOptions = options.filter(x => !values.includes(x))
|
||||
$: {
|
||||
// Close when deselected
|
||||
if (!selected) {
|
||||
|
@ -26,44 +28,59 @@
|
|||
return `hsla(${((index + 1) * 222) % 360}, 90%, 75%, 0.3)`
|
||||
}
|
||||
|
||||
const toggle = () => {
|
||||
open = !open
|
||||
const toggleOption = option => {
|
||||
if (!multi) {
|
||||
onChange(option)
|
||||
} else {
|
||||
if (values.includes(option)) {
|
||||
onChange(values.filter(x => x !== option))
|
||||
} else {
|
||||
onChange([...values, option])
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="container"
|
||||
class:multi
|
||||
class:selected
|
||||
class:open
|
||||
on:click={selected ? toggle : null}
|
||||
on:click={selected ? () => (open = true) : null}
|
||||
>
|
||||
{#if color}
|
||||
<div class="badge text" style="--color: {color}">
|
||||
{value}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text">
|
||||
{value || ""}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="values">
|
||||
{#each values as val (val)}
|
||||
{@const color = getColor(val)}
|
||||
{#if color}
|
||||
<div class="badge text" style="--color: {color}">
|
||||
{val}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text">
|
||||
{val || ""}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{#if selected}
|
||||
<Icon name="ChevronDown" />
|
||||
{/if}
|
||||
{#if open}
|
||||
<div class="options">
|
||||
{#if value}
|
||||
<div class="option">
|
||||
{#each values as val (val)}
|
||||
{@const color = getColor(val)}
|
||||
<div class="option" on:click={() => toggleOption(val)}>
|
||||
<div class="badge text" style="--color: {color}">
|
||||
{value}
|
||||
{val}
|
||||
</div>
|
||||
<Icon
|
||||
name="Checkmark"
|
||||
color="var(--spectrum-global-color-blue-400)"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#each options.filter(x => x !== value) as option}
|
||||
<div class="option" on:click={() => onChange(option)}>
|
||||
{/each}
|
||||
{#each unselectedOptions as option (option)}
|
||||
<div class="option" on:click={() => toggleOption(option)}>
|
||||
<div class="badge text" style="--color: {getColor(option)}">
|
||||
{option}
|
||||
</div>
|
||||
|
@ -87,11 +104,24 @@
|
|||
.container.selected:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.values {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
gap: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.multi .text {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.badge {
|
||||
padding: 2px 8px;
|
||||
background: var(--color);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import TextCell from "./TextCell.svelte"
|
||||
import OptionsCell from "./OptionsCell.svelte"
|
||||
import DateCell from "./DateCell.svelte"
|
||||
import MultiSelectCell from "./MultiSelectCell.svelte"
|
||||
|
||||
export let table
|
||||
export let filter
|
||||
|
@ -15,7 +16,7 @@
|
|||
getContext("sdk")
|
||||
const component = getContext("component")
|
||||
const limit = 100
|
||||
const defaultWidth = 200
|
||||
const defaultWidth = 160
|
||||
const minWidth = 100
|
||||
|
||||
let widths
|
||||
|
@ -36,7 +37,6 @@
|
|||
})
|
||||
$: fields = Object.keys($fetch.schema || {})
|
||||
$: initWidths(fields)
|
||||
$: sliderPositions = getSliderPositions(widths)
|
||||
$: gridStyles = getGridStyles(widths)
|
||||
$: schema = $fetch.schema
|
||||
$: rowCount = $fetch.rows?.length || 0
|
||||
|
@ -65,14 +65,7 @@
|
|||
if (!widths?.length) {
|
||||
return "--grid: 1fr;"
|
||||
}
|
||||
return `--grid: 50px ${widths.map(x => `${x}px`).join(" ")} 180px;`
|
||||
}
|
||||
|
||||
const getSliderPositions = widths => {
|
||||
let offset = 50
|
||||
return widths.map(width => {
|
||||
return (offset += width)
|
||||
})
|
||||
return `--grid: 40px ${widths.map(x => `${x}px`).join(" ")} 180px;`
|
||||
}
|
||||
|
||||
const handleScroll = e => {
|
||||
|
@ -88,6 +81,8 @@
|
|||
return OptionsCell
|
||||
} else if (type === "datetime") {
|
||||
return DateCell
|
||||
} else if (type === "array") {
|
||||
return MultiSelectCell
|
||||
}
|
||||
return TextCell
|
||||
}
|
||||
|
@ -262,20 +257,12 @@
|
|||
<span>
|
||||
{field}
|
||||
</span>
|
||||
<div class="slider" on:mousedown={e => startResizing(fieldIdx, e)} />
|
||||
</div>
|
||||
{/each}
|
||||
<!-- Horizontal spacer -->
|
||||
<div />
|
||||
|
||||
<!-- Sliders for resizing columns -->
|
||||
{#each sliderPositions as left, idx}
|
||||
<div
|
||||
class="slider"
|
||||
on:mousedown={e => startResizing(idx, e)}
|
||||
style="--left: {left}px"
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<!-- All real rows -->
|
||||
{#each rows as row, rowIdx (row._id)}
|
||||
{@const rowSelected = !!selectedRows[row._id]}
|
||||
|
@ -368,7 +355,7 @@
|
|||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
overflow: auto;
|
||||
max-height: 800px;
|
||||
max-height: 600px;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
}
|
||||
|
@ -444,7 +431,7 @@
|
|||
}
|
||||
.cell.sticky {
|
||||
position: sticky;
|
||||
left: 50px;
|
||||
left: 40px;
|
||||
z-index: 2;
|
||||
}
|
||||
.cell.sticky.selected {
|
||||
|
@ -479,25 +466,28 @@
|
|||
/* Column resizing */
|
||||
.slider {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
left: var(--left);
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 16px;
|
||||
height: 32px;
|
||||
transform: translateX(-50%);
|
||||
height: 100%;
|
||||
}
|
||||
.slider:hover:after {
|
||||
.slider:after {
|
||||
opacity: 0;
|
||||
content: " ";
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
left: 50%;
|
||||
width: 4px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
background: var(--spectrum-global-color-blue-400);
|
||||
transform: translateX(-50%);
|
||||
background: var(--spectrum-global-color-gray-600);
|
||||
transition: opacity 130ms ease-out;
|
||||
}
|
||||
.slider:hover {
|
||||
cursor: col-resize;
|
||||
}
|
||||
.slider:hover:after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sticky.shadow:after {
|
||||
content: " ";
|
||||
|
|
Loading…
Reference in New Issue