[Feature] Add language #19

Merged
chris-kruining merged 25 commits from feature/add-language into main 2024-12-19 15:22:42 +00:00
2 changed files with 109 additions and 34 deletions
Showing only changes of commit f21fa5e224 - Show all commits

View file

@ -1,26 +1,29 @@
.table {
--shadow: hsla(0 0% 0% / .1) 0 .5em 2em;
position: relative;
display: grid;
grid-template-columns: repeat(var(--columns), minmax(0, auto));
display: block grid;
grid-template-columns: repeat(var(--columns), minmax(max-content, auto));
align-content: start;
padding-inline: 1px;
margin-inline: -1px;
overflow: auto;
background-color: inherit;
isolation: isolate;
block-size: 100%;
&.selectable {
grid-template-columns: 2em repeat(calc(var(--columns) - 1), minmax(0, auto));
}
& input[type="checkbox"] {
margin: .1em;
}
& .cell {
display: grid;
display: block grid;
padding: var(--padding-m);
border: 1px solid transparent;
border-radius: var(--radii-m);
background: inherit;
white-space: nowrap;
&:has(textarea:focus) {
border-color: var(--info);
@ -29,11 +32,32 @@
& > span {
align-self: center;
}
&:first-of-type {
position: sticky;
inset-inline-start: calc(var(--padding-m) * var(--depth));
padding-inline-start: calc(var(--padding-m) * (1 + var(--depth)));
z-index: 1;
&::after {
content: '';
position: absolute;
inset-inline-start: 100%;
inset-block-start: -2px;
display: block;
inline-size: 2em;
block-size: calc(3px + 100%);
background: linear-gradient(90deg, hsla(0 0% 0% / .05), transparent);
animation: column-scroll-shadow linear both;
animation-timeline: scroll(inline);
animation-range: 0 2em;
}
}
}
& :is(.header, .main, .footer) {
grid-column: span calc(2 + var(--columns));
display: grid;
display: block grid;
grid-template-columns: subgrid;
}
@ -41,35 +65,49 @@
position: sticky;
inset-block-start: 0;
border-block-end: 1px solid var(--surface-300);
background-color: inherit;
z-index: 2;
animation: header-scroll-shadow linear both;
animation-timeline: scroll();
animation-range: 0 2em;
& > aside {
position: sticky;
inset-inline-start: 0;
background: inherit;
padding: var(--padding-m);
z-index: 1;
}
}
& .main {
overflow: clip auto;
background-color: inherit;
}
& .row {
--bg: var(--text);
--alpha: 0;
grid-column: span calc(2 + var(--columns));
display: grid;
display: block grid;
grid-template-columns: subgrid;
border: 1px solid transparent;
background-color: color(from var(--bg) srgb r g b / var(--alpha));
background-color: inherit;
background-image: linear-gradient(0deg, oklch(from var(--info) l c h / var(--alpha)), oklch(from var(--info) l c h / var(--alpha)));
&:has(> .cell > :checked) {
--bg: var(--info);
&:has(> aside > :checked) {
--alpha: .1;
border-color: var(--bg);
border-color: var(--info);
& span {
font-variation-settings: 'GRAD' 1000;
}
& + :has(> .cell> :checked) {
/* Remove the top border when directly preceded by a selected row */
& + :has(> aside > :checked) {
border-block-start-color: transparent;
}
&:has(+ .row > .cell > :checked) {
/* Remove the top border when directly succeeded by a selected row */
&:has(+ .row > aside > :checked) {
border-block-end-color: transparent;
}
}
@ -77,15 +115,25 @@
&:hover {
--alpha: .2 !important;
}
& > aside {
position: sticky;
inset-inline-start: 0;
background: inherit;
padding: var(--padding-m);
z-index: 1;
}
}
& details {
display: contents;
background-color: inherit;
&::details-content {
grid-column: span calc(2 + var(--columns));
display: grid;
display: block grid;
grid-template-columns: subgrid;
background-color: inherit;
}
&:not([open])::details-content {
@ -93,15 +141,30 @@
}
& > summary {
position: sticky;
inset-inline-start: 0;
grid-column: 1;
padding: .5em;
padding-inline-start: calc(var(--depth) * 1em + .5em);
}
& > .row > .cell {
padding-inline-start: calc(var(--depth) * 1em + var(--padding-m));
}
}
&.selectable {
grid-template-columns: 2em repeat(calc(var(--columns) - 1), minmax(max-content, auto));
& .cell:first-of-type {
inset-inline-start: calc(2em + var(--padding-m) * var(--depth));
}
& details > summary {
inset-inline-start: 2em;
grid-column: 2;
}
}
}
@property --depth {
@ -109,3 +172,23 @@
inherits: false;
initial-value: 0;
}
@keyframes header-scroll-shadow {
from {
box-shadow: none;
}
to {
box-shadow: var(--shadow);
}
}
@keyframes column-scroll-shadow {
from {
background: linear-gradient(90deg, transparent, transparent);
}
to {
background: linear-gradient(90deg, hsla(0 0% 0% / .05), transparent);
}
}

View file

@ -102,14 +102,14 @@ function Head<T extends Record<string, any>>(props: {}) {
return <header class={css.header}>
<Show when={table.selectionMode() !== SelectionMode.None}>
<div class={css.cell}>
<aside>
<input
type="checkbox"
checked={context.selection().length > 0 && context.selection().length === context.length()}
indeterminate={context.selection().length !== 0 && context.selection().length !== context.length()}
on:input={(e: InputEvent) => e.target.checked ? context.selectAll() : context.clear()}
/>
</div>
</aside>
</Show>
<For each={table.columns()}>{
@ -137,15 +137,15 @@ function Row<T extends Record<string, any>>(props: { key: string, value: T, dept
const values = createMemo(() => Object.entries(props.value));
const isSelected = context.isSelected(props.key);
return <div class={css.row} use:selectable={{ value: props.value, key: props.key }}>
return <div class={css.row} style={{ '--depth': props.depth }} use:selectable={{ value: props.value, key: props.key }}>
<Show when={table.selectionMode() !== SelectionMode.None}>
<div class={css.cell}>
<aside>
<input type="checkbox" checked={isSelected()} on:input={() => context.select([props.key])} on:pointerdown={e => e.stopPropagation()} />
</div>
</aside>
</Show>
<For each={values()}>{
([k, v]) => <div style={k === props.groupedBy ? { '--depth': props.depth } : {}} class={css.cell}>{table.cellRenderers()[k]?.({ value: v }) ?? v}</div>
([k, v]) => <div class={css.cell}>{table.cellRenderers()[k]?.({ value: v }) ?? v}</div>
}</For>
</div>;
};
@ -153,16 +153,8 @@ function Row<T extends Record<string, any>>(props: { key: string, value: T, dept
function Group<T extends Record<string, any>>(props: { key: string, groupedBy: keyof T, nodes: Node<T>[], depth: number }) {
const table = useTable();
const gridColumn = createMemo(() => {
const groupedBy = props.groupedBy;
const columns = table.columns();
const selectable = table.selectionMode() !== SelectionMode.None;
return columns.findIndex(({ id }) => id === groupedBy) + (selectable ? 2 : 1);
});
return <details open>
<summary style={{ '--depth': props.depth, 'grid-column-start': gridColumn() }}>{props.key}</summary>
<summary style={{ '--depth': props.depth }}>{props.key}</summary>
<For each={props.nodes}>{
node => <Node node={node} depth={props.depth + 1} groupedBy={props.groupedBy} />