1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2024-12-01 13:20:54 +00:00

Grid: correct indentation and column widths

This commit is contained in:
Romein van Buren 2023-01-17 18:00:57 +01:00
parent 69e7502222
commit f51abb45cd
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
2 changed files with 171 additions and 159 deletions

View File

@ -0,0 +1,168 @@
<script>
import { contextMenu } from '../stores';
import { createEventDispatcher } from 'svelte';
import Icon from './icon.svelte';
export let items = [];
export let columns = [];
export let key = '';
export let activeKey = '';
export let activeChildKey = '';
export let level = 0;
const dispatch = createEventDispatcher();
let childrenOpen = {};
$: _items = objectToArray(items).map(item => {
item.children = objectToArray(item.children);
return item;
});
function objectToArray(obj) {
if (Array.isArray(obj)) {
return obj;
}
else if (typeof obj === 'object') {
return Object.entries(obj).map(([ k, item ]) => ({ ...item, [key]: k }));
}
else {
return obj;
}
}
function select(itemKey) {
activeKey = itemKey;
activeChildKey = '';
dispatch('select', itemKey);
}
function closeAll() {
childrenOpen = {};
dispatch('closeAll');
}
function selectChild(itemKey, childKey) {
select(itemKey);
activeChildKey = childKey;
dispatch('selectChild', childKey);
}
function toggleChildren(itemKey, shift) {
childrenOpen[itemKey] = !childrenOpen[itemKey];
if (shift) {
closeAll();
}
}
function doubleClick(itemKey) {
toggleChildren(itemKey, false);
dispatch('trigger', itemKey);
}
function showContextMenu(evt, item) {
select(item[key]);
contextMenu.show(evt, item.menu);
}
function formatValue(value) {
if (Array.isArray(value)) {
return '[...]';
}
if (typeof value === 'object') {
return '{...}';
}
if (value === undefined || value === null) {
return '';
}
if (typeof value === 'number' || typeof value === 'boolean') {
return String(value);
}
if (String(value).length <= 1000) {
return value;
}
return String(value).slice(0, 1000) + '…';
}
</script>
{#each _items as item (item[key])}
<tr
on:click={() => select(item[key])}
on:dblclick={() => doubleClick(item[key])}
on:contextmenu|preventDefault={evt => showContextMenu(evt, item)}
class:selected={activeKey === item[key] && !activeChildKey}
>
<td class="has-toggle">
{#if item.children?.length}
<button
class="toggle"
on:click={evt => toggleChildren(item[key], evt.shiftKey)}
style:transform="translateX({level * 10}px)"
>
<Icon name={childrenOpen[item[key]] ? 'chev-d' : 'chev-r'} />
</button>
{/if}
</td>
{#each columns as column, columnIndex}
{@const value = item[column.key]}
<td class:right={column.right} title={value}>
<div class="value" style:margin-left="{level * 10}px">
{formatValue(value)}
</div>
</td>
{/each}
</tr>
{#if item.children && childrenOpen[item[key]]}
<svelte:self
{columns}
{key}
bind:activeKey={activeChildKey}
showHeaders={false}
items={item.children}
level={level + 1}
on:select={e => selectChild(item[key], e.detail)}
on:closeAll={closeAll}
/>
{/if}
{/each}
<style>
td {
padding: 0.3rem;
text-overflow: ellipsis;
cursor: pointer;
}
td.has-toggle {
width: calc(20px + 0.3rem);
}
td .value {
height: 2.1ex;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 25em;
}
tbody tr.selected td {
background-color: #00008b;
color: #fff;
}
button.toggle {
color: inherit;
padding: 0;
margin: 0;
vertical-align: top;
}
button.toggle :global(svg) {
width: 15px;
height: 15px;
vertical-align: top;
}
.right {
text-align: right;
}
</style>

View File

@ -1,6 +1,5 @@
<script> <script>
import { contextMenu } from '../stores'; import GridItems from './grid-items.svelte';
import { createEventDispatcher } from 'svelte';
import Icon from './icon.svelte'; import Icon from './icon.svelte';
export let columns = []; export let columns = [];
@ -11,79 +10,6 @@
export let activeChildKey = ''; export let activeChildKey = '';
export let showHeaders = true; export let showHeaders = true;
export let level = 0; export let level = 0;
const dispatch = createEventDispatcher();
let childrenOpen = {};
$: _items = objectToArray(items).map(item => {
item.children = objectToArray(item.children);
return item;
});
function objectToArray(obj) {
if (Array.isArray(obj)) {
return obj;
}
else if (typeof obj === 'object') {
return Object.entries(obj).map(([ k, item ]) => ({ ...item, [key]: k }));
}
else {
return obj;
}
}
function select(itemKey) {
activeKey = itemKey;
activeChildKey = '';
dispatch('select', itemKey);
}
function selectChild(itemKey, childKey) {
select(itemKey);
activeChildKey = childKey;
dispatch('selectChild', childKey);
}
function toggleChildren(itemKey, shift) {
childrenOpen[itemKey] = !childrenOpen[itemKey];
if (shift) {
closeAll();
}
}
function doubleClick(itemKey) {
toggleChildren(itemKey, false);
dispatch('trigger', itemKey);
}
function showContextMenu(evt, item) {
select(item[key]);
contextMenu.show(evt, item.menu);
}
function formatValue(value) {
if (Array.isArray(value)) {
return '[...]';
}
if (typeof value === 'object') {
return '{...}';
}
if (value === undefined || value === null) {
return '';
}
if (typeof value === 'number' || typeof value === 'boolean') {
return String(value);
}
if (String(value).length <= 1000) {
return value;
}
return String(value).slice(0, 1000) + '…';
}
function closeAll() {
childrenOpen = {};
dispatch('closeAll');
}
</script> </script>
<div class:grid={level === 0} class:subgrid={level > 0}> <div class:grid={level === 0} class:subgrid={level > 0}>
@ -99,13 +25,6 @@
{/if} {/if}
<table> <table>
<colgroup>
<col style:width="calc(15px + 0.6rem)" />
{#each columns as column}
<col />
{/each}
</colgroup>
{#if showHeaders && columns.some(col => col.title)} {#if showHeaders && columns.some(col => col.title)}
<thead> <thead>
<tr> <tr>
@ -118,47 +37,7 @@
{/if} {/if}
<tbody> <tbody>
{#each _items as item (item[key])} <GridItems {items} {columns} {key} bind:activeKey bind:activeChildKey on:select on:selectChild on:trigger />
<tr
on:click={() => select(item[key])}
on:dblclick={() => doubleClick(item[key])}
on:contextmenu|preventDefault={evt => showContextMenu(evt, item)}
class:selected={activeKey === item[key] && !activeChildKey}
>
<td class="has-toggle">
{#if item.children?.length}
<button class="toggle" on:click={evt => toggleChildren(item[key], evt.shiftKey)}>
<Icon name={childrenOpen[item[key]] ? 'chev-d' : 'chev-r'} />
</button>
{/if}
</td>
{#each columns as column}
{@const value = item[column.key]}
<td class:right={column.right} title={value}>
<div class="value">{formatValue(value)}</div>
</td>
{/each}
</tr>
{#if item.children && childrenOpen[item[key]]}
<tr>
<td></td>
<td colspan={columns.length + 1} class="subgrid-parent">
<svelte:self
{columns}
{key}
bind:activeKey={activeChildKey}
showHeaders={false}
items={item.children}
level={level + 1}
on:select={e => selectChild(item[key], e.detail)}
on:closeAll={closeAll}
/>
</td>
</tr>
{/if}
{/each}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -201,43 +80,8 @@
cursor: pointer; cursor: pointer;
} }
td, th { th {
padding: 0.3rem; padding: 0.3rem;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
td.has-toggle {
width: calc(20px + 0.3rem);
}
td.subgrid-parent {
padding: 0;
}
td .value {
height: 2.1ex;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 25em;
}
tbody tr.selected td {
background-color: #00008b;
color: #fff;
}
button.toggle {
color: inherit;
padding: 0;
margin: 0;
display: contents;
}
button.toggle :global(svg) {
width: 15px;
height: 15px;
vertical-align: top;
}
.right {
text-align: right;
}
</style> </style>