filter.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Filtered view manager
  2. // We define local filter objects for each different type of field (SetFilter,
  3. // RangeFilter, dateFilter, etc.). We then define a global `Filter` object whose
  4. // goal is to filter complete documents by using the local filters for each
  5. // fields.
  6. function showFilterSidebar() {
  7. Sidebar.setView('filter');
  8. }
  9. // Use a "set" filter for a field that is a set of documents uniquely
  10. // identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
  11. class SetFilter {
  12. constructor() {
  13. this._dep = new Tracker.Dependency();
  14. this._selectedElements = [];
  15. }
  16. isSelected(val) {
  17. this._dep.depend();
  18. return this._selectedElements.indexOf(val) > -1;
  19. }
  20. add(val) {
  21. if (this._indexOfVal(val) === -1) {
  22. this._selectedElements.push(val);
  23. this._dep.changed();
  24. showFilterSidebar();
  25. }
  26. }
  27. remove(val) {
  28. const indexOfVal = this._indexOfVal(val);
  29. if (this._indexOfVal(val) !== -1) {
  30. this._selectedElements.splice(indexOfVal, 1);
  31. this._dep.changed();
  32. }
  33. }
  34. toogle(val) {
  35. if (this._indexOfVal(val) === -1) {
  36. this.add(val);
  37. } else {
  38. this.remove(val);
  39. }
  40. }
  41. reset() {
  42. this._selectedElements = [];
  43. this._dep.changed();
  44. }
  45. _indexOfVal(val) {
  46. return this._selectedElements.indexOf(val);
  47. }
  48. _isActive() {
  49. this._dep.depend();
  50. return this._selectedElements.length !== 0;
  51. }
  52. _getMongoSelector() {
  53. this._dep.depend();
  54. return { $in: this._selectedElements };
  55. }
  56. }
  57. // The global Filter object.
  58. // XXX It would be possible to re-write this object more elegantly, and removing
  59. // the need to provide a list of `_fields`. We also should move methods into the
  60. // object prototype.
  61. Filter = {
  62. // XXX I would like to rename this field into `labels` to be consistent with
  63. // the rest of the schema, but we need to set some migrations architecture
  64. // before changing the schema.
  65. labelIds: new SetFilter(),
  66. members: new SetFilter(),
  67. _fields: ['labelIds', 'members'],
  68. // We don't filter cards that have been added after the last filter change. To
  69. // implement this we keep the id of these cards in this `_exceptions` fields
  70. // and use a `$or` condition in the mongo selector we return.
  71. _exceptions: [],
  72. _exceptionsDep: new Tracker.Dependency(),
  73. isActive() {
  74. return _.any(this._fields, (fieldName) => {
  75. return this[fieldName]._isActive();
  76. });
  77. },
  78. _getMongoSelector() {
  79. if (!this.isActive())
  80. return {};
  81. const filterSelector = {};
  82. _.forEach(this._fields, (fieldName) => {
  83. const filter = this[fieldName];
  84. if (filter._isActive())
  85. filterSelector[fieldName] = filter._getMongoSelector();
  86. });
  87. const exceptionsSelector = {_id: {$in: this._exceptions}};
  88. this._exceptionsDep.depend();
  89. return {$or: [filterSelector, exceptionsSelector]};
  90. },
  91. mongoSelector(additionalSelector) {
  92. const filterSelector = this._getMongoSelector();
  93. if (_.isUndefined(additionalSelector))
  94. return filterSelector;
  95. else
  96. return {$and: [filterSelector, additionalSelector]};
  97. },
  98. reset() {
  99. _.forEach(this._fields, (fieldName) => {
  100. const filter = this[fieldName];
  101. filter.reset();
  102. });
  103. this.resetExceptions();
  104. },
  105. addException(_id) {
  106. if (this.isActive()) {
  107. this._exceptions.push(_id);
  108. this._exceptionsDep.changed();
  109. Tracker.flush();
  110. }
  111. },
  112. resetExceptions() {
  113. this._exceptions = [];
  114. this._exceptionsDep.changed();
  115. },
  116. };
  117. Blaze.registerHelper('Filter', Filter);