group editor code display changed
This commit is contained in:
@@ -595,24 +595,24 @@
|
||||
.current-location-button img { width: 24px; height: 24px; vertical-align: middle; }
|
||||
|
||||
/* Shared drafts and vertically scrolling Wichtel-code wheels. */
|
||||
.group-share-pictures { display: flex; align-items: center; justify-content: center; gap: 1rem; margin: 1rem 0; }
|
||||
.group-share-pictures img { width: min(34vw, 9rem); height: 10rem; object-fit: contain; }
|
||||
.group-share-button img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
|
||||
.group-dialog { width: min(35rem, calc(100% - 2rem)); max-height: 90dvh; box-sizing: border-box; overflow-y: auto; padding: 1.25rem; border: 1px solid #c8d3ca; border-radius: 1.25rem; background: #f8faf5; color: #1f3022; }
|
||||
.group-code-dialog { width: min(56rem, calc(100vw - 1rem)); max-height: calc(100dvh - 1rem); padding: clamp(.5rem, 2vw, 1.5rem); }
|
||||
.group-dialog::backdrop { background: rgba(20, 40, 25, .5); }
|
||||
.group-dialog-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
||||
.group-dialog-header h2 { margin: 0; font-size: 1.2rem; }
|
||||
.group-dialog button { min-width: 44px; min-height: 44px; border: 1px solid #c8d3ca; border-radius: .85rem; background: white; color: #24462b; touch-action: manipulation; }
|
||||
.group-dialog button:focus-visible, .group-wheel-viewport:focus-visible { outline: 3px solid #1e6fba; outline-offset: 2px; }
|
||||
.group-wheels { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .3rem; padding: 0; border: 0; margin: 1rem 0; }
|
||||
.group-wheels { display: grid; grid-template-columns: repeat(5, minmax(44px, 1fr)); gap: .3rem; padding: 0; border: 0; margin: 1rem 0; }
|
||||
.group-code-fieldset { border: 0; padding: 0; min-width: 0; }
|
||||
.group-code-fieldset legend { margin-bottom: .5rem; font-weight: 600; }
|
||||
.group-wheel { display: grid; gap: .3rem; min-width: 0; }
|
||||
.group-wheel-viewport { height: 156px; padding: 52px 0; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain; scroll-snap-type: y mandatory; scrollbar-width: none; border-radius: .85rem; background: linear-gradient(to bottom, #edf2eb 0 33.33%, #dcebdc 33.33% 66.66%, #edf2eb 66.66%); }
|
||||
.group-wheel-viewport { height: 288px; padding: 96px 0; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain; scroll-snap-type: y mandatory; scrollbar-width: none; border-radius: .85rem; background: linear-gradient(to bottom, #edf2eb 0 33.33%, #dcebdc 33.33% 66.66%, #edf2eb 66.66%); }
|
||||
.group-wheel-viewport::-webkit-scrollbar { display: none; }
|
||||
.group-wheel-option { height: 52px; display: grid; place-items: center; scroll-snap-align: center; cursor: pointer; }
|
||||
.group-wheel-option img { width: 100%; max-width: 48px; height: 48px; object-fit: contain; pointer-events: none; }
|
||||
.group-code { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .3rem; padding: .6rem; border: 1px solid #c8d3ca; border-radius: 1rem; background: #e7f1e9; }
|
||||
.group-code img { width: 100%; height: 64px; object-fit: contain; }
|
||||
.group-wheel-option { height: 96px; display: grid; place-items: center; scroll-snap-align: center; cursor: pointer; }
|
||||
.group-wheel-option img { width: 100%; max-width: 88px; height: 88px; object-fit: contain; pointer-events: none; }
|
||||
.group-code { display: grid; grid-template-columns: repeat(5, minmax(44px, 1fr)); gap: .3rem; padding: .6rem; border: 1px solid #c8d3ca; border-radius: 1rem; background: #e7f1e9; }
|
||||
.group-code img { width: 100%; height: clamp(64px, 14vw, 128px); object-fit: contain; }
|
||||
.group-actions { max-width: min(18rem, 52vw); flex-wrap: wrap; justify-content: flex-end; }
|
||||
.group-actions .editor-secondary-button { font-size: .85rem; }
|
||||
.group-status { display: block; min-height: 1.5rem; color: #9b2c2c; }
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#315d3a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M5 23 12 6Q14 2 16 6L23 23Z" fill="#e7f1e9"/>
|
||||
<path d="M7 24Q5 34 14 43 23 34 21 24" fill="#fff"/>
|
||||
<circle cx="14" cy="25" r="3" fill="#f2ca6b"/>
|
||||
<path d="M25 25 32 10Q34 6 36 10L43 25Z" fill="#f8f5eb"/>
|
||||
<path d="M27 26Q25 35 34 43 43 35 41 26" fill="#fff"/>
|
||||
<circle cx="34" cy="27" r="3" fill="#f2ca6b"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 507 B |
+13
-17
@@ -579,33 +579,29 @@
|
||||
<script src="js/user-messages.js?v=0.12.14"></script>
|
||||
<script src="js/local-routes.js?v=0.12.14"></script>
|
||||
<script src="js/route-recorder.js?v=0.12.14"></script>
|
||||
<dialog id="group-share-dialog" class="group-dialog" aria-labelledby="group-share-title">
|
||||
<dialog id="group-entry-dialog" class="group-dialog group-code-dialog" aria-labelledby="group-entry-title">
|
||||
<header class="group-dialog-header">
|
||||
<h2 id="group-share-title" data-i18n="groups.share">Gemeinsam bearbeiten</h2>
|
||||
<button id="group-share-close" type="button" data-i18n-aria-label="info.close">×</button>
|
||||
</header>
|
||||
<p id="group-share-route"></p>
|
||||
<div id="group-share-pictures" class="group-share-pictures"></div>
|
||||
<p data-i18n="groups.share-help">Mit diesen beiden Wichteln öffnest du einen gemeinsamen Entwurf. Andere können mit einem Wichtelcode mitmachen. Das Original bleibt erhalten.</p>
|
||||
<div class="editor-form-actions">
|
||||
<button id="group-share-start" type="button" data-i18n="groups.share-start">Gemeinsamen Entwurf öffnen</button>
|
||||
</div>
|
||||
</dialog>
|
||||
<dialog id="group-dialog" class="group-dialog" aria-labelledby="group-title">
|
||||
<header class="group-dialog-header">
|
||||
<h2 id="group-title" data-i18n="groups.share">Gemeinsam bearbeiten</h2>
|
||||
<button id="group-close" type="button" aria-label="Schließen" data-i18n-aria-label="info.close">×</button>
|
||||
<h2 id="group-entry-title" data-i18n="groups.join-open">Gruppencode eingeben</h2>
|
||||
<button id="group-entry-close" type="button" data-i18n-aria-label="info.close">×</button>
|
||||
</header>
|
||||
<section id="group-entry">
|
||||
<p data-i18n="groups.wheel-help">Schiebe die Wichtel nach oben oder unten. Die fünf Wichtel in der Mitte ergeben den Code. Lies von links nach rechts. Ein Wichtel darf mehrmals vorkommen.</p>
|
||||
<fieldset class="group-code-fieldset"><legend data-i18n="groups.code-label">Gruppencode</legend><div id="group-wheels" class="group-wheels"></div></fieldset>
|
||||
<button id="group-join" type="button" data-i18n="groups.join">Mitmachen</button>
|
||||
<div class="editor-form-actions"><button id="group-join" type="button" data-i18n="groups.join">Mitmachen</button></div>
|
||||
</section>
|
||||
<output id="group-entry-status" class="group-status"></output>
|
||||
</dialog>
|
||||
<dialog id="group-dialog" class="group-dialog group-code-dialog" aria-labelledby="group-title">
|
||||
<header class="group-dialog-header">
|
||||
<h2 id="group-title" data-i18n="groups.code-label">Gruppencode</h2>
|
||||
<button id="group-close" type="button" aria-label="Schließen" data-i18n-aria-label="info.close">×</button>
|
||||
</header>
|
||||
<p id="group-route"></p>
|
||||
<section id="group-invitation" hidden>
|
||||
<p data-i18n="groups.invite-help">Zeige diesen Code den anderen. Damit können sie deinen gemeinsamen Entwurf öffnen und bearbeiten. Das Original bleibt erhalten.</p>
|
||||
<div id="group-code" class="group-code" aria-label="Gruppencode" data-i18n-aria-label="groups.code-label"></div>
|
||||
<p id="group-expiry"></p>
|
||||
<button id="group-edit" type="button" data-i18n="groups.edit">Entwurf bearbeiten</button>
|
||||
<div class="editor-form-actions"><button id="group-edit" type="button" data-i18n="groups.edit">Entwurf bearbeiten</button></div>
|
||||
</section>
|
||||
<output id="group-status" class="group-status"></output>
|
||||
</dialog>
|
||||
|
||||
@@ -1652,7 +1652,6 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
if (shareInProgress) return;
|
||||
shareInProgress = true;
|
||||
try {
|
||||
if (ns.Groups.confirmShare && !await ns.Groups.confirmShare(routeName(route))) return;
|
||||
let sourceId = routeId(route);
|
||||
if (routeSource(route) === 'local') {
|
||||
if (!globalThis.confirm(ns.I18n.t('groups.upload-confirm'))) return;
|
||||
|
||||
+28
-43
@@ -6,10 +6,11 @@ globalThis.Wegwichtel ??= {};
|
||||
joinOpen: 'group-join-open', editorJoin: 'editor-group-join', list: 'editor-group-list',
|
||||
dialog: 'group-dialog', close: 'group-close', status: 'group-status', wheels: 'group-wheels',
|
||||
entry: 'group-entry', invitation: 'group-invitation', code: 'group-code', join: 'group-join',
|
||||
edit: 'group-edit', expiry: 'group-expiry'
|
||||
edit: 'group-edit', expiry: 'group-expiry',
|
||||
entryDialog: 'group-entry-dialog', entryClose: 'group-entry-close', entryStatus: 'group-entry-status'
|
||||
};
|
||||
const codeLength = 5;
|
||||
const wheelStep = 52;
|
||||
const wheelStep = 96;
|
||||
const revisions = new Map();
|
||||
let selection = [];
|
||||
let currentGroup = null;
|
||||
@@ -73,12 +74,12 @@ globalThis.Wegwichtel ??= {};
|
||||
async function guarded(action) {
|
||||
if (pending) return;
|
||||
pending = true;
|
||||
element(ids.status).textContent = '';
|
||||
const status = element(element(ids.entryDialog).open ? ids.entryStatus : ids.status);
|
||||
status.textContent = '';
|
||||
element(ids.join).disabled = true;
|
||||
try { await action(); } catch (error) {
|
||||
element(ids.status).textContent = errorMessage(error);
|
||||
if (!element(ids.dialog).open) {
|
||||
element(ids.entry).hidden = true;
|
||||
element(element(ids.entryDialog).open ? ids.entryStatus : ids.status).textContent = errorMessage(error);
|
||||
if (!element(ids.dialog).open && !element(ids.entryDialog).open) {
|
||||
element(ids.invitation).hidden = true;
|
||||
element(ids.dialog).showModal();
|
||||
}
|
||||
@@ -146,21 +147,22 @@ globalThis.Wegwichtel ??= {};
|
||||
}
|
||||
|
||||
function openEntry() {
|
||||
if (element(ids.dialog).open) element(ids.dialog).close();
|
||||
dialogGeneration += 1;
|
||||
selection = new Array(codeLength).fill(0);
|
||||
element(ids.status).textContent = '';
|
||||
element(ids.entry).hidden = false;
|
||||
element(ids.invitation).hidden = true;
|
||||
element(ids.entryStatus).textContent = '';
|
||||
const wheels = element(ids.wheels);
|
||||
wheels.replaceChildren(...selection.map((_, index) => makeWheel(index)));
|
||||
if (!element(ids.dialog).open) element(ids.dialog).showModal();
|
||||
if (!element(ids.entryDialog).open) element(ids.entryDialog).showModal();
|
||||
}
|
||||
|
||||
function showInvitation(payload) {
|
||||
if (!Array.isArray(payload.code) || payload.code.length !== codeLength) throw new Error(t('error.connection'));
|
||||
currentGroup = payload.group;
|
||||
element(ids.status).textContent = '';
|
||||
element(ids.entry).hidden = true;
|
||||
if (element(ids.entryDialog).open) element(ids.entryDialog).close();
|
||||
element(ids.invitation).hidden = false;
|
||||
element('group-route').textContent = payload.group.route.name || '';
|
||||
const motifs = payload.code.map(value => ns.GroupSymbols.get(value));
|
||||
element(ids.code).replaceChildren(...motifs.map(picture));
|
||||
const time = new Date(payload.expiresAt * 1000).toLocaleTimeString(ns.I18n.language, { hour: '2-digit', minute: '2-digit' });
|
||||
@@ -182,7 +184,8 @@ globalThis.Wegwichtel ??= {};
|
||||
finally { invitationPolling = false; }
|
||||
}
|
||||
async function edit(group) {
|
||||
element(ids.dialog).close();
|
||||
if (element(ids.dialog).open) element(ids.dialog).close();
|
||||
if (element(ids.entryDialog).open) element(ids.entryDialog).close();
|
||||
await ns.EditorUi.openGroup(group.route);
|
||||
}
|
||||
|
||||
@@ -258,34 +261,20 @@ globalThis.Wegwichtel ??= {};
|
||||
function shareButton(action) {
|
||||
const result = document.createElement('button');
|
||||
result.type = 'button';
|
||||
result.className = 'editor-icon-button';
|
||||
result.className = 'editor-icon-button group-share-button';
|
||||
result.dataset.editorAction = action;
|
||||
result.title = t('share');
|
||||
result.setAttribute('aria-label', t('share'));
|
||||
result.setAttribute('aria-haspopup', 'dialog');
|
||||
result.textContent = '👥';
|
||||
const icon = document.createElement('img');
|
||||
icon.src = '/images/icons/group-edit.svg';
|
||||
icon.alt = '';
|
||||
icon.width = 32;
|
||||
icon.height = 32;
|
||||
result.appendChild(icon);
|
||||
return result;
|
||||
}
|
||||
|
||||
let shareDecision = null;
|
||||
function confirmShare(name) {
|
||||
if (shareDecision) return Promise.resolve(false);
|
||||
const dialog = element('group-share-dialog');
|
||||
element('group-share-route').textContent = name;
|
||||
element('group-share-pictures').replaceChildren(...['🐦', '🪨'].map(value => picture(ns.GroupSymbols.get(value))));
|
||||
return new Promise(resolve => {
|
||||
shareDecision = resolve;
|
||||
dialog.showModal();
|
||||
});
|
||||
}
|
||||
|
||||
function finishShare(accepted) {
|
||||
const resolve = shareDecision;
|
||||
shareDecision = null;
|
||||
element('group-share-dialog').close();
|
||||
resolve?.(accepted);
|
||||
}
|
||||
|
||||
function headersFor(path) {
|
||||
const id = /^\/routes\/(\d+)(?:\/|$)/.exec(path)?.[1];
|
||||
return id && revisions.has(id) ? { 'If-Match': String(revisions.get(id)) } : {};
|
||||
@@ -306,31 +295,27 @@ globalThis.Wegwichtel ??= {};
|
||||
code: selection.map(index => motifs[index].unicode), creator_id: ns.LocalRoutes.creatorId()
|
||||
});
|
||||
} catch (error) {
|
||||
if (!element(ids.dialog).open || generation !== dialogGeneration) return;
|
||||
if (!element(ids.entryDialog).open || generation !== dialogGeneration) return;
|
||||
throw error;
|
||||
}
|
||||
if (!element(ids.dialog).open || generation !== dialogGeneration) return;
|
||||
if (!element(ids.entryDialog).open || generation !== dialogGeneration) return;
|
||||
await edit(payload.group);
|
||||
await refresh();
|
||||
}
|
||||
|
||||
function init() {
|
||||
globalThis.setInterval?.(() => { refreshInvitation(); }, 30_000);
|
||||
activate(element('group-share-start'), () => finishShare(true));
|
||||
activate(element('group-share-close'), () => finishShare(false));
|
||||
element('group-share-dialog').addEventListener('close', () => {
|
||||
const resolve = shareDecision; shareDecision = null; resolve?.(false);
|
||||
});
|
||||
document.addEventListener('wegwichtel:point-drafts-changed', () => { renderPending(); ns.StorageUsage?.refresh(); });
|
||||
element(ids.dialog).addEventListener('cancel', () => { dialogGeneration += 1; });
|
||||
element(ids.dialog).addEventListener('close', () => { dialogGeneration += 1; });
|
||||
element(ids.entryDialog).addEventListener('cancel', () => { dialogGeneration += 1; });
|
||||
element(ids.entryDialog).addEventListener('close', () => { dialogGeneration += 1; });
|
||||
activate(element(ids.entryClose), () => element(ids.entryDialog).close());
|
||||
for (const id of [ids.joinOpen, ids.editorJoin]) activate(element(id), openEntry);
|
||||
activate(element(ids.close), () => element(ids.dialog).close());
|
||||
activate(element(ids.join), () => guarded(joinSelectedGroup));
|
||||
activate(element(ids.edit), () => guarded(() => edit(currentGroup)));
|
||||
}
|
||||
|
||||
ns.Groups = Object.freeze({ share, shareButton, confirmShare, refresh, refreshInvitation, headersFor, observe });
|
||||
ns.Groups = Object.freeze({ share, shareButton, refresh, refreshInvitation, headersFor, observe });
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init, { once: true });
|
||||
else init();
|
||||
}(globalThis.Wegwichtel));
|
||||
|
||||
@@ -135,8 +135,6 @@ globalThis.Wegwichtel ??= {};
|
||||
},
|
||||
"groups": {
|
||||
"code-wait": "In der Gruppe wird gerade eine Station bearbeitet. Ein neuer Code erscheint, sobald die Stationen wieder frei sind. Ein abgelaufener Code erlaubt keinen neuen Beitritt.",
|
||||
"share-help": "Mit diesen beiden Wichteln öffnest du einen gemeinsamen Entwurf. Andere können mit einem Wichtelcode mitmachen. Das Original bleibt erhalten.",
|
||||
"share-start": "Gemeinsamen Entwurf öffnen",
|
||||
"title": "Gemeinsame Entwürfe",
|
||||
"upload-confirm": "Dein Entwurf mit Bildern und Ton wird dafür an die Website gesendet und als gemeinsamer Entwurf kopiert. Fortfahren?",
|
||||
"share": "Gemeinsam bearbeiten",
|
||||
|
||||
@@ -135,8 +135,6 @@ globalThis.Wegwichtel ??= {};
|
||||
},
|
||||
"groups": {
|
||||
"code-wait": "Someone in the group is editing a stop. A new code will appear once the stops are free. An expired code cannot be used to join.",
|
||||
"share-help": "These two gnomes open a shared draft. Others can join using a gnome code. The original stays unchanged.",
|
||||
"share-start": "Open a shared draft",
|
||||
"title": "Shared drafts",
|
||||
"upload-confirm": "Your draft, including pictures and recordings, will be sent to the website and copied into a shared draft. Continue?",
|
||||
"share": "Edit together",
|
||||
|
||||
Reference in New Issue
Block a user