Skip to content

Commit

Permalink
GH-105 - Multi-sheet support (#106)
Browse files Browse the repository at this point in the history
* GH-105 Multi-sheet support

* Can save multiple sheets in a single JSON file.
* Can rename sheets
* Fully compatible with Helix
* Single sheets fallback to default behavior (to maintain consistency with Helix & existing projects)
* Can delete sheets
* Reads existing multi-sheets

Resolves: #150

* Improve tests
  • Loading branch information
auniverseaway committed Apr 18, 2024
1 parent 3a0c21b commit 936c59f
Show file tree
Hide file tree
Showing 20 changed files with 493 additions and 149 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ module.exports = {
overrides: [
{
files: ['test/**/*.js'],
rules: { 'no-console': 'off' },
rules: {
'no-console': 'off',
'import/no-unresolved': 'off',
},
},
],
plugins: [
Expand Down
1 change: 1 addition & 0 deletions blocks/edit/da-title/da-title.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default class DaTitle extends LitElement {
const { hash } = window.location;
const pathname = hash.replace('#', '');
// Only save to DA if it is a sheet or config

if (this.details.view === 'sheet') {
const dasSave = await saveToDa(pathname, this.sheet);
if (!dasSave.ok) return;
Expand Down
7 changes: 7 additions & 0 deletions blocks/edit/img/Smock_Cancel_18_N.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions blocks/edit/img/Smock_Checkmark_18_N.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions blocks/edit/img/Smock_Delete_18_N.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions blocks/edit/img/Smock_Edit_18_N.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 26 additions & 2 deletions blocks/edit/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ async function saveHtml(fullPath) {
return daFetch(fullPath, opts);
}

async function saveJson(fullPath, sheet, dataType = 'blob') {
function formatSheetData(sheet) {
const jData = sheet.getData();

const data = jData.reduce((acc, row, idx) => {
if (idx > 0) {
const rowObj = {};
Expand All @@ -137,8 +138,31 @@ async function saveJson(fullPath, sheet, dataType = 'blob') {
emptyRow = false;
}
}
return data;
}

const json = { total: data.length, offset: 0, limit: data.length, data, ':type': 'sheet' };
async function saveJson(fullPath, sheets, dataType = 'blob') {
let json;
const formatted = sheets.reduce((acc, sheet) => {
const data = formatSheetData(sheet);
acc[sheet.name] = {
total: data.length,
limit: data.length,
offset: 0,
data,
};
return acc;
}, {});

if (sheets.length > 1) {
formatted[':names'] = sheets.map((sheet) => sheet.name);
formatted[':version'] = 3;
formatted[':type'] = 'multi-sheet';
json = formatted;
} else {
json = formatted[sheets[0].name];
json[':type'] = 'sheet';
}

const formData = new FormData();

Expand Down
89 changes: 89 additions & 0 deletions blocks/sheet/da-sheet-tabs.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
:host {
display: flex;
gap: var(--spacing-200);
margin-bottom: var(--spacing-200);
}

svg {
display: none;
}

ul {
margin: 0;
padding: 0;
list-style: none;
display: flex;
gap: var(--spacing-200);
}

li {
background: rgb(220 220 220);
border-radius: 6px;
line-height: 32px;
padding: 0 0 0 12px;
overflow: hidden;
}

input {
border: none;
padding: 0 6px;
background: rgb(255 255 255 / 40%);
}

button,
input[type="text"] {
font-family: var(--body-font-family);
}

li.active {
background: rgb(180 180 180);
}

li button {
background: none;
border: none;
padding: 7px 10px;
font-weight: 700;
}

li button[value="select"] {
font-weight: 400;
}

form {
display: flex;
gap: 4px;
}

button:hover {
color: #FFF;
background-color: var(--spectrum-blue-800);
}

.icon {
color: rgb(0 0 0 / 40%);
display: block;
width: 18px;
height: 18px;
}

button:hover .icon {
color: #FFF;
}

.action-container {
display: flex;
}

.add-sheet {
font-weight: 700;
background: none;
border: 2px solid var(--s2-gray-800);
border-radius: 6px;
padding: 0 12px;
}

.add-sheet:hover {
background-color: var(--spectrum-blue-800);
border: 2px solid var(--spectrum-blue-800);
}
147 changes: 147 additions & 0 deletions blocks/sheet/da-sheet-tabs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { LitElement, html, nothing } from '../../deps/lit/lit-core.min.js';
import { getNx } from '../../scripts/utils.js';

const { default: getStyle } = await import(`${getNx()}/utils/styles.js`);
const { default: getSvg } = await import(`${getNx()}/utils/svg.js`);

const style = await getStyle(import.meta.url);

const SHEET_TEMPLATE = { minDimensions: [20, 20], sheetName: 'data' };
const ICONS = [
'/blocks/edit/img/Smock_Delete_18_N.svg',
'/blocks/edit/img/Smock_Edit_18_N.svg',
'/blocks/edit/img/Smock_Cancel_18_N.svg',
'/blocks/edit/img/Smock_Checkmark_18_N.svg',
];

class DaSheetTabs extends LitElement {
static properties = {
_names: { attribute: false },
_active: { attribute: false },
_edit: { attribute: false },
};

connectedCallback() {
super.connectedCallback();
this.shadowRoot.adoptedStyleSheets = [style];
getSvg({ parent: this.shadowRoot, paths: ICONS });
this._names = this.getNames();
this.showSheet(0);
}

showSheet(idx) {
this._active = idx;
this.sheetContents.forEach((sheet) => { sheet.style.display = 'none'; });
this.sheetContents[idx].style.display = 'block';
}

getNames() {
return this.jexcel.map((sheet) => sheet.name);
}

get tabContainer() {
return this.parentElement.querySelector('.jexcel_tabs');
}

get jexcel() {
return this.tabContainer.jexcel;
}

get hiddenTabs() {
const parent = this.tabContainer.querySelector(':scope > div:first-child');
return parent.querySelectorAll('.jexcel_tab_link');
}

get sheetContents() {
const parent = this.tabContainer.querySelector(':scope > div:last-child');
return parent.querySelectorAll('.jexcel_container');
}

handleAdd() {
const sheets = [{
...SHEET_TEMPLATE,
sheetName: `data-${this.jexcel.length + 1}`,
}];
// Add the new tab
window.jspreadsheet.tabs(this.tabContainer, sheets);
// Set the sheet name for later use
this.jexcel.slice(-1)[0].name = sheets[0].sheetName;
// Refresh the tab names
this._names = this.getNames();
// Only set active as jspreadsheet will set the visibility of the sheet
this._active = this.jexcel.length - 1;
// Set the tab to be in edit mode
this._edit = this.jexcel.length - 1;
}

handleEdit(e, idx) {
e.preventDefault();
if (e.submitter.value === 'select') {
this.showSheet(idx);
return;
}
if (e.submitter.value === 'edit') {
this._edit = idx;
return;
}
if (e.submitter.value === 'cancel') {
this._edit = null;
return;
}
if (e.submitter.value === 'remove') {
this._names.splice(idx, 1);
this.jexcel.splice(idx, 1);
this.hiddenTabs[idx].remove();
this.sheetContents[idx].remove();
this._edit = null;
this.showSheet(0);
return;
}
if (e.submitter.value === 'confirm') {
const entries = Object.fromEntries(new FormData(e.target));
this._names[idx] = entries.name;
this.jexcel[idx].name = entries.name;
this.hiddenTabs[idx].innerHTML = entries.name;
this._edit = null;
}
}

render() {
if (!this._names) return nothing;

return html`
<ul>
${this._names.map((name, idx) => html`
<li class="${idx === this._active ? 'active' : ''}" @click=${() => this.showSheet(idx)}>
<form @submit=${(e) => this.handleEdit(e, idx)}>
${idx === this._edit ? html`
<input type="text" name="name" value="${name}" />
` : html`
<button value="select"><span>${name}</span></button>
`}
${idx === this._edit ? html`
<div class="action-container">
<button aria-label="Confirm" value="confirm">
<svg class="icon"><use href="#spectrum-Checkmark"/></svg>
</button>
<button aria-label="Cancel" value="cancel">
<svg class="icon"><use href="#spectrum-Cancel"/></svg>
</button>
<button aria-label="Remove" value="remove">
<svg class="icon"><use href="#spectrum-Delete"/></svg>
</button>
</div>
` : html`
<button aria-label="Edit" value="edit">
<svg class="icon"><use href="#spectrum-Edit"/></svg>
</button>
`}
</form>
</li>`)}
</ul>
<button class="add-sheet" @click=${this.handleAdd}>Add sheet</button>
`;
}
}

customElements.define('da-sheet-tabs', DaSheetTabs);
39 changes: 0 additions & 39 deletions blocks/sheet/da-toolbar.css

This file was deleted.

Loading

0 comments on commit 936c59f

Please sign in to comment.