mirror of
https://github.com/wekan/wekan.git
synced 2026-03-14 01:16:13 +01:00
Migrate rules, activities, and remaining components to Template
Convert all remaining BlazeComponent-based components to native Meteor Template pattern: activities, comments, all rules actions/triggers, swimlanes, users, gantt, import, and main utility components.
This commit is contained in:
parent
bae23f9ed8
commit
477e1c89e5
29 changed files with 2859 additions and 2894 deletions
|
|
@ -18,21 +18,19 @@ const accessibilityHelpers = {
|
|||
};
|
||||
|
||||
// Main accessibility page component
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
Template.accessibility.onCreated(function () {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('accessibilitySettings');
|
||||
},
|
||||
...accessibilityHelpers
|
||||
}).register('accessibility');
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('accessibilitySettings');
|
||||
});
|
||||
|
||||
Template.accessibility.helpers(accessibilityHelpers);
|
||||
|
||||
// Header bar component
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('accessibilitySettings');
|
||||
},
|
||||
...accessibilityHelpers
|
||||
}).register('accessibilityHeaderBar');
|
||||
Template.accessibilityHeaderBar.onCreated(function () {
|
||||
Meteor.subscribe('accessibilitySettings');
|
||||
});
|
||||
|
||||
Template.accessibilityHeaderBar.helpers(accessibilityHelpers);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,57 @@
|
|||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import { CardSearchPaged } from '../../lib/cardSearch';
|
||||
|
||||
BlazeComponent.extendComponent({}).register('brokenCardsHeaderBar');
|
||||
Template.brokenCards.onCreated(function () {
|
||||
const search = new CardSearchPaged(this);
|
||||
this.search = search;
|
||||
|
||||
Meteor.subscribe('brokenCards', search.sessionId);
|
||||
});
|
||||
|
||||
Template.brokenCards.helpers({
|
||||
userId() {
|
||||
return Meteor.userId();
|
||||
},
|
||||
|
||||
// Return ReactiveVars so jade can use .get pattern
|
||||
searching() {
|
||||
return Template.instance().search.searching;
|
||||
},
|
||||
hasResults() {
|
||||
return Template.instance().search.hasResults;
|
||||
},
|
||||
hasQueryErrors() {
|
||||
return Template.instance().search.hasQueryErrors;
|
||||
},
|
||||
errorMessages() {
|
||||
return Template.instance().search.queryErrorMessages();
|
||||
},
|
||||
resultsCount() {
|
||||
return Template.instance().search.resultsCount;
|
||||
},
|
||||
resultsHeading() {
|
||||
return Template.instance().search.resultsHeading;
|
||||
},
|
||||
results() {
|
||||
return Template.instance().search.results;
|
||||
},
|
||||
getSearchHref() {
|
||||
return Template.instance().search.getSearchHref();
|
||||
},
|
||||
hasPreviousPage() {
|
||||
return Template.instance().search.hasPreviousPage;
|
||||
},
|
||||
hasNextPage() {
|
||||
return Template.instance().search.hasNextPage;
|
||||
},
|
||||
});
|
||||
|
||||
class BrokenCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
Meteor.subscribe('brokenCards', this.sessionId);
|
||||
}
|
||||
}
|
||||
BrokenCardsComponent.register('brokenCards');
|
||||
Template.brokenCards.events({
|
||||
'click .js-next-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.search.nextPage();
|
||||
},
|
||||
'click .js-previous-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.search.previousPage();
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,178 +1,37 @@
|
|||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { BlazeComponent } from 'meteor/peerlibrary:blaze-components';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
Template.dueCardsHeaderBar.helpers({
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('sort:', Utils.dueCardsView());
|
||||
return Utils && Utils.dueCardsView ? Utils.dueCardsView() : 'me';
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-due-cards-view-change': Popup.open('dueCardsViewChange'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('dueCardsHeaderBar');
|
||||
|
||||
Template.dueCards.helpers({
|
||||
userId() {
|
||||
return Meteor.userId();
|
||||
},
|
||||
dueCardsList() {
|
||||
const component = BlazeComponent.getComponentForElement(this.firstNode);
|
||||
if (component && component.dueCardsList) {
|
||||
return component.dueCardsList();
|
||||
}
|
||||
return [];
|
||||
},
|
||||
hasResults() {
|
||||
const component = BlazeComponent.getComponentForElement(this.firstNode);
|
||||
if (component && component.hasResults) {
|
||||
return component.hasResults.get();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
searching() {
|
||||
const component = BlazeComponent.getComponentForElement(this.firstNode);
|
||||
if (component && component.isLoading) {
|
||||
return component.isLoading.get();
|
||||
}
|
||||
return true; // Show loading by default
|
||||
},
|
||||
hasQueryErrors() {
|
||||
return false; // No longer using search, so always false
|
||||
},
|
||||
errorMessages() {
|
||||
return []; // No longer using search, so always empty
|
||||
},
|
||||
cardsCount() {
|
||||
const component = BlazeComponent.getComponentForElement(this.firstNode);
|
||||
if (component && component.cardsCount) {
|
||||
return component.cardsCount();
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
resultsText() {
|
||||
const component = BlazeComponent.getComponentForElement(this.firstNode);
|
||||
if (component && component.resultsText) {
|
||||
return component.resultsText();
|
||||
}
|
||||
return '';
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-due-cards-view-me'() {
|
||||
if (Utils && Utils.setDueCardsView) {
|
||||
Utils.setDueCardsView('me');
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
Template.dueCardsHeaderBar.events({
|
||||
'click .js-due-cards-view-change': Popup.open('dueCardsViewChange'),
|
||||
});
|
||||
|
||||
'click .js-due-cards-view-all'() {
|
||||
if (Utils && Utils.setDueCardsView) {
|
||||
Utils.setDueCardsView('all');
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('dueCardsViewChangePopup');
|
||||
Template.dueCards.onCreated(function () {
|
||||
this._cachedCards = null;
|
||||
this._cachedTimestamp = null;
|
||||
this.subscriptionHandle = null;
|
||||
this.isLoading = new ReactiveVar(true);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.searching = new ReactiveVar(false);
|
||||
|
||||
class DueCardsComponent extends BlazeComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
const tpl = this;
|
||||
|
||||
this._cachedCards = null;
|
||||
this._cachedTimestamp = null;
|
||||
this.subscriptionHandle = null;
|
||||
this.isLoading = new ReactiveVar(true);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.searching = new ReactiveVar(false);
|
||||
|
||||
// Subscribe to the optimized due cards publication
|
||||
this.autorun(() => {
|
||||
const allUsers = this.dueCardsView() === 'all';
|
||||
if (this.subscriptionHandle) {
|
||||
this.subscriptionHandle.stop();
|
||||
}
|
||||
this.subscriptionHandle = Meteor.subscribe('dueCards', allUsers);
|
||||
|
||||
// Update loading state based on subscription
|
||||
this.autorun(() => {
|
||||
if (this.subscriptionHandle && this.subscriptionHandle.ready()) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards: subscription ready, loading data...');
|
||||
}
|
||||
this.isLoading.set(false);
|
||||
const cards = this.dueCardsList();
|
||||
this.hasResults.set(cards && cards.length > 0);
|
||||
} else {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards: subscription not ready, showing loading...');
|
||||
}
|
||||
this.isLoading.set(true);
|
||||
this.hasResults.set(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onDestroyed() {
|
||||
super.onDestroyed();
|
||||
if (this.subscriptionHandle) {
|
||||
this.subscriptionHandle.stop();
|
||||
}
|
||||
}
|
||||
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('sort:', Utils.dueCardsView());
|
||||
function dueCardsView() {
|
||||
return Utils && Utils.dueCardsView ? Utils.dueCardsView() : 'me';
|
||||
}
|
||||
|
||||
sortByBoard() {
|
||||
return this.dueCardsView() === 'board';
|
||||
}
|
||||
|
||||
hasResults() {
|
||||
return this.hasResults.get();
|
||||
}
|
||||
|
||||
cardsCount() {
|
||||
const cards = this.dueCardsList();
|
||||
return cards ? cards.length : 0;
|
||||
}
|
||||
|
||||
resultsText() {
|
||||
const count = this.cardsCount();
|
||||
if (count === 1) {
|
||||
return TAPi18n.__('one-card-found');
|
||||
} else {
|
||||
// Get the translated text and manually replace %s with the count
|
||||
const baseText = TAPi18n.__('n-cards-found');
|
||||
const result = baseText.replace('%s', count);
|
||||
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards: base text:', baseText, 'count:', count, 'result:', result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
dueCardsList() {
|
||||
function dueCardsList() {
|
||||
// Check if subscription is ready
|
||||
if (!this.subscriptionHandle || !this.subscriptionHandle.ready()) {
|
||||
if (!tpl.subscriptionHandle || !tpl.subscriptionHandle.ready()) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards client: subscription not ready');
|
||||
}
|
||||
|
|
@ -180,11 +39,11 @@ class DueCardsComponent extends BlazeComponent {
|
|||
}
|
||||
|
||||
// Use cached results if available to avoid expensive re-sorting
|
||||
if (this._cachedCards && this._cachedTimestamp && (Date.now() - this._cachedTimestamp < 5000)) {
|
||||
if (tpl._cachedCards && tpl._cachedTimestamp && (Date.now() - tpl._cachedTimestamp < 5000)) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards client: using cached results,', this._cachedCards.length, 'cards');
|
||||
console.log('dueCards client: using cached results,', tpl._cachedCards.length, 'cards');
|
||||
}
|
||||
return this._cachedCards;
|
||||
return tpl._cachedCards;
|
||||
}
|
||||
|
||||
// Get cards directly from the subscription (already sorted by the publication)
|
||||
|
|
@ -208,7 +67,7 @@ class DueCardsComponent extends BlazeComponent {
|
|||
}
|
||||
|
||||
// Filter cards based on user view preference
|
||||
const allUsers = this.dueCardsView() === 'all';
|
||||
const allUsers = dueCardsView() === 'all';
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
let filteredCards = cards;
|
||||
|
||||
|
|
@ -255,15 +114,106 @@ class DueCardsComponent extends BlazeComponent {
|
|||
}
|
||||
|
||||
// Cache the results for 5 seconds to avoid re-filtering on every render
|
||||
this._cachedCards = filteredCards;
|
||||
this._cachedTimestamp = Date.now();
|
||||
tpl._cachedCards = filteredCards;
|
||||
tpl._cachedTimestamp = Date.now();
|
||||
|
||||
// Update reactive variables
|
||||
this.hasResults.set(filteredCards && filteredCards.length > 0);
|
||||
this.isLoading.set(false);
|
||||
tpl.hasResults.set(filteredCards && filteredCards.length > 0);
|
||||
tpl.isLoading.set(false);
|
||||
|
||||
return filteredCards;
|
||||
}
|
||||
}
|
||||
|
||||
DueCardsComponent.register('dueCards');
|
||||
// Store dueCardsList on the instance so helpers can call it
|
||||
this.dueCardsList = dueCardsList;
|
||||
this.dueCardsView = dueCardsView;
|
||||
|
||||
// Subscribe to the optimized due cards publication
|
||||
this.autorun(() => {
|
||||
const allUsers = dueCardsView() === 'all';
|
||||
if (tpl.subscriptionHandle) {
|
||||
tpl.subscriptionHandle.stop();
|
||||
}
|
||||
tpl.subscriptionHandle = Meteor.subscribe('dueCards', allUsers);
|
||||
|
||||
// Update loading state based on subscription
|
||||
tpl.autorun(() => {
|
||||
if (tpl.subscriptionHandle && tpl.subscriptionHandle.ready()) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards: subscription ready, loading data...');
|
||||
}
|
||||
tpl.isLoading.set(false);
|
||||
const cards = dueCardsList();
|
||||
tpl.hasResults.set(cards && cards.length > 0);
|
||||
} else {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards: subscription not ready, showing loading...');
|
||||
}
|
||||
tpl.isLoading.set(true);
|
||||
tpl.hasResults.set(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Template.dueCards.onDestroyed(function () {
|
||||
if (this.subscriptionHandle) {
|
||||
this.subscriptionHandle.stop();
|
||||
}
|
||||
});
|
||||
|
||||
Template.dueCards.helpers({
|
||||
userId() {
|
||||
return Meteor.userId();
|
||||
},
|
||||
// Return ReactiveVar so jade can use .get pattern
|
||||
searching() {
|
||||
return Template.instance().isLoading;
|
||||
},
|
||||
hasResults() {
|
||||
return Template.instance().hasResults;
|
||||
},
|
||||
hasQueryErrors() {
|
||||
return new ReactiveVar(false);
|
||||
},
|
||||
errorMessages() {
|
||||
return [];
|
||||
},
|
||||
dueCardsList() {
|
||||
const tpl = Template.instance();
|
||||
return tpl.dueCardsList ? tpl.dueCardsList() : [];
|
||||
},
|
||||
resultsText() {
|
||||
const tpl = Template.instance();
|
||||
const cards = tpl.dueCardsList ? tpl.dueCardsList() : [];
|
||||
const count = cards ? cards.length : 0;
|
||||
if (count === 1) {
|
||||
return TAPi18n.__('one-card-found');
|
||||
} else {
|
||||
// Get the translated text and manually replace %s with the count
|
||||
const baseText = TAPi18n.__('n-cards-found');
|
||||
const result = baseText.replace('%s', count);
|
||||
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards: base text:', baseText, 'count:', count, 'result:', result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.dueCardsViewChangePopup.events({
|
||||
'click .js-due-cards-view-me'() {
|
||||
if (Utils && Utils.setDueCardsView) {
|
||||
Utils.setDueCardsView('me');
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
'click .js-due-cards-view-all'() {
|
||||
if (Utils && Utils.setDueCardsView) {
|
||||
Utils.setDueCardsView('all');
|
||||
}
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ const boardSpecialHandles = [
|
|||
const specialHandleNames = specialHandles.map(m => m.username);
|
||||
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onRendered() {
|
||||
Template.editor.onRendered(function () {
|
||||
const tpl = this;
|
||||
// Start: Copy <pre> code https://github.com/wekan/wekan/issues/5149
|
||||
// TODO: Try to make copyPre visible at Card Details after editing or closing editor or Card Details.
|
||||
// - Also this same TODO below at event, if someone gets it working.
|
||||
|
|
@ -89,7 +89,7 @@ BlazeComponent.extendComponent({
|
|||
];
|
||||
|
||||
const enableTextarea = function() {
|
||||
const $textarea = this.$(textareaSelector);
|
||||
const $textarea = tpl.$(textareaSelector);
|
||||
autosize($textarea);
|
||||
$textarea.escapeableTextComplete(mentions);
|
||||
};
|
||||
|
|
@ -314,29 +314,25 @@ BlazeComponent.extendComponent({
|
|||
enableTextarea();
|
||||
}
|
||||
enableTextarea();
|
||||
},
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click a.fa.fa-copy'(event) {
|
||||
const $editor = this.$('textarea.editor');
|
||||
const promise = Utils.copyTextToClipboard($editor[0].value);
|
||||
});
|
||||
|
||||
const $tooltip = this.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'click a.fa.fa-brands.fa-markdown'(event) {
|
||||
const $editor = this.$('textarea.editor');
|
||||
$editor[0].value = converter.convert($editor[0].value);
|
||||
},
|
||||
// TODO: Try to make copyPre visible at Card Details after editing or closing editor or Card Details.
|
||||
//'click .js-close-inlined-form'(event) {
|
||||
// Utils.copyPre();
|
||||
//},
|
||||
}
|
||||
]
|
||||
}
|
||||
}).register('editor');
|
||||
Template.editor.events({
|
||||
'click a.fa.fa-copy'(event, tpl) {
|
||||
const $editor = tpl.$('textarea.editor');
|
||||
const promise = Utils.copyTextToClipboard($editor[0].value);
|
||||
|
||||
const $tooltip = tpl.$('.copied-tooltip');
|
||||
Utils.showCopied(promise, $tooltip);
|
||||
},
|
||||
'click a.fa.fa-brands.fa-markdown'(event, tpl) {
|
||||
const $editor = tpl.$('textarea.editor');
|
||||
$editor[0].value = converter.convert($editor[0].value);
|
||||
},
|
||||
// TODO: Try to make copyPre visible at Card Details after editing or closing editor or Card Details.
|
||||
//'click .js-close-inlined-form'(event) {
|
||||
// Utils.copyPre();
|
||||
//},
|
||||
});
|
||||
|
||||
import DOMPurify from 'dompurify';
|
||||
import { sanitizeHTML } from '/imports/lib/secureDOMPurify';
|
||||
|
|
|
|||
|
|
@ -1,114 +1,154 @@
|
|||
import { TAPi18n } from '/imports/i18n';
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import { CardSearchPaged } from '../../lib/cardSearch';
|
||||
import Boards from '../../../models/boards';
|
||||
import { Query, QueryErrors } from '../../../config/query-classes';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-due-cards-view-change': Popup.open('globalSearchViewChange'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('globalSearchHeaderBar');
|
||||
Template.globalSearchHeaderBar.events({
|
||||
'click .js-due-cards-view-change': Popup.open('globalSearchViewChange'),
|
||||
});
|
||||
|
||||
Template.globalSearch.onCreated(function () {
|
||||
const search = new CardSearchPaged(this);
|
||||
this.search = search;
|
||||
|
||||
this.myLists = new ReactiveVar([]);
|
||||
this.myLabelNames = new ReactiveVar([]);
|
||||
this.myBoardNames = new ReactiveVar([]);
|
||||
this.parsingErrors = new QueryErrors();
|
||||
this.queryParams = null;
|
||||
|
||||
Meteor.call('myLists', (err, data) => {
|
||||
if (!err) {
|
||||
this.myLists.set(data);
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('myLabelNames', (err, data) => {
|
||||
if (!err) {
|
||||
this.myLabelNames.set(data);
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('myBoardNames', (err, data) => {
|
||||
if (!err) {
|
||||
this.myBoardNames.set(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.globalSearch.onRendered(function () {
|
||||
Meteor.subscribe('setting');
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('lang:', TAPi18n.getLanguage());
|
||||
|
||||
if (Session.get('globalQuery')) {
|
||||
searchAllBoards(this, Session.get('globalQuery'));
|
||||
}
|
||||
});
|
||||
|
||||
function searchAllBoards(tpl, queryText) {
|
||||
const search = tpl.search;
|
||||
|
||||
queryText = queryText.trim();
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('queryText:', queryText);
|
||||
|
||||
search.query.set(queryText);
|
||||
|
||||
search.resetSearch();
|
||||
tpl.parsingErrors = new QueryErrors();
|
||||
|
||||
if (!queryText) {
|
||||
return;
|
||||
}
|
||||
|
||||
search.searching.set(true);
|
||||
|
||||
const query = new Query();
|
||||
query.buildParams(queryText);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', query.getParams());
|
||||
|
||||
tpl.queryParams = query.getQueryParams().getParams();
|
||||
|
||||
if (query.hasErrors()) {
|
||||
search.searching.set(false);
|
||||
search.queryErrors = query.errors();
|
||||
search.hasResults.set(true);
|
||||
search.hasQueryErrors.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
search.runGlobalSearch(query.getQueryParams());
|
||||
}
|
||||
|
||||
function errorMessages(tpl) {
|
||||
if (tpl.parsingErrors.hasErrors()) {
|
||||
return tpl.parsingErrors.errorMessages();
|
||||
}
|
||||
return tpl.search.queryErrorMessages();
|
||||
}
|
||||
|
||||
Template.globalSearch.helpers({
|
||||
userId() {
|
||||
return Meteor.userId();
|
||||
},
|
||||
});
|
||||
|
||||
class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
this.myLists = new ReactiveVar([]);
|
||||
this.myLabelNames = new ReactiveVar([]);
|
||||
this.myBoardNames = new ReactiveVar([]);
|
||||
this.parsingErrors = new QueryErrors();
|
||||
this.queryParams = null;
|
||||
// Return ReactiveVars so jade can use .get pattern
|
||||
searching() {
|
||||
return Template.instance().search.searching;
|
||||
},
|
||||
hasResults() {
|
||||
return Template.instance().search.hasResults;
|
||||
},
|
||||
hasQueryErrors() {
|
||||
return Template.instance().search.hasQueryErrors;
|
||||
},
|
||||
serverError() {
|
||||
return Template.instance().search.serverError;
|
||||
},
|
||||
query() {
|
||||
return Template.instance().search.query;
|
||||
},
|
||||
debug() {
|
||||
return Template.instance().search.debug;
|
||||
},
|
||||
resultsHeading() {
|
||||
return Template.instance().search.resultsHeading;
|
||||
},
|
||||
results() {
|
||||
return Template.instance().search.results;
|
||||
},
|
||||
hasNextPage() {
|
||||
return Template.instance().search.hasNextPage;
|
||||
},
|
||||
hasPreviousPage() {
|
||||
return Template.instance().search.hasPreviousPage;
|
||||
},
|
||||
sessionData() {
|
||||
return Template.instance().search.sessionData;
|
||||
},
|
||||
getSearchHref() {
|
||||
return Template.instance().search.getSearchHref();
|
||||
},
|
||||
|
||||
Meteor.call('myLists', (err, data) => {
|
||||
if (!err) {
|
||||
this.myLists.set(data);
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('myLabelNames', (err, data) => {
|
||||
if (!err) {
|
||||
this.myLabelNames.set(data);
|
||||
}
|
||||
});
|
||||
|
||||
Meteor.call('myBoardNames', (err, data) => {
|
||||
if (!err) {
|
||||
this.myBoardNames.set(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
Meteor.subscribe('setting');
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('lang:', TAPi18n.getLanguage());
|
||||
|
||||
if (Session.get('globalQuery')) {
|
||||
this.searchAllBoards(Session.get('globalQuery'));
|
||||
}
|
||||
}
|
||||
|
||||
resetSearch() {
|
||||
super.resetSearch();
|
||||
this.parsingErrors = new QueryErrors();
|
||||
}
|
||||
myLists() {
|
||||
return Template.instance().myLists;
|
||||
},
|
||||
myLabelNames() {
|
||||
return Template.instance().myLabelNames;
|
||||
},
|
||||
myBoardNames() {
|
||||
return Template.instance().myBoardNames;
|
||||
},
|
||||
|
||||
errorMessages() {
|
||||
if (this.parsingErrors.hasErrors()) {
|
||||
return this.parsingErrors.errorMessages();
|
||||
}
|
||||
return this.queryErrorMessages();
|
||||
}
|
||||
|
||||
parsingErrorMessages() {
|
||||
this.parsingErrors.errorMessages();
|
||||
}
|
||||
|
||||
searchAllBoards(queryText) {
|
||||
queryText = queryText.trim();
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('queryText:', queryText);
|
||||
|
||||
this.query.set(queryText);
|
||||
|
||||
this.resetSearch();
|
||||
|
||||
if (!queryText) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.searching.set(true);
|
||||
|
||||
const query = new Query();
|
||||
query.buildParams(queryText);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('params:', query.getParams());
|
||||
|
||||
this.queryParams = query.getQueryParams().getParams();
|
||||
|
||||
if (query.hasErrors()) {
|
||||
this.searching.set(false);
|
||||
this.queryErrors = query.errors();
|
||||
this.hasResults.set(true);
|
||||
this.hasQueryErrors.set(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.runGlobalSearch(query.getQueryParams());
|
||||
}
|
||||
return errorMessages(Template.instance());
|
||||
},
|
||||
|
||||
searchInstructions() {
|
||||
const tags = {
|
||||
|
|
@ -203,7 +243,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
|||
.replace(/\>\*/, '\>\`')
|
||||
});
|
||||
return text;
|
||||
}
|
||||
},
|
||||
|
||||
labelColors() {
|
||||
return Boards.simpleSchema()._schema['labels.$.color'].allowedValues.map(
|
||||
|
|
@ -211,89 +251,163 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
|
|||
return { color, name: TAPi18n.__(`color-${color}`) };
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
events() {
|
||||
return super.events().concat([
|
||||
{
|
||||
'submit .js-search-query-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.searchAllBoards(evt.target.searchQuery.value);
|
||||
},
|
||||
'click .js-label-color'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-label')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-copy-debug-selector'(evt) {
|
||||
/* Get the text field */
|
||||
const selector = document.getElementById("debug-selector");
|
||||
Template.globalSearch.events({
|
||||
'submit .js-search-query-form'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
searchAllBoards(tpl, evt.target.searchQuery.value);
|
||||
},
|
||||
'click .js-label-color'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
tpl.search.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-label')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-copy-debug-selector'(evt) {
|
||||
/* Get the text field */
|
||||
const selector = document.getElementById("debug-selector");
|
||||
|
||||
try {
|
||||
navigator.clipboard.writeText(selector.textContent);
|
||||
alert("Selector copied to clipboard");
|
||||
} catch(err) {
|
||||
alert("Error copying text: " + err);
|
||||
}
|
||||
try {
|
||||
navigator.clipboard.writeText(selector.textContent);
|
||||
alert("Selector copied to clipboard");
|
||||
} catch(err) {
|
||||
alert("Error copying text: " + err);
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-copy-debug-projection'(evt) {
|
||||
/* Get the text field */
|
||||
const projection = document.getElementById("debug-projection");
|
||||
},
|
||||
'click .js-copy-debug-projection'(evt) {
|
||||
/* Get the text field */
|
||||
const projection = document.getElementById("debug-projection");
|
||||
|
||||
try {
|
||||
navigator.clipboard.writeText(projection.textContent);
|
||||
alert("Projection copied to clipboard");
|
||||
} catch(err) {
|
||||
alert("Error copying text: " + err);
|
||||
}
|
||||
try {
|
||||
navigator.clipboard.writeText(projection.textContent);
|
||||
alert("Projection copied to clipboard");
|
||||
} catch(err) {
|
||||
alert("Error copying text: " + err);
|
||||
}
|
||||
|
||||
},
|
||||
'click .js-board-title'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-board')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-list-title'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-list')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-label-name'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
this.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-label')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-new-search'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
input.value = '';
|
||||
this.query.set('');
|
||||
this.hasResults.set(false);
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
'click .js-board-title'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
tpl.search.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-board')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-list-title'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
tpl.search.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-list')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-label-name'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
tpl.search.query.set(
|
||||
`${input.value} ${TAPi18n.__('operator-label')}:"${
|
||||
evt.currentTarget.textContent
|
||||
}"`,
|
||||
);
|
||||
document.getElementById('global-search-input').focus();
|
||||
},
|
||||
'click .js-new-search'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
input.value = '';
|
||||
tpl.search.query.set('');
|
||||
tpl.search.hasResults.set(false);
|
||||
},
|
||||
'click .js-next-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.search.nextPage();
|
||||
},
|
||||
'click .js-previous-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.search.previousPage();
|
||||
},
|
||||
});
|
||||
|
||||
GlobalSearchComponent.register('globalSearch');
|
||||
// resultsPaged template helpers - this template is used by globalSearch, brokenCards, and brokenCardsReport.
|
||||
// It receives the parent's data context (which includes reactive vars) via +resultsPaged(this).
|
||||
// The jade accesses resultsHeading.get, results.get, getSearchHref, hasPreviousPage.get, hasNextPage.get
|
||||
// directly on the data context, so we don't need helpers for those when the parent passes
|
||||
// the right data context. However, we need to ensure the helpers exist for the template.
|
||||
Template.resultsPaged.helpers({
|
||||
resultsHeading() {
|
||||
const data = Template.currentData();
|
||||
if (data && data.resultsHeading) return data.resultsHeading;
|
||||
// fallback: check parent template
|
||||
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
|
||||
if (parentTpl && parentTpl.search) return parentTpl.search.resultsHeading;
|
||||
return new ReactiveVar('');
|
||||
},
|
||||
results() {
|
||||
const data = Template.currentData();
|
||||
if (data && data.results) return data.results;
|
||||
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
|
||||
if (parentTpl && parentTpl.search) return parentTpl.search.results;
|
||||
return new ReactiveVar([]);
|
||||
},
|
||||
getSearchHref() {
|
||||
const data = Template.currentData();
|
||||
if (data && data.getSearchHref) return data.getSearchHref();
|
||||
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
|
||||
if (parentTpl && parentTpl.search) return parentTpl.search.getSearchHref();
|
||||
return '';
|
||||
},
|
||||
hasPreviousPage() {
|
||||
const data = Template.currentData();
|
||||
if (data && data.hasPreviousPage) return data.hasPreviousPage;
|
||||
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
|
||||
if (parentTpl && parentTpl.search) return parentTpl.search.hasPreviousPage;
|
||||
return new ReactiveVar(false);
|
||||
},
|
||||
hasNextPage() {
|
||||
const data = Template.currentData();
|
||||
if (data && data.hasNextPage) return data.hasNextPage;
|
||||
const parentTpl = Template.instance().view.parentView.templateInstance && Template.instance().view.parentView.templateInstance();
|
||||
if (parentTpl && parentTpl.search) return parentTpl.search.hasNextPage;
|
||||
return new ReactiveVar(false);
|
||||
},
|
||||
});
|
||||
|
||||
Template.resultsPaged.events({
|
||||
'click .js-next-page'(evt) {
|
||||
evt.preventDefault();
|
||||
// Walk up to find the search instance
|
||||
let view = Template.instance().view;
|
||||
while (view) {
|
||||
const tplInst = view.templateInstance && view.templateInstance();
|
||||
if (tplInst && tplInst.search) {
|
||||
tplInst.search.nextPage();
|
||||
return;
|
||||
}
|
||||
view = view.parentView;
|
||||
}
|
||||
},
|
||||
'click .js-previous-page'(evt) {
|
||||
evt.preventDefault();
|
||||
let view = Template.instance().view;
|
||||
while (view) {
|
||||
const tplInst = view.templateInstance && view.templateInstance();
|
||||
if (tplInst && tplInst.search) {
|
||||
tplInst.search.previousPage();
|
||||
return;
|
||||
}
|
||||
view = view.parentView;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import { CardSearchPaged } from '../../lib/cardSearch';
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
Template.myCardsHeaderBar.helpers({
|
||||
myCardsSort() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('sort:', Utils.myCardsSort());
|
||||
|
|
@ -12,86 +12,69 @@ BlazeComponent.extendComponent({
|
|||
// console.log('sort:', Utils.myCardsView());
|
||||
return Utils.myCardsView();
|
||||
},
|
||||
});
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-toggle-my-cards-choose-sort': Popup.open(
|
||||
'myCardsSortChange',
|
||||
),
|
||||
'click .js-my-cards-view-change': Popup.open(
|
||||
'myCardsViewChange'),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('myCardsHeaderBar');
|
||||
Template.myCardsHeaderBar.events({
|
||||
'click .js-toggle-my-cards-choose-sort': Popup.open(
|
||||
'myCardsSortChange',
|
||||
),
|
||||
'click .js-my-cards-view-change': Popup.open(
|
||||
'myCardsViewChange'),
|
||||
});
|
||||
|
||||
Template.myCards.onCreated(function () {
|
||||
const search = new CardSearchPaged(this);
|
||||
this.search = search;
|
||||
|
||||
// Override getSubscription for myCards
|
||||
search.getSubscription = function (queryParams) {
|
||||
return Meteor.subscribe(
|
||||
'myCards',
|
||||
search.sessionId,
|
||||
search.subscriptionCallbacks,
|
||||
);
|
||||
};
|
||||
|
||||
search.runGlobalSearch(null);
|
||||
Meteor.subscribe('setting');
|
||||
});
|
||||
|
||||
Template.myCards.helpers({
|
||||
userId() {
|
||||
return Meteor.userId();
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-my-cards-view-boards'() {
|
||||
Utils.setMyCardsView('boards');
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
'click .js-my-cards-view-table'() {
|
||||
Utils.setMyCardsView('table');
|
||||
Popup.back();
|
||||
},
|
||||
},
|
||||
];
|
||||
// Return ReactiveVar so jade can use .get pattern
|
||||
searching() {
|
||||
return Template.instance().search.searching;
|
||||
},
|
||||
}).register('myCardsViewChangePopup');
|
||||
|
||||
class MyCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
this.runGlobalSearch(null);
|
||||
Meteor.subscribe('setting');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
getSubscription(queryParams) {
|
||||
return Meteor.subscribe(
|
||||
'myCards',
|
||||
this.sessionId,
|
||||
this.subscriptionCallbacks,
|
||||
);
|
||||
}
|
||||
|
||||
myCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('sort:', Utils.myCardsView());
|
||||
return Utils.myCardsView();
|
||||
}
|
||||
},
|
||||
|
||||
labelName(board, labelId) {
|
||||
const label = board.getLabelById(labelId)
|
||||
const name = label.name
|
||||
return name
|
||||
}
|
||||
const label = board.getLabelById(labelId);
|
||||
const name = label.name;
|
||||
return name;
|
||||
},
|
||||
|
||||
labelColor(board, labelId) {
|
||||
const label = board.getLabelById(labelId)
|
||||
const color = label.color
|
||||
return color
|
||||
}
|
||||
const label = board.getLabelById(labelId);
|
||||
const color = label.color;
|
||||
return color;
|
||||
},
|
||||
|
||||
myCardsList() {
|
||||
const search = Template.instance().search;
|
||||
const boards = [];
|
||||
let board = null;
|
||||
let swimlane = null;
|
||||
let list = null;
|
||||
|
||||
const cursor = this.getResults();
|
||||
const cursor = search.getResults();
|
||||
|
||||
if (cursor) {
|
||||
cursor.forEach(card => {
|
||||
|
|
@ -177,6 +160,28 @@ class MyCardsComponent extends CardSearchPagedComponent {
|
|||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
MyCardsComponent.register('myCards');
|
||||
},
|
||||
});
|
||||
|
||||
Template.myCards.events({
|
||||
'click .js-next-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.search.nextPage();
|
||||
},
|
||||
'click .js-previous-page'(evt, tpl) {
|
||||
evt.preventDefault();
|
||||
tpl.search.previousPage();
|
||||
},
|
||||
});
|
||||
|
||||
Template.myCardsViewChangePopup.events({
|
||||
'click .js-my-cards-view-boards'() {
|
||||
Utils.setMyCardsView('boards');
|
||||
Popup.back();
|
||||
},
|
||||
|
||||
'click .js-my-cards-view-table'() {
|
||||
Utils.setMyCardsView('table');
|
||||
Popup.back();
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Spinner } from '/client/lib/spinner';
|
||||
import { getSpinnerTemplate } from '/client/lib/spinner';
|
||||
|
||||
(class extends Spinner {
|
||||
}.register('spinner'));
|
||||
Template.spinner.helpers({
|
||||
getSpinnerTemplate() {
|
||||
return getSpinnerTemplate();
|
||||
},
|
||||
});
|
||||
|
||||
(class extends Spinner {
|
||||
Template.spinnerRaw.helpers({
|
||||
getSpinnerTemplateRaw() {
|
||||
let ret = super.getSpinnerTemplate() + 'Raw';
|
||||
return ret;
|
||||
}
|
||||
}.register('spinnerRaw'));
|
||||
return getSpinnerTemplate() + 'Raw';
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,20 +22,18 @@ const supportHelpers = {
|
|||
};
|
||||
|
||||
// Main support page component
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
Template.support.onCreated(function () {
|
||||
this.error = new ReactiveVar('');
|
||||
this.loading = new ReactiveVar(false);
|
||||
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
...supportHelpers
|
||||
}).register('support');
|
||||
Meteor.subscribe('setting');
|
||||
});
|
||||
|
||||
Template.support.helpers(supportHelpers);
|
||||
|
||||
// Header bar component
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
...supportHelpers
|
||||
}).register('supportHeaderBar');
|
||||
Template.supportHeaderBar.onCreated(function () {
|
||||
Meteor.subscribe('setting');
|
||||
});
|
||||
|
||||
Template.supportHeaderBar.helpers(supportHelpers);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue