mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 09:40:20 -05:00
Link to a swimlane or a list, the way you can link to a card.
A card has had an address since there has been a card route. A swimlane and a list had none: "the Backlog list of this board" could only be sent as "open this board and scroll down". Worse, List.absoluteUrl() answered with the URL of whichever CARD the cache returned first for that list, so the one place that did offer a list link - listMorePopup - showed a card's address, and an empty box for an empty list. Both have their own now: /b/<boardId>/<slug>/swimlane/<swimlaneId> /b/<boardId>/<slug>/list/<listId> FIVE segments against the card route's four, which is the whole reason the three can coexist: a card URL cannot match these and these cannot match a card. A guard checks all nine combinations, because losing a segment would silently turn a link to a list into a link to a card called "list". The kind is a word in the path rather than a query parameter - it is part of what the address names, and a reader can see which is which. config/router.js registers the routes from the same constants the links are built from, so the two cannot disagree about the path. Following one brings the thing into view. The route cannot do that: it runs before the board has rendered, and on a board that is already open it runs without re-creating anything, so at that moment there is nothing on screen to scroll. It names what to reveal in a Session value and the board body reveals it, WAITING for the element rather than assuming it - swimlanes and lists arrive as their subscriptions do and a large board renders in more than one pass, so a single look on the next tick finds nothing on a cold load. It gives up after a few seconds rather than spinning: a link to a list that was archived names an element that is never going to exist, and the board is still the right place to have landed. The Session value is cleared when acted on AND when given up on, so it is a one-shot instruction about the address you followed rather than a standing order that re-scrolls on every later render - and cleared BEFORE the scroll, because scrollIntoView can re-enter the autorun through the reactive read. Every other board route clears it too: without that, opening a card after a list link would scroll the board away from the card it had just opened. The element ids are the ones the templates already carry, held as a map and not derived by a rule. Deriving one name from another is how allBoardsMultiselectionSidebar happened, and this failure is silent - nothing is found and nothing is reported - so a guard checks each id against the markup that draws it. All three are copied the same way, from the FIRST row of the hamburger menu, with the link icon and the name beside it. The card carried this as an icon in its title header named only by a tooltip, which is the one place a name cannot be read without hovering; that button is gone, and so are its handler and the "Copied" tooltip only it used - a handler for markup that no longer exists is dead code that reads as a working feature. The copy row sits ABOVE every permission check in those menus. Copying an address is reading, not editing: somebody who may only read the board can still tell a colleague which list they mean, while everything else in those menus changes the board and stays guarded. It copies absoluteUrl(), because what lands in the clipboard is pasted into a chat and a relative path is only a link inside this page. copy-link-to-clipboard is added to all 147 language files, in the same position in each. 119 are really translated, each derived from that language's OWN existing copy-strings rather than invented. The 16 that keep the English placeholder are the ones whose existing copy-strings are already in the wrong language - Romanian holding Italian, Esperanto holding Spanish, Hindi holding Gujarati - where deriving from them would only spread the error; the en_* variants are English by design. docs/Design/Page/Header.md is written at last. Twenty-seven comments have been pointing at it since the header restructure began and it had never existed, so every one of them was a dead reference. Thanks to xet7 !
This commit is contained in:
1 parent
4bf3dab1c5
commit
e755b60b37
165 files changed
+1173
-33
No files matched your search
@@ -265,6 +265,43 @@ browser build to verify).
|
||||
|
||||
This release adds the following new features:
|
||||
|
||||
<details>
|
||||
<summary><a href="https://github.com/wekan/wekan/commit/HASH">A swimlane, a list and a card can each be linked, and the link lands on the thing it names</a>. Thanks to xet7.</summary>
|
||||
|
||||
A card has had an address since there has been a card route. A swimlane and a
|
||||
list had none, so "the Backlog list of this board" could only be sent as "open
|
||||
this board and scroll down" — and `List.absoluteUrl()` answered with the URL of
|
||||
whichever CARD the cache returned first for that list, so the one place that did
|
||||
offer a list link showed a card's address, and an empty box for an empty list.
|
||||
|
||||
Both have their own now — `/b/<board>/<slug>/swimlane/<id>` and
|
||||
`/b/<board>/<slug>/list/<id>`. Five segments against the card route's four,
|
||||
which is what keeps the three apart: a card URL cannot match these and these
|
||||
cannot match a card.
|
||||
|
||||
Following one brings the thing into view. The route cannot scroll — it runs
|
||||
before the board has rendered, and on a board that is already open it runs
|
||||
without re-creating anything — so it names what to reveal and the board body
|
||||
reveals it once the element exists, waiting for it rather than assuming it,
|
||||
because a large board renders in more than one pass. It gives up after a few
|
||||
seconds instead of spinning: a link to a list that was archived names an element
|
||||
that is never going to exist, and the board is still the right place to have
|
||||
landed. The revealed swimlane or list gets a brief outline, because a scroll
|
||||
that lands mid-board gives no sign of which of the things now on screen the link
|
||||
was about.
|
||||
|
||||
All three are copied the same way, from the first row of the hamburger menu,
|
||||
with the link icon and the name beside it. The card carried this as an icon in
|
||||
its title header named only by a tooltip — the one place a name cannot be read
|
||||
without hovering — and that button is gone, along with its handler and the
|
||||
"Copied" tooltip only it used.
|
||||
|
||||
The copy row sits above every permission check in those menus: copying an
|
||||
address is reading, not editing, and somebody who may only read the board can
|
||||
still tell a colleague which list they mean.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><a href="https://github.com/wekan/wekan/commit/6051a4368">All Boards keeps its four controls in the header bar, and drops the hamburger that only led to them</a>. Thanks to xet7.</summary>
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
import '/client/lib/dragscrollTouch';
|
||||
import '/client/lib/dragDropTouch';
|
||||
import { boardConverter } from '/client/lib/boardConverter';
|
||||
// Bring the swimlane or list a link named into view, once it has rendered.
|
||||
import { watchBoardItemReveals } from '/client/lib/revealBoardItem';
|
||||
import { formatDateByUserPreference } from '/imports/lib/dateUtils';
|
||||
import { toFullCalendarFirstDay } from '/client/lib/calendarFirstDay';
|
||||
import { weekNumberByFirstDay } from '/models/lib/weekStart';
|
||||
@@ -289,6 +291,11 @@ Template.boardBody.onRendered(function () {
|
||||
// Initialize user settings (zoom and mobile mode)
|
||||
Utils.initializeUserSettings();
|
||||
|
||||
// A link to a swimlane or a list has to end with that thing on screen. The
|
||||
// route can only NAME it - it runs before this has rendered - so the reveal
|
||||
// waits here for the element to exist. client/lib/revealBoardItem.js
|
||||
this.boardItemReveals = watchBoardItemReveals();
|
||||
|
||||
// Detect iPhone devices and add class for better CSS targeting
|
||||
const isIPhone = /iPhone|iPod/.test(navigator.userAgent);
|
||||
if (isIPhone) {
|
||||
@@ -672,6 +679,12 @@ Template.boardBody.onRendered(function () {
|
||||
});
|
||||
|
||||
Template.boardBody.onDestroyed(function () {
|
||||
// The reveal watcher owns a Tracker computation and a retry interval, and
|
||||
// neither stops on its own when this template goes.
|
||||
if (this.boardItemReveals) {
|
||||
this.boardItemReveals.stop();
|
||||
this.boardItemReveals = null;
|
||||
}
|
||||
if (BoardBody === this) {
|
||||
BoardBody = null;
|
||||
}
|
||||
|
||||
@@ -24,17 +24,9 @@ template(name="cardDetails")
|
||||
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
|
||||
a.card-details-menu.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
i.fa.fa-bars
|
||||
a.card-copy-button.js-copy-link(
|
||||
id="cardURL_copy"
|
||||
title="{{_ 'copy-card-link-to-clipboard'}}"
|
||||
href="{{ originRelativeUrl }}"
|
||||
)
|
||||
span.emoji-icon
|
||||
i.fa.fa-link
|
||||
if canModifyCard
|
||||
span.card-drag-handle.js-card-drag-handle(title="Drag card")
|
||||
i.fa.fa-arrows
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
else
|
||||
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
i.fa.fa-times-thin
|
||||
@@ -53,14 +45,6 @@ template(name="cardDetails")
|
||||
a.fa.fa-window-maximize.maximize-card-details.js-maximize-card-details(title="{{_ 'maximize-card'}}")
|
||||
a.card-details-menu-mobile-web.js-open-card-details-menu(title="{{_ 'cardDetailsActionsPopup-title'}}")
|
||||
i.fa.fa-bars
|
||||
a.card-copy-mobile-button.js-copy-link(
|
||||
id="cardURL_copy"
|
||||
title="{{_ 'copy-card-link-to-clipboard'}}"
|
||||
href="{{ originRelativeUrl }}"
|
||||
)
|
||||
span.emoji-icon
|
||||
i.fa.fa-link
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
if canModifyCard
|
||||
if currentBoard.allowsDueComplete
|
||||
a.card-details-complete-toggle.js-toggle-due-complete(title="{{#if getDueComplete}}{{_ 'card-mark-incomplete'}}{{else}}{{_ 'card-mark-complete'}}{{/if}}")
|
||||
@@ -786,6 +770,18 @@ template(name="editCardSortOrderForm")
|
||||
|
||||
template(name="cardDetailsActionsPopup")
|
||||
if currentUser
|
||||
//- Copy link, first, and named - the card's header carried this as an icon
|
||||
with only a tooltip, which is the one place a name cannot be read without
|
||||
hovering. It is here now, beside the swimlane's and the list's, so all
|
||||
three are copied the same way from the same kind of menu.
|
||||
docs/Design/Page/Board-Item-Links.md
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-copy-card-link
|
||||
i.fa.fa-link
|
||||
| {{_ 'copy-link-to-clipboard'}}
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
hr
|
||||
ul.pop-over-list
|
||||
li
|
||||
a.js-toggle-watch-card
|
||||
|
||||
@@ -961,15 +961,6 @@ Template.cardDetails.events({
|
||||
Utils.goBoardId(boardId);
|
||||
}
|
||||
},
|
||||
'click .js-copy-link'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const card = Template.currentData();
|
||||
const url = card.absoluteUrl();
|
||||
const promise = Utils.copyTextToClipboard(url);
|
||||
|
||||
const $tooltip = tpl.$('.card-details-header .copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'change .js-date-format-selector'(event) {
|
||||
const dateFormat = event.target.value;
|
||||
if (Meteor.userId()) {
|
||||
@@ -1595,6 +1586,19 @@ Template.cardDetailsActionsPopup.helpers({
|
||||
});
|
||||
|
||||
Template.cardDetailsActionsPopup.events({
|
||||
// Copy the card's address. This was an icon in the card's header with only a
|
||||
// tooltip to name it - the one place a name cannot be read without hovering -
|
||||
// and it is here now beside the swimlane's and the list's, so all three are
|
||||
// copied the same way from the same kind of menu.
|
||||
// docs/Design/Page/Board-Item-Links.md
|
||||
'click .js-copy-card-link'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const card = Template.currentData();
|
||||
if (!card) return;
|
||||
const url = card.absoluteUrl();
|
||||
if (!url) return;
|
||||
Utils.showCopied(Utils.copyTextToClipboard(url), tpl.$('.copied-tooltip'));
|
||||
},
|
||||
'click .js-export-card': Popup.open('exportCard'),
|
||||
'click .js-members': Popup.open('cardMembers'),
|
||||
'click .js-assignees': Popup.open('cardAssignees'),
|
||||
|
||||
@@ -128,6 +128,16 @@ template(name="editListTitleForm")
|
||||
i.fa.fa-times-thin
|
||||
|
||||
template(name="listActionPopup")
|
||||
//- Copy link, FIRST and above every permission check - see the swimlane
|
||||
popup: copying an address is reading, not editing.
|
||||
models/lib/boardItemUrl.js, docs/Design/Page/Board-Item-Links.md
|
||||
ul.pop-over-list
|
||||
li: a.js-copy-list-link
|
||||
i.fa.fa-link
|
||||
span
|
||||
| {{_ 'copy-link-to-clipboard'}}
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
hr
|
||||
unless currentUser.isReadOnly
|
||||
unless currentUser.isReadAssignedOnly
|
||||
ul.pop-over-list
|
||||
@@ -226,8 +236,10 @@ template(name="listMorePopup")
|
||||
= ' '
|
||||
i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
|
||||
//- #6459: was {{ rootUrl }}, a helper that does not exist anywhere, so the
|
||||
//- link input was always empty. absoluteUrl resolves a URL via a card on
|
||||
//- this list.
|
||||
//- link input was always empty. absoluteUrl was then resolved through a
|
||||
//- CARD of this list, so the box showed a card's address - and nothing at
|
||||
//- all for an empty list. A list has its own address now.
|
||||
//- models/lib/boardItemUrl.js
|
||||
input.inline-input(type="text" readonly value="{{ absoluteUrl }}")
|
||||
| {{_ 'added'}}
|
||||
span.date(title=list.createdAt) {{ displayDate createdAt 'LLL' }}
|
||||
|
||||
@@ -278,6 +278,19 @@ Template.listActionPopup.helpers({
|
||||
});
|
||||
|
||||
Template.listActionPopup.events({
|
||||
// Copy the list's own address. `absoluteUrl` so what lands in the
|
||||
// clipboard can be pasted anywhere - a relative path is only a link inside
|
||||
// this page. The popup stays open long enough to show "Copied"; it is the
|
||||
// confirmation, and a menu that vanishes the instant you click leaves you
|
||||
// wondering whether anything happened. models/lib/boardItemUrl.js
|
||||
'click .js-copy-list-link'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const list = Template.currentData();
|
||||
if (!list) return;
|
||||
const url = list.absoluteUrl();
|
||||
if (!url) return;
|
||||
Utils.showCopied(Utils.copyTextToClipboard(url), tpl.$('.copied-tooltip'));
|
||||
},
|
||||
'click .js-list-subscribe'() {},
|
||||
'click .js-add-card.list-header-plus-top'(event) {
|
||||
const listDom = $(`#js-list-${this._id}`)[0];
|
||||
|
||||
@@ -55,6 +55,18 @@ template(name="editSwimlaneTitleForm")
|
||||
|
||||
template(name="swimlaneActionPopup")
|
||||
if currentUser
|
||||
//- Copy link, FIRST and above every permission check: a swimlane has an
|
||||
address now, and copying it is reading, not editing - somebody who may
|
||||
only read the board may still tell a colleague which swimlane they mean.
|
||||
The rows below are all guarded because they change the board.
|
||||
models/lib/boardItemUrl.js, docs/Design/Page/Board-Item-Links.md
|
||||
ul.pop-over-list
|
||||
li: a.js-copy-swimlane-link
|
||||
i.fa.fa-link
|
||||
span
|
||||
| {{_ 'copy-link-to-clipboard'}}
|
||||
span.copied-tooltip {{_ 'copied'}}
|
||||
hr
|
||||
unless currentUser.isCommentOnly
|
||||
unless currentUser.isReadOnly
|
||||
unless currentUser.isReadAssignedOnly
|
||||
|
||||
@@ -98,6 +98,19 @@ Template.editSwimlaneTitleForm.helpers({
|
||||
});
|
||||
|
||||
Template.swimlaneActionPopup.events({
|
||||
// Copy the swimlane's own address. `absoluteUrl` so what lands in the
|
||||
// clipboard can be pasted anywhere - a relative path is only a link inside
|
||||
// this page. The popup stays open long enough to show "Copied"; it is the
|
||||
// confirmation, and a menu that vanishes the instant you click leaves you
|
||||
// wondering whether anything happened. models/lib/boardItemUrl.js
|
||||
'click .js-copy-swimlane-link'(event, tpl) {
|
||||
event.preventDefault();
|
||||
const swimlane = Template.currentData();
|
||||
if (!swimlane) return;
|
||||
const url = swimlane.absoluteUrl();
|
||||
if (!url) return;
|
||||
Utils.showCopied(Utils.copyTextToClipboard(url), tpl.$('.copied-tooltip'));
|
||||
},
|
||||
'click .js-add-swimlane': Popup.open('swimlaneAdd'),
|
||||
'click .js-add-list-from-swimlane': Popup.open('addList'),
|
||||
'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
|
||||
|
||||
@@ -399,3 +399,31 @@ body.swimlane-resizing-active {
|
||||
body.swimlane-resizing-active * {
|
||||
cursor: row-resize !important;
|
||||
}
|
||||
|
||||
/* The swimlane or list a link named, for a moment after it is scrolled to.
|
||||
A scroll that lands mid-board gives no sign of WHICH of the things now on
|
||||
screen the link was about; this says so and then gets out of the way. The
|
||||
class is removed after the animation by client/lib/revealBoardItem.js, so
|
||||
nothing is left highlighted for the rest of the session.
|
||||
|
||||
`outline`, not `border` or `box-shadow`: it is drawn outside the box and so
|
||||
moves neither the swimlane's rows nor the list's cards by a pixel. */
|
||||
.is-revealed {
|
||||
outline: 3px solid #2980b9;
|
||||
outline-offset: -3px;
|
||||
animation: wekan-reveal-fade 2s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes wekan-reveal-fade {
|
||||
0% { outline-color: #2980b9; }
|
||||
70% { outline-color: #2980b9; }
|
||||
100% { outline-color: transparent; }
|
||||
}
|
||||
|
||||
/* Somebody who asked not to be animated gets the outline without the fade -
|
||||
the point is to say which one, and that does not need movement. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.is-revealed {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Session } from 'meteor/session';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import {
|
||||
REVEAL_KINDS,
|
||||
REVEAL_TARGETS,
|
||||
revealElementId,
|
||||
} from '/models/lib/revealBoardItem';
|
||||
|
||||
// Bring the swimlane or list a link named into view.
|
||||
//
|
||||
// The route names it in a Session value (config/router.js) because the route
|
||||
// runs before the board has rendered, and on a board that is already open it
|
||||
// runs without re-creating anything - so there is nothing to scroll at the
|
||||
// moment the address changes.
|
||||
//
|
||||
// This waits for the element instead of assuming it. Swimlanes and lists arrive
|
||||
// as their subscriptions do, and a board with many of them renders in more than
|
||||
// one pass, so a single look on the next tick finds nothing on a cold load. It
|
||||
// retries on a short interval and gives up after a few seconds rather than
|
||||
// spinning forever: a link to a list that was archived or deleted names an
|
||||
// element that is never going to exist, and the board is still the right place
|
||||
// to have landed.
|
||||
//
|
||||
// The Session value is cleared as soon as it is acted on - or given up on - so
|
||||
// it is a one-shot instruction about the address you followed, not a standing
|
||||
// order that re-scrolls the board every time something re-renders.
|
||||
//
|
||||
// docs/Design/Page/Board-Item-Links.md
|
||||
|
||||
const RETRY_MS = 100;
|
||||
const GIVE_UP_MS = 8000;
|
||||
|
||||
function scrollIntoView(element) {
|
||||
if (!element) return;
|
||||
// `center` on both axes: a swimlane is a row and a list is a column, so one
|
||||
// of the two is always the axis that matters and neither kind knows which.
|
||||
if (typeof element.scrollIntoView === 'function') {
|
||||
element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'smooth' });
|
||||
}
|
||||
// A brief highlight, because a scroll that lands mid-board gives no sign of
|
||||
// WHICH of the things on screen the link was about. The class removes itself;
|
||||
// the animation is in the stylesheet.
|
||||
element.classList.add('is-revealed');
|
||||
setTimeout(() => element.classList.remove('is-revealed'), 2000);
|
||||
}
|
||||
|
||||
// Watch one kind. Returns the Tracker computation so a caller can stop it.
|
||||
function watchKind(kind) {
|
||||
const { sessionKey } = REVEAL_TARGETS[kind];
|
||||
let timer = null;
|
||||
const stopTimer = () => {
|
||||
if (timer) { clearInterval(timer); timer = null; }
|
||||
};
|
||||
|
||||
const computation = Tracker.autorun(() => {
|
||||
const id = Session.get(sessionKey);
|
||||
stopTimer();
|
||||
const elementId = revealElementId(kind, id);
|
||||
if (!elementId) return;
|
||||
|
||||
const started = Date.now();
|
||||
const attempt = () => {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
stopTimer();
|
||||
// Cleared first: scrollIntoView can re-enter this autorun through the
|
||||
// reactive Session read, and clearing before scrolling makes the second
|
||||
// pass a no-op rather than a second scroll.
|
||||
Session.set(sessionKey, null);
|
||||
scrollIntoView(element);
|
||||
return;
|
||||
}
|
||||
if (Date.now() - started > GIVE_UP_MS) {
|
||||
// Archived, deleted, or on a board this user cannot see all of. The
|
||||
// board is still the right place to have landed, so this stops quietly.
|
||||
stopTimer();
|
||||
Session.set(sessionKey, null);
|
||||
}
|
||||
};
|
||||
|
||||
// Once now - on a board that is already open the element is already there,
|
||||
// and waiting a tick to find it makes the jump visibly late.
|
||||
attempt();
|
||||
if (Session.get(sessionKey)) timer = setInterval(attempt, RETRY_MS);
|
||||
});
|
||||
|
||||
return { stop() { stopTimer(); computation.stop(); } };
|
||||
}
|
||||
|
||||
// Start watching every kind. Called from the board body once it has rendered.
|
||||
export function watchBoardItemReveals() {
|
||||
const watchers = REVEAL_KINDS.map(watchKind);
|
||||
return { stop() { watchers.forEach(w => w.stop()); } };
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { decideSandstormAutoOpen } from '/models/lib/sandstormAutoOpen';
|
||||
import { cardBoardRedirectTarget } from '/models/lib/cardLinkRedirect';
|
||||
import Settings from '/models/settings';
|
||||
// The swimlane and list routes, whose paths are built beside the URLs they
|
||||
// match so the two cannot disagree. models/lib/boardItemUrl.js
|
||||
import { SWIMLANE_ROUTE_PATH, LIST_ROUTE_PATH } from '/models/lib/boardItemUrl';
|
||||
import { EscapeActions } from '/client/lib/escapeActions';
|
||||
import { Filter } from '/client/lib/filter';
|
||||
import { Utils } from '/client/lib/utils';
|
||||
@@ -392,11 +395,63 @@ function maybeRedirectMovedCard(urlBoardId, cardId) {
|
||||
movedCardCheck = { computation, sub };
|
||||
}
|
||||
|
||||
// A swimlane and a list have an address of their own, so either can be linked
|
||||
// the way a card can. FIVE segments against the card route's four, which is what
|
||||
// keeps the three apart: a card route cannot match these and these cannot match
|
||||
// a card. models/lib/boardItemUrl.js
|
||||
//
|
||||
// The route cannot scroll anything: it runs before the board has rendered, and
|
||||
// on a board that is already open it runs without re-creating anything. So it
|
||||
// names what to REVEAL in a Session value, and the board body reveals it once
|
||||
// the element exists. docs/Design/Page/Board-Item-Links.md
|
||||
FlowRouter.route(SWIMLANE_ROUTE_PATH, {
|
||||
name: 'swimlane',
|
||||
action(params) {
|
||||
Session.set('currentBoard', params.boardId);
|
||||
Session.set('currentCard', null);
|
||||
Session.set('popupCardId', null);
|
||||
Session.set('popupCardBoardId', null);
|
||||
Session.set('revealSwimlaneId', params.swimlaneId);
|
||||
Session.set('revealListId', null);
|
||||
|
||||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
|
||||
this.render('defaultLayout', {
|
||||
content: 'board',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.route(LIST_ROUTE_PATH, {
|
||||
name: 'list',
|
||||
action(params) {
|
||||
Session.set('currentBoard', params.boardId);
|
||||
Session.set('currentCard', null);
|
||||
Session.set('popupCardId', null);
|
||||
Session.set('popupCardBoardId', null);
|
||||
Session.set('revealListId', params.listId);
|
||||
Session.set('revealSwimlaneId', null);
|
||||
|
||||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
|
||||
this.render('defaultLayout', {
|
||||
content: 'board',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.route('/b/:boardId/:slug/:cardId', {
|
||||
name: 'card',
|
||||
action(params) {
|
||||
Session.set('currentBoard', params.boardId);
|
||||
Session.set('currentCard', params.cardId);
|
||||
// A reveal is about the address you just followed, so following another
|
||||
// one cancels it: without this, opening a card after a list link would
|
||||
// scroll the board away from the card it just opened.
|
||||
Session.set('revealSwimlaneId', null);
|
||||
Session.set('revealListId', null);
|
||||
Session.set('popupCardId', null);
|
||||
Session.set('popupCardBoardId', null);
|
||||
// #4758: if the card was moved to another board, redirect to its current one.
|
||||
@@ -453,6 +508,8 @@ FlowRouter.route('/b/:id/:slug', {
|
||||
const previousBoard = Session.get('currentBoard');
|
||||
Session.set('currentBoard', currentBoard);
|
||||
Session.set('currentCard', null);
|
||||
Session.set('revealSwimlaneId', null);
|
||||
Session.set('revealListId', null);
|
||||
Session.set('popupCardId', null);
|
||||
Session.set('popupCardBoardId', null);
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Design: linking to a swimlane, a list, or a card
|
||||
|
||||
Every one of the three has an address, and following it lands on the thing the
|
||||
address names.
|
||||
|
||||
A card has had one since there has been a card route. A swimlane and a list had
|
||||
none: "the Backlog list of this board" could only be sent as *open this board and
|
||||
scroll down*. Worse, `List.absoluteUrl()` answered with the URL of whichever
|
||||
**card** the cache returned first for that list — so the one place that did offer
|
||||
a list link (`listMorePopup`) showed a card's address, and an empty box for an
|
||||
empty list.
|
||||
|
||||
## The shape
|
||||
|
||||
```
|
||||
/b/<boardId>/<slug>/<cardId> a card (unchanged)
|
||||
/b/<boardId>/<slug>/swimlane/<swimlaneId> a swimlane
|
||||
/b/<boardId>/<slug>/list/<listId> a list
|
||||
```
|
||||
|
||||
**Five segments against the card route's four**, which is what keeps the three
|
||||
apart: the card route cannot match a swimlane or list URL, and neither of those
|
||||
can match a card. A guard checks all nine combinations, because losing a segment
|
||||
would silently turn a link to a list into a link to a card called `list`.
|
||||
|
||||
The kind is a **word in the path** rather than a query parameter: it is part of
|
||||
what the address names, and a reader can see which is which.
|
||||
|
||||
`models/lib/boardItemUrl.js` builds them and exports the route patterns, so
|
||||
`config/router.js` registers the routes from the same constants the links are
|
||||
built from — the two cannot disagree about the path when there is only one copy
|
||||
of it.
|
||||
|
||||
It is pure and isomorphic, next to `models/lib/cardUrl.js` and for the same
|
||||
reason: on the server `board()` is async and returns a Promise, so a caller there
|
||||
passes the already-awaited board. A thenable is never read from — doing that is
|
||||
what emitted `/b/undefined/board/...` in [#6427](https://github.com/wekan/wekan/issues/6427).
|
||||
|
||||
## Following the link reveals the thing
|
||||
|
||||
For a card that already worked: the route opens it. For a swimlane or a list
|
||||
there is nothing to open, so the link has to **bring it into view**, or the
|
||||
address named a place and took you to the top of the board instead.
|
||||
|
||||
**The route cannot do the scrolling.** It runs before the board has rendered, and
|
||||
on a board that is already open it runs without re-creating anything — so at the
|
||||
moment the address changes there is nothing on screen to scroll. The route
|
||||
therefore *names* what to reveal in a Session value (`revealSwimlaneId`,
|
||||
`revealListId`) and `client/lib/revealBoardItem.js`, started by the board body
|
||||
once it has rendered, does the rest.
|
||||
|
||||
It **waits for the element** rather than assuming it. Swimlanes and lists arrive
|
||||
as their subscriptions do and a large board renders in more than one pass, so a
|
||||
single look on the next tick finds nothing on a cold load. It retries on a short
|
||||
interval and **gives up after a few seconds**: a link to a list that was archived
|
||||
or deleted names an element that is never going to exist, and the board is still
|
||||
the right place to have landed.
|
||||
|
||||
The Session value is cleared as soon as it is acted on **or** given up on, so it
|
||||
is a one-shot instruction about the address you followed rather than a standing
|
||||
order that re-scrolls the board on every later render. It is cleared *before* the
|
||||
scroll, because `scrollIntoView` can re-enter the autorun through the reactive
|
||||
read and clearing first makes that second pass a no-op.
|
||||
|
||||
Every **other** board route clears it too. Without that, opening a card after a
|
||||
list link would scroll the board away from the card it had just opened.
|
||||
|
||||
The element ids are the ones the templates already carry — `swimlane-<id>` and
|
||||
`js-list-<id>` — held in `models/lib/revealBoardItem.js` as a **map**, not
|
||||
derived by a rule. Deriving one name from another is how
|
||||
`allBoardsMultiselectionSidebar` happened, and this failure is silent: nothing is
|
||||
found and nothing is reported. A guard checks each id against the markup that
|
||||
draws it.
|
||||
|
||||
The revealed element gets a brief outline. A scroll that lands mid-board gives no
|
||||
sign of *which* of the things now on screen the link was about. It is an
|
||||
`outline` rather than a border or shadow so it is drawn outside the box and moves
|
||||
nothing by a pixel, and `prefers-reduced-motion` gets the outline without the
|
||||
fade — the point is to say which one, and that does not need movement.
|
||||
|
||||
## Copying a link
|
||||
|
||||
All three are copied the same way, from the same kind of menu: the **first row**
|
||||
of the hamburger popup — swimlane, list, and the card details menu — with the
|
||||
link icon and the name `copy-link-to-clipboard` ("Copy link to clipboard")
|
||||
beside it.
|
||||
|
||||
The card used to carry this as an **icon in its title header**, named only by a
|
||||
tooltip, which is the one place a name cannot be read without hovering. That
|
||||
button is gone, and so is its handler and the "Copied" tooltip only it used: a
|
||||
handler for markup that no longer exists is dead code that reads as a working
|
||||
feature.
|
||||
|
||||
**The copy row sits above every permission check.** Copying an address is
|
||||
reading, not editing — somebody who may only read the board can still tell a
|
||||
colleague which list they mean — while everything else in those menus changes the
|
||||
board and stays guarded.
|
||||
|
||||
`absoluteUrl()`, not the relative path: what lands in the clipboard is pasted
|
||||
into a chat or an email, and a relative path is only a link inside this page.
|
||||
|
||||
## Related files
|
||||
|
||||
| File Path | File Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `models/lib/boardItemUrl.js` | `.js` module, pure | The URLs and the route patterns. No Meteor, so it is unit-testable and works on the server. |
|
||||
| `models/lib/revealBoardItem.js` | `.js` module, pure | Which Session key each kind uses, and which element id to look for. |
|
||||
| `client/lib/revealBoardItem.js` | `.js` client | Waits for the element, scrolls it into view, gives up rather than spinning. |
|
||||
| `config/router.js` | `.js` routes | The two routes, and the clearing of a stale reveal on every other board route. |
|
||||
| `models/lists.js`, `models/swimlanes.js` | `.js` collections | `originRelativeUrl` / `absoluteUrl` for each. |
|
||||
| `client/components/swimlanes/swimlaneHeader.jade` | `.jade` template | The swimlane popup's copy row. |
|
||||
| `client/components/lists/listHeader.jade` | `.jade` template | The list popup's copy row, and `listMorePopup`, whose link box now shows the list. |
|
||||
| `client/components/cards/cardDetails.jade` | `.jade` template | The card menu's copy row — and the header button that is gone. |
|
||||
| `client/components/swimlanes/swimlanes.css` | `.css` | The reveal outline. |
|
||||
| `tests/boardItemLinks.test.cjs` | `.cjs` Node test | The URLs, that no address matches the wrong route, the reveal, the three copy rows, and the translations. |
|
||||
|
||||
## Related
|
||||
|
||||
- [The header](Header.md) — where the card's copy button used to be
|
||||
- [Archive](Archive.md) — the other page a board menu row navigates to
|
||||
@@ -0,0 +1,146 @@
|
||||
# Design: the header
|
||||
|
||||
WeKan has **one** header bar. It is always on screen, it says where you are, and
|
||||
it holds the controls of whatever page you are on.
|
||||
|
||||
There used to be two. The first was a thin strip with the house icon, the logo
|
||||
and your account; the second was a coloured block that every page filled with its
|
||||
own title and its own buttons. That second bar is gone from every page but the
|
||||
few that still have something only it can hold, and `#header` is not rendered at
|
||||
all where a page has none — it is a coloured block with a height of its own, so
|
||||
leaving it in painted a tall empty strip under the first bar.
|
||||
|
||||
## What the bar says
|
||||
|
||||
Beside the house icon is **the page you are on**. It used to read "All Boards" on
|
||||
every page, so the one bar that is always on screen named a place you were not.
|
||||
|
||||
`models/lib/pageTitles.js` maps a route name to a translation key. Three sources,
|
||||
in order:
|
||||
|
||||
1. **the board's title**, wherever there is a board — the one thing worth having
|
||||
there;
|
||||
2. **a custom title** — Support and Accessibility can be renamed by an admin, and
|
||||
Import names its source ("Import / Trello");
|
||||
3. **the key** for everything else.
|
||||
|
||||
A board title and a custom title both come back as `title` rather than `key`,
|
||||
because both are text to print as-is: neither may go through the translator, and
|
||||
a board called "settings" is not the Admin Panel.
|
||||
|
||||
A route nobody has added answers with nothing and the bar shows nothing — better
|
||||
than naming the wrong page.
|
||||
|
||||
### …and the path below it
|
||||
|
||||
Two pages are several places under one name, and for those the title continues
|
||||
after a slash:
|
||||
|
||||
```
|
||||
Admin Panel / Settings / Version
|
||||
All Boards / Workspaces / Engineering / Backend
|
||||
```
|
||||
|
||||
`headerTitleTrail()` in `client/components/main/header.js` returns **one list** of
|
||||
segments rather than a helper per segment, because the two pages do not have the
|
||||
same number of them — the Admin Panel always has two, and a workspace has as many
|
||||
as its tree is deep. Each entry is one of the two forms a left-menu entry already
|
||||
has: `key` for something translated, `title` for text that must not be.
|
||||
|
||||
The words are the ones the navigation beside it already uses — the Admin Panel's
|
||||
tab key and its menu row's label ([Admin Panel URLs](Admin-Panel-URLs.md)), the
|
||||
All Boards left-menu key and the workspace's own name
|
||||
([All Boards URLs](All-Boards-URLs.md)) — so the title and the thing highlighted
|
||||
next to it cannot say different things about one place.
|
||||
|
||||
It is read from the **URL** and from the user document, never from the pages
|
||||
themselves. The header is a separate Blaze instance, and importing a page module
|
||||
from it once ran that module before its own template was registered; the throw
|
||||
aborted every module after it, which is why `client/lib/pageTitleSources.js`
|
||||
exists — it registers no template and touches no DOM, so it is safe to load
|
||||
early.
|
||||
|
||||
## What the bar holds
|
||||
|
||||
In source order: the house and the page title (plus the pencil that renames a
|
||||
board), the logo, the phone/desktop toggle, the drag-handles toggle — and then
|
||||
`.header-quick-access-end`, which holds everything else and is pushed to the end
|
||||
of the bar by a single `margin-inline-start: auto`. That is a **logical**
|
||||
property, so a right-to-left language mirrors it by itself rather than needing a
|
||||
second rule kept in step with the first.
|
||||
|
||||
Inside that group: the starred-boards dropdown, the page's own controls, the
|
||||
page's view menu, the Admin Panel's tabs, the notification bell, **a divider**,
|
||||
help, your account, and the sidebar hamburger where the page has one.
|
||||
|
||||
The divider is the seam: everything before it belongs to the **page**, everything
|
||||
after belongs to **you**. Without it the run of icons reads as one list of
|
||||
unrelated things.
|
||||
|
||||
**Every button is an icon named by a tooltip.** The view menus lost their visible
|
||||
labels — the board's said "Swimlanes", All Boards' said "Lists" — because the
|
||||
label is the widest part of a button and the name is in the tooltip where the
|
||||
other buttons of this bar already keep theirs. A tooltip is a poor place for a
|
||||
name in general; it is the right one here only because the whole bar is icons and
|
||||
consistent.
|
||||
|
||||
**The button of the page you are on keeps the hover background**, permanently and
|
||||
a shade darker. Darker rather than equal: hovering the tab you are already on has
|
||||
to still look like a hover.
|
||||
|
||||
### It wraps
|
||||
|
||||
`#header-quick-access` is `flex-wrap: wrap` with a row gap, and **every** height
|
||||
on it is a minimum. It used to be one row with `overflow: hidden`, so a button
|
||||
that did not fit was not drawn at all — and a button that is not drawn gives no
|
||||
sign that it exists. Four phone rules pinned 40px or 48px; a fixed height cannot
|
||||
hold two rows, so it would have cut off exactly what the wrap was for. A phone is
|
||||
where the buttons run out of room first, which makes it the last place that
|
||||
should hide them.
|
||||
|
||||
`--wekan-header-height` is what anything laid out against the viewport starts
|
||||
below. It measures **both** bars — the bottom of the lowest one present, rather
|
||||
than a sum of heights, so any margin between them counts and a bar that is absent
|
||||
contributes nothing without a special case — and each bar has its own
|
||||
`ResizeObserver`, because the first one wrapping to a second row is a resize of
|
||||
that element and of nothing else. `client/lib/utils.js` keeps it current.
|
||||
|
||||
## The sidebar, and the hamburger
|
||||
|
||||
A page's controls used to live in its own second header bar beside its `h1`. The
|
||||
title moved here and the controls moved into a **right sidebar**, so a page is its
|
||||
content plus one panel rather than its content plus a strip of buttons.
|
||||
|
||||
`models/lib/pageSidebar.js` says which page gets what. A board has its own
|
||||
sidebar and All Boards has its own; every other page with controls gets the
|
||||
shared one, and a page with **no** controls gets no sidebar and no hamburger —
|
||||
an empty panel is worse than none.
|
||||
|
||||
`NO_HAMBURGER_ROUTES` is the other half of it: a page can have a sidebar and
|
||||
still not be offered a hamburger. All Boards is one — its four controls are in
|
||||
this bar and Search and Multi-Selection open the sidebar straight into their own
|
||||
view, so the hamburger's only destination was a menu listing what is already one
|
||||
click away. A board is deliberately not in that list: what its sidebar holds —
|
||||
members, labels, activities, settings — is not in the bar and has nowhere else to
|
||||
be opened from.
|
||||
|
||||
## Related files
|
||||
|
||||
| File Path | File Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `client/components/main/header.jade` | `.jade` template | The bar itself, and the starred-boards popup. |
|
||||
| `client/components/main/header.js` | `.js` Blaze template logic | The title, the trail, which page gets what, and the hamburger's click. |
|
||||
| `client/components/main/header.css` | `.css` | The wrap, the end group, the active mark, the divider. |
|
||||
| `models/lib/pageTitles.js` | `.js` module, pure | Route name → title key, and the Admin Panel's second segment. |
|
||||
| `models/lib/pageSidebar.js` | `.js` module, pure | Which sidebar a page has, and whether it is offered a hamburger. |
|
||||
| `client/lib/pageTitleSources.js` | `.js` client | The three pages whose title is their own, without importing those pages. |
|
||||
| `client/lib/utils.js` | `.js` client | `--wekan-header-height`, measured from both bars. |
|
||||
| `tests/headerBars.test.cjs` | `.cjs` Node test | The wrap, the end group, the view menus, the active mark, the divider, and both title paths. |
|
||||
| `tests/pageSidebar.test.cjs` | `.cjs` Node test | Which page has which sidebar, and where the hamburger is offered. |
|
||||
|
||||
## Related
|
||||
|
||||
- [Admin Panel URLs](Admin-Panel-URLs.md) — the panel's addresses and its title path
|
||||
- [All Boards URLs](All-Boards-URLs.md) — the sections, workspaces and their title path
|
||||
- [All Boards](All-Boards.md) — the four controls this bar holds there
|
||||
- [Linking to a swimlane, list or card](Board-Item-Links.md) — where the card's copy button went
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Hapus Senarai semak?",
|
||||
"checklistItemDeletePopup-title": "Hapus item senarai semak?",
|
||||
"copy-card-link-to-clipboard": "Salin capaian kad ke papan klip",
|
||||
"copy-link-to-clipboard": "Salin capaian ke papan klip",
|
||||
"copy-text-to-clipboard": "Salin teks ke papan klip",
|
||||
"linkCardPopup-title": "Paut Kad",
|
||||
"searchElementPopup-title": "Cari",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Vee kontrolelys uit?",
|
||||
"checklistItemDeletePopup-title": "Vee kontrolelysitem uit?",
|
||||
"copy-card-link-to-clipboard": "Kopieer kaartskakel na knipbord",
|
||||
"copy-link-to-clipboard": "Kopieer skakel na knipbord",
|
||||
"copy-text-to-clipboard": "Kopieer teks na knipbord",
|
||||
"linkCardPopup-title": "Koppelkaart",
|
||||
"searchElementPopup-title": "Soek",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Vee kontrolelys uit?",
|
||||
"checklistItemDeletePopup-title": "Vee kontrolelysitem uit?",
|
||||
"copy-card-link-to-clipboard": "Kopieer kaartskakel na knipbord",
|
||||
"copy-link-to-clipboard": "Kopieer skakel na knipbord",
|
||||
"copy-text-to-clipboard": "Kopieer teks na knipbord",
|
||||
"linkCardPopup-title": "Koppelkaart",
|
||||
"searchElementPopup-title": "Soek",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Supprimer la check-list ?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "نسخ رابط البطاقة إلى الحافظة",
|
||||
"copy-link-to-clipboard": "نسخ الرابط إلى الحافظة",
|
||||
"copy-text-to-clipboard": "Copier le texte dans le presse-papier",
|
||||
"linkCardPopup-title": "ربط البطاقة",
|
||||
"searchElementPopup-title": "بحث",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "حذف قائمة التحقق؟",
|
||||
"checklistItemDeletePopup-title": "حذف عنصر قائمة التحقق؟",
|
||||
"copy-card-link-to-clipboard": "نسخ رابط البطاقة إلى الحافظة",
|
||||
"copy-link-to-clipboard": "نسخ الرابط إلى الحافظة",
|
||||
"copy-text-to-clipboard": "نسخ النص إلى الحافظة",
|
||||
"linkCardPopup-title": "ربط البطاقة",
|
||||
"searchElementPopup-title": "بحث",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "حذف قائمة التحقق؟",
|
||||
"checklistItemDeletePopup-title": "حذف عنصر قائمة التحقق؟",
|
||||
"copy-card-link-to-clipboard": "نسخ رابط البطاقة إلى الحافظة",
|
||||
"copy-link-to-clipboard": "نسخ الرابط إلى الحافظة",
|
||||
"copy-text-to-clipboard": "نسخ النص إلى الحافظة",
|
||||
"linkCardPopup-title": "ربط البطاقة",
|
||||
"searchElementPopup-title": "بحث",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "حذف قائمة التحقق؟",
|
||||
"checklistItemDeletePopup-title": "حذف عنصر قائمة التحقق؟",
|
||||
"copy-card-link-to-clipboard": "نسخ رابط البطاقة إلى الحافظة",
|
||||
"copy-link-to-clipboard": "نسخ الرابط إلى الحافظة",
|
||||
"copy-text-to-clipboard": "نسخ النص إلى الحافظة",
|
||||
"linkCardPopup-title": "ربط البطاقة",
|
||||
"searchElementPopup-title": "بحث",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar l'elementu del checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Yoxlama siyahısı silinsin?",
|
||||
"checklistItemDeletePopup-title": "Yoxlama siyahısı elementi silinsin?",
|
||||
"copy-card-link-to-clipboard": "Kart linkini panoya kopyalayın",
|
||||
"copy-link-to-clipboard": "Linki panoya kopyalayın",
|
||||
"copy-text-to-clipboard": "Mətni panoya kopyalayın",
|
||||
"linkCardPopup-title": "Əlaqə Kartı",
|
||||
"searchElementPopup-title": "Axtar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Yoxlama siyahısı silinsin?",
|
||||
"checklistItemDeletePopup-title": "Yoxlama siyahısı elementi silinsin?",
|
||||
"copy-card-link-to-clipboard": "Kart linkini panoya kopyalayın",
|
||||
"copy-link-to-clipboard": "Linki panoya kopyalayın",
|
||||
"copy-text-to-clipboard": "Mətni panoya kopyalayın",
|
||||
"linkCardPopup-title": "Əlaqə Kartı",
|
||||
"searchElementPopup-title": "Axtar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Yoxlama siyahısı silinsin?",
|
||||
"checklistItemDeletePopup-title": "Yoxlama siyahısı elementi silinsin?",
|
||||
"copy-card-link-to-clipboard": "Kart linkini panoya kopyalayın",
|
||||
"copy-link-to-clipboard": "Linki panoya kopyalayın",
|
||||
"copy-text-to-clipboard": "Mətni panoya kopyalayın",
|
||||
"linkCardPopup-title": "Əlaqə Kartı",
|
||||
"searchElementPopup-title": "Axtar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Выдаліць кантрольны спіс?",
|
||||
"checklistItemDeletePopup-title": "Выдаліць пункт кантрольнага спіса?",
|
||||
"copy-card-link-to-clipboard": "Скапіраваць спасылку на картку ў буфер абмену",
|
||||
"copy-link-to-clipboard": "Скапіраваць спасылку ў буфер абмену",
|
||||
"copy-text-to-clipboard": "Скапіраваць тэкст у буфер абмену",
|
||||
"linkCardPopup-title": "Звязаць картку",
|
||||
"searchElementPopup-title": "Пошук",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Избрисаћете предметну радњу?",
|
||||
"checklistItemDeletePopup-title": "Избрисаћете ставку из предметне радње?",
|
||||
"copy-card-link-to-clipboard": "Копирай връзката на картата в клипборда",
|
||||
"copy-link-to-clipboard": "Копирай връзката в клипборда",
|
||||
"copy-text-to-clipboard": "Причувај текст на кратко",
|
||||
"linkCardPopup-title": "Свържи картата",
|
||||
"searchElementPopup-title": "Търсене",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Supprimer la checklist ?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Copier le lien de la carte dans le presse-papier",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copier le texte dans le presse-papier",
|
||||
"linkCardPopup-title": "Lier une Carte",
|
||||
"searchElementPopup-title": "Klask",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Vols suprimir la llista de comprovació?",
|
||||
"checklistItemDeletePopup-title": "Voleu esborrar l'element de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copia l'enllaç de la ftixa al porta-retalls",
|
||||
"copy-link-to-clipboard": "Copia l'enllaç al porta-retalls",
|
||||
"copy-text-to-clipboard": "Copia el text al porta-retalls",
|
||||
"linkCardPopup-title": "Fitxa d'enllaç",
|
||||
"searchElementPopup-title": "Cerca",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Vols suprimir la llista de comprovació?",
|
||||
"checklistItemDeletePopup-title": "Voleu esborrar l'element de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copia l'enllaç de la ftixa al porta-retalls",
|
||||
"copy-link-to-clipboard": "Copia l'enllaç al porta-retalls",
|
||||
"copy-text-to-clipboard": "Copia el text al porta-retalls",
|
||||
"linkCardPopup-title": "Fitxa d'enllaç",
|
||||
"searchElementPopup-title": "Cerca",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Vols suprimir la llista de comprovació?",
|
||||
"checklistItemDeletePopup-title": "Voleu esborrar l'element de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copia l'enllaç de la ftixa al porta-retalls",
|
||||
"copy-link-to-clipboard": "Copia l'enllaç al porta-retalls",
|
||||
"copy-text-to-clipboard": "Copia el text al porta-retalls",
|
||||
"linkCardPopup-title": "Fitxa d'enllaç",
|
||||
"searchElementPopup-title": "Cerca",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "删除清单?",
|
||||
"checklistItemDeletePopup-title": "删除清单项目?",
|
||||
"copy-card-link-to-clipboard": "将卡片链接复制到剪贴板",
|
||||
"copy-link-to-clipboard": "复制链接到剪贴板",
|
||||
"copy-text-to-clipboard": "将文本复制到剪贴板",
|
||||
"linkCardPopup-title": "链接卡",
|
||||
"searchElementPopup-title": "搜索",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Smazat checklist?",
|
||||
"checklistItemDeletePopup-title": "Smazat položku checklistu?",
|
||||
"copy-card-link-to-clipboard": "Kopírovat adresu karty do mezipaměti",
|
||||
"copy-link-to-clipboard": "Kopírovat odkaz do schránky",
|
||||
"copy-text-to-clipboard": "Kopírovat text do schránky",
|
||||
"linkCardPopup-title": "Propojit kartu",
|
||||
"searchElementPopup-title": "Hledat",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Smazat checklist?",
|
||||
"checklistItemDeletePopup-title": "Smazat položku checklistu?",
|
||||
"copy-card-link-to-clipboard": "Kopírovat adresu karty do mezipaměti",
|
||||
"copy-link-to-clipboard": "Kopírovat odkaz do schránky",
|
||||
"copy-text-to-clipboard": "Kopírovat text do schránky",
|
||||
"linkCardPopup-title": "Propojit kartu",
|
||||
"searchElementPopup-title": "Hledat",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Dileu Rhestr Wirio?",
|
||||
"checklistItemDeletePopup-title": "Dileu Eitem Rhestr Wirio?",
|
||||
"copy-card-link-to-clipboard": "Copïo dolen cerdyn i'r clipfwrdd",
|
||||
"copy-link-to-clipboard": "Copïo dolen i'r clipfwrdd",
|
||||
"copy-text-to-clipboard": "Copïo testun i'r clipfwrdd",
|
||||
"linkCardPopup-title": "Cerdyn Cyswllt",
|
||||
"searchElementPopup-title": "Chwiliwch",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Dileu Rhestr Wirio?",
|
||||
"checklistItemDeletePopup-title": "Dileu Eitem Rhestr Wirio?",
|
||||
"copy-card-link-to-clipboard": "Copïo dolen cerdyn i'r clipfwrdd",
|
||||
"copy-link-to-clipboard": "Copïo dolen i'r clipfwrdd",
|
||||
"copy-text-to-clipboard": "Copïo testun i'r clipfwrdd",
|
||||
"linkCardPopup-title": "Cerdyn Cyswllt",
|
||||
"searchElementPopup-title": "Chwiliwch",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Slet tjeklisten?",
|
||||
"checklistItemDeletePopup-title": "Slet element på tjeklisten?",
|
||||
"copy-card-link-to-clipboard": "Kopiér link til kort til udklipsholder",
|
||||
"copy-link-to-clipboard": "Kopiér link til udklipsholder",
|
||||
"copy-text-to-clipboard": "Kopier tekst til udklipsholder",
|
||||
"linkCardPopup-title": "Sammenkæd kort",
|
||||
"searchElementPopup-title": "Søg",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Checkliste löschen?",
|
||||
"checklistItemDeletePopup-title": "Element der Checkliste löschen?",
|
||||
"copy-card-link-to-clipboard": "Kopiere Link zur Karte in die Zwischenablage",
|
||||
"copy-link-to-clipboard": "Link in die Zwischenablage kopieren",
|
||||
"copy-text-to-clipboard": "Text in die Zwischenablage kopieren",
|
||||
"linkCardPopup-title": "Karte verknüpfen",
|
||||
"searchElementPopup-title": "Suche",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Checkliste löschen?",
|
||||
"checklistItemDeletePopup-title": "Element der Checkliste löschen?",
|
||||
"copy-card-link-to-clipboard": "Kopiere Link zur Karte in die Zwischenablage",
|
||||
"copy-link-to-clipboard": "Link in die Zwischenablage kopieren",
|
||||
"copy-text-to-clipboard": "Text in die Zwischenablage kopieren",
|
||||
"linkCardPopup-title": "Karte verknüpfen",
|
||||
"searchElementPopup-title": "Suche",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Checkliste löschen?",
|
||||
"checklistItemDeletePopup-title": "Element der Checkliste löschen?",
|
||||
"copy-card-link-to-clipboard": "Kopiere Link zur Karte in die Zwischenablage",
|
||||
"copy-link-to-clipboard": "Link in die Zwischenablage kopieren",
|
||||
"copy-text-to-clipboard": "Text in die Zwischenablage kopieren",
|
||||
"linkCardPopup-title": "Karte verknüpfen",
|
||||
"searchElementPopup-title": "Suche",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Checkliste löschen?",
|
||||
"checklistItemDeletePopup-title": "Element der Checkliste löschen?",
|
||||
"copy-card-link-to-clipboard": "Kopiere Link zur Karte in die Zwischenablage",
|
||||
"copy-link-to-clipboard": "Link in die Zwischenablage kopieren",
|
||||
"copy-text-to-clipboard": "Text in die Zwischenablage kopieren",
|
||||
"linkCardPopup-title": "Karte verknüpfen",
|
||||
"searchElementPopup-title": "Suche",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Eliminare checklist?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Αντιγραφή του συνδέσμου της κάρτας στο clipboard",
|
||||
"copy-link-to-clipboard": "Αντιγραφή του συνδέσμου στο clipboard",
|
||||
"copy-text-to-clipboard": "Copia il testo negli appunti",
|
||||
"linkCardPopup-title": "Σύνδεση Κάρτας",
|
||||
"searchElementPopup-title": "Αναζήτηση",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Eliminare checklist?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Αντιγραφή του συνδέσμου της κάρτας στο clipboard",
|
||||
"copy-link-to-clipboard": "Αντιγραφή του συνδέσμου στο clipboard",
|
||||
"copy-text-to-clipboard": "Copia il testo negli appunti",
|
||||
"linkCardPopup-title": "Σύνδεση Κάρτας",
|
||||
"searchElementPopup-title": "Αναζήτηση",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Delete Checklist?",
|
||||
"checklistItemDeletePopup-title": "Delete Checklist Item?",
|
||||
"copy-card-link-to-clipboard": "Copy card link to clipboard",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copy text to clipboard",
|
||||
"linkCardPopup-title": "Link Card",
|
||||
"searchElementPopup-title": "Search",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Serĉi",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar enlace a tarjeta en el portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Tarjeta vinculada",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "¿Borrar la lista de tareas?",
|
||||
"checklistItemDeletePopup-title": "¿Eliminar elemento de la checklist?",
|
||||
"copy-card-link-to-clipboard": "Copiar el enlace de la tarjeta al portapapeles",
|
||||
"copy-link-to-clipboard": "Copiar el enlace al portapapeles",
|
||||
"copy-text-to-clipboard": "Copiar texto en el portapapeles",
|
||||
"linkCardPopup-title": "Enlazar tarjeta",
|
||||
"searchElementPopup-title": "Buscar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Kustuta kontrollnimekiri?",
|
||||
"checklistItemDeletePopup-title": "Kustutada kontrollnimekirja objekt?",
|
||||
"copy-card-link-to-clipboard": "Kopeeri kaardi link lõikelauale",
|
||||
"copy-link-to-clipboard": "Kopeeri link lõikelauale",
|
||||
"copy-text-to-clipboard": "Teksti kopeerimine lõikelauale",
|
||||
"linkCardPopup-title": "Linkkaart",
|
||||
"searchElementPopup-title": "Otsi",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Ezabatu kontrol-zerrenda?",
|
||||
"checklistItemDeletePopup-title": "Kontrol-zerrendako elementua ezabatu?",
|
||||
"copy-card-link-to-clipboard": "Kopiatu txartela arbelera",
|
||||
"copy-link-to-clipboard": "Kopiatu esteka arbelean",
|
||||
"copy-text-to-clipboard": "Kopiatu testua arbelean",
|
||||
"linkCardPopup-title": "Estekatu txartela",
|
||||
"searchElementPopup-title": "Bilatu",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "چکلیست حذف شود؟",
|
||||
"checklistItemDeletePopup-title": "آیتم چکلیست حذف شود؟",
|
||||
"copy-card-link-to-clipboard": "درج پیوند کارت در حافظه",
|
||||
"copy-link-to-clipboard": "کپی پیوند در کلیپبورد",
|
||||
"copy-text-to-clipboard": "کپی متن در کلیپبورد",
|
||||
"linkCardPopup-title": "ارتباط دادن کارت",
|
||||
"searchElementPopup-title": "جستجو",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "چکلیست حذف شود؟",
|
||||
"checklistItemDeletePopup-title": "آیتم چکلیست حذف شود؟",
|
||||
"copy-card-link-to-clipboard": "درج پیوند کارت در حافظه",
|
||||
"copy-link-to-clipboard": "کپی پیوند در کلیپبورد",
|
||||
"copy-text-to-clipboard": "کپی متن در کلیپبورد",
|
||||
"linkCardPopup-title": "ارتباط دادن کارت",
|
||||
"searchElementPopup-title": "جستجو",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Poista tarkistuslista?",
|
||||
"checklistItemDeletePopup-title": "Poista tarkistuslistan kohta?",
|
||||
"copy-card-link-to-clipboard": "Kopioi kortin linkki leikepöydälle",
|
||||
"copy-link-to-clipboard": "Kopioi linkki leikepöydälle",
|
||||
"copy-text-to-clipboard": "Kopioi teksti leikepöydälle",
|
||||
"linkCardPopup-title": "Linkitä kortti",
|
||||
"searchElementPopup-title": "Etsi",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Supprimer la check-list ?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Copier le lien de la carte dans le presse-papier",
|
||||
"copy-link-to-clipboard": "Copier le lien dans le presse-papier",
|
||||
"copy-text-to-clipboard": "Copier le texte dans le presse-papier",
|
||||
"linkCardPopup-title": "Lier une Carte",
|
||||
"searchElementPopup-title": "Chercher",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Supprimer la checklist ?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Copier le lien de la carte dans le presse-papier",
|
||||
"copy-link-to-clipboard": "Copier le lien dans le presse-papier",
|
||||
"copy-text-to-clipboard": "Copier le texte dans le presse-papier",
|
||||
"linkCardPopup-title": "Lier une Carte",
|
||||
"searchElementPopup-title": "Chercher",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Supprimer la check-list ?",
|
||||
"checklistItemDeletePopup-title": "Supprimer l'élément de la check-list ?",
|
||||
"copy-card-link-to-clipboard": "Copier le lien de la carte dans le presse-papier",
|
||||
"copy-link-to-clipboard": "Copier le lien dans le presse-papier",
|
||||
"copy-text-to-clipboard": "Copier le texte dans le presse-papier",
|
||||
"linkCardPopup-title": "Lier une Carte",
|
||||
"searchElementPopup-title": "Chercher",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Kontrolearje wiskje?",
|
||||
"checklistItemDeletePopup-title": "Checklist-item wiskje?",
|
||||
"copy-card-link-to-clipboard": "Kopiearje kaartkeppeling nei klamboerd",
|
||||
"copy-link-to-clipboard": "Kopiearje keppeling nei klamboerd",
|
||||
"copy-text-to-clipboard": "Kopiearje tekst nei klamboerd",
|
||||
"linkCardPopup-title": "Kaart keppelje",
|
||||
"searchElementPopup-title": "Sykje",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Kontrolearje wiskje?",
|
||||
"checklistItemDeletePopup-title": "Checklist-item wiskje?",
|
||||
"copy-card-link-to-clipboard": "Kopiearje kaartkeppeling nei klamboerd",
|
||||
"copy-link-to-clipboard": "Kopiearje keppeling nei klamboerd",
|
||||
"copy-text-to-clipboard": "Kopiearje tekst nei klamboerd",
|
||||
"linkCardPopup-title": "Kaart keppelje",
|
||||
"searchElementPopup-title": "Sykje",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Eliminar lista de verificação?",
|
||||
"checklistItemDeletePopup-title": "Excluir Item da Lista de Verificação?",
|
||||
"copy-card-link-to-clipboard": "Copiar a ligação do cartão para a área de transferência",
|
||||
"copy-link-to-clipboard": "Copiar a ligazón ao portapapeis",
|
||||
"copy-text-to-clipboard": "Copiar texto para a área de transferência",
|
||||
"linkCardPopup-title": "Ligar Cartão",
|
||||
"searchElementPopup-title": "Procurar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Eliminar lista de verificação?",
|
||||
"checklistItemDeletePopup-title": "Excluir Item da Lista de Verificação?",
|
||||
"copy-card-link-to-clipboard": "Copiar a ligação do cartão para a área de transferência",
|
||||
"copy-link-to-clipboard": "Copiar a ligazón ao portapapeis",
|
||||
"copy-text-to-clipboard": "Copiar texto para a área de transferência",
|
||||
"linkCardPopup-title": "Ligar Cartão",
|
||||
"searchElementPopup-title": "Procurar",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "ચેકલિસ્ટ ડિલીટ કરીએ?",
|
||||
"checklistItemDeletePopup-title": "ચેકલિસ્ટ આઇટમ કાઢી નાખીએ?",
|
||||
"copy-card-link-to-clipboard": "ક્લિપબોર્ડ પર કાર્ડ લિંક કૉપિ કરો",
|
||||
"copy-link-to-clipboard": "ક્લિપબોર્ડ પર લિંક કૉપિ કરો",
|
||||
"copy-text-to-clipboard": "ટેક્સ્ટને ક્લિપબોર્ડ પર કૉપિ કરો",
|
||||
"linkCardPopup-title": "લિંક કાર્ડ",
|
||||
"searchElementPopup-title": "શોધો",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "למחוק רשימת מטלות?",
|
||||
"checklistItemDeletePopup-title": "למחוק פריט רשימת סימון?",
|
||||
"copy-card-link-to-clipboard": "העתקת קישור הכרטיס ללוח הגזירים",
|
||||
"copy-link-to-clipboard": "העתקת קישור ללוח הגזירים",
|
||||
"copy-text-to-clipboard": "העתקת טקסט ללוח הגזירים",
|
||||
"linkCardPopup-title": "קישור כרטיס",
|
||||
"searchElementPopup-title": "חיפוש",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "למחוק רשימת מטלות?",
|
||||
"checklistItemDeletePopup-title": "למחוק פריט רשימת סימון?",
|
||||
"copy-card-link-to-clipboard": "העתקת קישור הכרטיס ללוח הגזירים",
|
||||
"copy-link-to-clipboard": "העתקת קישור ללוח הגזירים",
|
||||
"copy-text-to-clipboard": "העתקת טקסט ללוח הגזירים",
|
||||
"linkCardPopup-title": "קישור כרטיס",
|
||||
"searchElementPopup-title": "חיפוש",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "ચેકલિસ્ટ ડિલીટ કરીએ?",
|
||||
"checklistItemDeletePopup-title": "ચેકલિસ્ટ આઇટમ કાઢી નાખીએ?",
|
||||
"copy-card-link-to-clipboard": "कॉपी कार्ड क्लिपबोर्ड करने के लिए लिंक",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "ટેક્સ્ટને ક્લિપબોર્ડ પર કૉપિ કરો",
|
||||
"linkCardPopup-title": "कार्ड कड़ी",
|
||||
"searchElementPopup-title": "શોધો",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "ચેકલિસ્ટ ડિલીટ કરીએ?",
|
||||
"checklistItemDeletePopup-title": "ચેકલિસ્ટ આઇટમ કાઢી નાખીએ?",
|
||||
"copy-card-link-to-clipboard": "कॉपी कार्ड क्लिपबोर्ड करने के लिए लिंक",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "ટેક્સ્ટને ક્લિપબોર્ડ પર કૉપિ કરો",
|
||||
"linkCardPopup-title": "कार्ड कड़ी",
|
||||
"searchElementPopup-title": "શોધો",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Избрисаћете предметну радњу?",
|
||||
"checklistItemDeletePopup-title": "Избрисаћете ставку из предметне радње?",
|
||||
"copy-card-link-to-clipboard": "Причувај везу на кратко",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Причувај текст на кратко",
|
||||
"linkCardPopup-title": "Повежи предмет",
|
||||
"searchElementPopup-title": "Traži",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Törlöd az ellenőrzőlistát?",
|
||||
"checklistItemDeletePopup-title": "Törlöd az ellenőrzőlista-elemet?",
|
||||
"copy-card-link-to-clipboard": "Kártya hivatkozásának másolása a vágólapra",
|
||||
"copy-link-to-clipboard": "Hivatkozás másolása a vágólapra",
|
||||
"copy-text-to-clipboard": "Szöveg másolása vágólapra",
|
||||
"linkCardPopup-title": "Kártyára hivatkozás",
|
||||
"searchElementPopup-title": "Keresés",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Ջնջե՞լ ստուգաթերթը:",
|
||||
"checklistItemDeletePopup-title": "Ջնջե՞լ ստուգաթերթի կետը:",
|
||||
"copy-card-link-to-clipboard": "Պատճենել քարտի հղումը սեղմատախտակին",
|
||||
"copy-link-to-clipboard": "Պատճենել հղումը սեղմատախտակին",
|
||||
"copy-text-to-clipboard": "Պատճենել տեքստը clipboard-ում",
|
||||
"linkCardPopup-title": "Հղման քարտ",
|
||||
"searchElementPopup-title": "Որոնում",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Hapus Senarai semak?",
|
||||
"checklistItemDeletePopup-title": "Hapus item senarai semak?",
|
||||
"copy-card-link-to-clipboard": "Salin capaian kad ke papan klip",
|
||||
"copy-link-to-clipboard": "Salin capaian ke papan klip",
|
||||
"copy-text-to-clipboard": "Salin teks ke papan klip",
|
||||
"linkCardPopup-title": "Paut Kad",
|
||||
"searchElementPopup-title": "Cari",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Hichapụ ndepụta nlele?",
|
||||
"checklistItemDeletePopup-title": "Hichapụ ihe ndetu ihe?",
|
||||
"copy-card-link-to-clipboard": "Detuo njikọ kaadị na klipbọọdụ",
|
||||
"copy-link-to-clipboard": "Detuo njikọ na klipbọọdụ",
|
||||
"copy-text-to-clipboard": "Detuo ederede na klipbọọdụ",
|
||||
"linkCardPopup-title": "Kaadị njikọ",
|
||||
"searchElementPopup-title": "Chọọ",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Eliminare checklist?",
|
||||
"checklistItemDeletePopup-title": "Eliminare l'elemento della checklist?",
|
||||
"copy-card-link-to-clipboard": "Copia link della scheda negli appunti",
|
||||
"copy-link-to-clipboard": "Copia il link negli appunti",
|
||||
"copy-text-to-clipboard": "Copia il testo negli appunti",
|
||||
"linkCardPopup-title": "Collega scheda",
|
||||
"searchElementPopup-title": "Cerca",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "チェックリストを削除しますか?",
|
||||
"checklistItemDeletePopup-title": "チェックリスト項目を削除しますか?",
|
||||
"copy-card-link-to-clipboard": "カードへのリンクをクリップボードにコピー",
|
||||
"copy-link-to-clipboard": "リンクをクリップボードにコピー",
|
||||
"copy-text-to-clipboard": "テキストをクリップボードにコピー",
|
||||
"linkCardPopup-title": "カードをリンク",
|
||||
"searchElementPopup-title": "検索",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "チェックリストを削除しますか?",
|
||||
"checklistItemDeletePopup-title": "チェックリスト項目を削除しますか?",
|
||||
"copy-card-link-to-clipboard": "カードへのリンクをクリップボードにコピー",
|
||||
"copy-link-to-clipboard": "リンクをクリップボードにコピー",
|
||||
"copy-text-to-clipboard": "テキストをクリップボードにコピー",
|
||||
"linkCardPopup-title": "カードをリンク",
|
||||
"searchElementPopup-title": "検索",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Удалить Чек-лист?",
|
||||
"checklistItemDeletePopup-title": "წაიშალოს საკონტროლო სიის ელემენტი?",
|
||||
"copy-card-link-to-clipboard": "დააკოპირეთ ბარათის ბმული clipboard-ზე",
|
||||
"copy-link-to-clipboard": "დააკოპირეთ ბმული clipboard-ზე",
|
||||
"copy-text-to-clipboard": "Скопировать текст в буфер обмена",
|
||||
"linkCardPopup-title": "Карточка-ссылка",
|
||||
"searchElementPopup-title": "ძებნა",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "លុបបញ្ជីត្រួតពិនិត្យ?",
|
||||
"checklistItemDeletePopup-title": "លុបធាតុបញ្ជីត្រួតពិនិត្យ?",
|
||||
"copy-card-link-to-clipboard": "ចម្លងតំណកាតទៅក្ដារតម្បៀតខ្ទាស់",
|
||||
"copy-link-to-clipboard": "ចម្លងតំណទៅក្ដារតម្បៀតខ្ទាស់",
|
||||
"copy-text-to-clipboard": "ចម្លងអត្ថបទទៅក្ដារតម្បៀតខ្ទាស់",
|
||||
"linkCardPopup-title": "កាតភ្ជាប់",
|
||||
"searchElementPopup-title": "ស្វែងរក",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "លុបបញ្ជីត្រួតពិនិត្យ?",
|
||||
"checklistItemDeletePopup-title": "លុបធាតុបញ្ជីត្រួតពិនិត្យ?",
|
||||
"copy-card-link-to-clipboard": "ចម្លងតំណកាតទៅក្ដារតម្បៀតខ្ទាស់",
|
||||
"copy-link-to-clipboard": "ចម្លងតំណទៅក្ដារតម្បៀតខ្ទាស់",
|
||||
"copy-text-to-clipboard": "ចម្លងអត្ថបទទៅក្ដារតម្បៀតខ្ទាស់",
|
||||
"linkCardPopup-title": "កាតភ្ជាប់",
|
||||
"searchElementPopup-title": "ស្វែងរក",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "체크리스트를 삭제합니까?",
|
||||
"checklistItemDeletePopup-title": "チェックリスト項目を削除しますか?",
|
||||
"copy-card-link-to-clipboard": "클립보드에 카드의 링크가 복사되었습니다.",
|
||||
"copy-link-to-clipboard": "링크를 클립보드에 복사합니다",
|
||||
"copy-text-to-clipboard": "텍스트를 클립보드에 복사합니다",
|
||||
"linkCardPopup-title": "카드 연결",
|
||||
"searchElementPopup-title": "검색",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "체크리스트를 삭제합니까?",
|
||||
"checklistItemDeletePopup-title": "チェックリスト項目を削除しますか?",
|
||||
"copy-card-link-to-clipboard": "클립보드에 카드의 링크가 복사되었습니다.",
|
||||
"copy-link-to-clipboard": "링크를 클립보드에 복사합니다",
|
||||
"copy-text-to-clipboard": "텍스트를 클립보드에 복사합니다",
|
||||
"linkCardPopup-title": "카드 연결",
|
||||
"searchElementPopup-title": "검색",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Ištrinti kontrolinį sąrašą?",
|
||||
"checklistItemDeletePopup-title": "Ištrinti kontrolinio sąrašo elementą?",
|
||||
"copy-card-link-to-clipboard": "Nukopijuokite kortelės nuorodą į mainų sritį",
|
||||
"copy-link-to-clipboard": "Nukopijuokite nuorodą į mainų sritį",
|
||||
"copy-text-to-clipboard": "Nukopijuokite tekstą į mainų sritį",
|
||||
"linkCardPopup-title": "Nuorodų kortelė",
|
||||
"searchElementPopup-title": "Ieškoti",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Ištrinti kontrolinį sąrašą?",
|
||||
"checklistItemDeletePopup-title": "Ištrinti kontrolinio sąrašo elementą?",
|
||||
"copy-card-link-to-clipboard": "Kopet kartiņas saiti starpliktuvē",
|
||||
"copy-link-to-clipboard": "Kopēt saiti starpliktuvē",
|
||||
"copy-text-to-clipboard": "Nukopijuokite tekstą į mainų sritį",
|
||||
"linkCardPopup-title": "Kartiņas saite",
|
||||
"searchElementPopup-title": "Meklēt",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Избрисаћете предметну радњу?",
|
||||
"checklistItemDeletePopup-title": "Избрисаћете ставку из предметне радње?",
|
||||
"copy-card-link-to-clipboard": "Копирай връзката на картата в клипборда",
|
||||
"copy-link-to-clipboard": "Copy link to clipboard",
|
||||
"copy-text-to-clipboard": "Причувај текст на кратко",
|
||||
"linkCardPopup-title": "Поврзи картичка",
|
||||
"searchElementPopup-title": "Барај",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Удалить Чек-лист?",
|
||||
"checklistItemDeletePopup-title": "Чеклистийн зүйлийг устгах уу?",
|
||||
"copy-card-link-to-clipboard": "Копировать ссылку на карточку в буфер обмена",
|
||||
"copy-link-to-clipboard": "Копировать ссылку в буфер обмена",
|
||||
"copy-text-to-clipboard": "Скопировать текст в буфер обмена",
|
||||
"linkCardPopup-title": "Карточка-ссылка",
|
||||
"searchElementPopup-title": "Поиск",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Hapus Senarai semak?",
|
||||
"checklistItemDeletePopup-title": "Hapus item senarai semak?",
|
||||
"copy-card-link-to-clipboard": "Salin capaian kad ke papan klip",
|
||||
"copy-link-to-clipboard": "Salin capaian ke papan klip",
|
||||
"copy-text-to-clipboard": "Salin teks ke papan klip",
|
||||
"linkCardPopup-title": "Paut Kad",
|
||||
"searchElementPopup-title": "Cari",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Hapus Senarai semak?",
|
||||
"checklistItemDeletePopup-title": "Hapus item senarai semak?",
|
||||
"copy-card-link-to-clipboard": "Salin capaian kad ke papan klip",
|
||||
"copy-link-to-clipboard": "Salin capaian ke papan klip",
|
||||
"copy-text-to-clipboard": "Salin teks ke papan klip",
|
||||
"linkCardPopup-title": "Paut Kad",
|
||||
"searchElementPopup-title": "Cari",
|
||||
|
||||
@@ -465,6 +465,7 @@
|
||||
"checklistDeletePopup-title": "Slette sjekklisten?",
|
||||
"checklistItemDeletePopup-title": "Slette sjekklisteelement?",
|
||||
"copy-card-link-to-clipboard": "Kopier lenke",
|
||||
"copy-link-to-clipboard": "Kopier lenke til utklippstavlen",
|
||||
"copy-text-to-clipboard": "Kopier tekst til utklippstavlen",
|
||||
"linkCardPopup-title": "Link Kort",
|
||||
"searchElementPopup-title": "Søk",
|
||||
|
||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user