selectedGroupStore.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import {createStore} from 'reflux';
  2. import GroupStore from 'sentry/stores/groupStore';
  3. import {makeSafeRefluxStore} from 'sentry/utils/makeSafeRefluxStore';
  4. import {CommonStoreDefinition} from './types';
  5. interface InternalDefinition {
  6. /**
  7. * The last item to have been selected
  8. */
  9. lastSelected: string | null;
  10. /**
  11. * Mapping of item ID -> if it is selected. This is a map to
  12. * make it easier to track if everything has been selected or not.
  13. */
  14. records: Map<string, boolean>;
  15. }
  16. interface SelectedGroupStoreDefinition
  17. extends CommonStoreDefinition<Map<string, boolean>>,
  18. InternalDefinition {
  19. add(ids: string[]): void;
  20. allSelected(): boolean;
  21. anySelected(): boolean;
  22. deselectAll(): void;
  23. getSelectedIds(): Set<string>;
  24. init(): void;
  25. isSelected(itemId: string): boolean;
  26. multiSelected(): boolean;
  27. onGroupChange(itemIds: Set<string>): void;
  28. prune(): void;
  29. reset(): void;
  30. shiftToggleItems(itemId: string): void;
  31. toggleSelect(itemId: string): void;
  32. toggleSelectAll(): void;
  33. }
  34. const storeConfig: SelectedGroupStoreDefinition = {
  35. records: new Map(),
  36. lastSelected: null,
  37. unsubscribeListeners: [],
  38. init() {
  39. this.reset();
  40. this.unsubscribeListeners.push(
  41. this.listenTo(GroupStore, this.onGroupChange, this.onGroupChange)
  42. );
  43. },
  44. reset() {
  45. this.records = new Map();
  46. this.lastSelected = null;
  47. },
  48. getState() {
  49. return this.records;
  50. },
  51. onGroupChange(itemIds) {
  52. this.prune();
  53. this.add([...itemIds]);
  54. this.trigger();
  55. },
  56. add(ids) {
  57. const allSelected = this.allSelected();
  58. ids
  59. .filter(id => !this.records.has(id))
  60. .forEach(id => this.records.set(id, allSelected));
  61. },
  62. prune() {
  63. const existingIds = new Set(GroupStore.getAllItemIds());
  64. this.lastSelected = null;
  65. // Remove everything that no longer exists
  66. [...this.records.keys()]
  67. .filter(id => !existingIds.has(id))
  68. .forEach(id => this.records.delete(id));
  69. },
  70. allSelected() {
  71. const itemIds = this.getSelectedIds();
  72. return itemIds.size > 0 && itemIds.size === this.records.size;
  73. },
  74. numSelected() {
  75. return this.getSelectedIds().size;
  76. },
  77. anySelected() {
  78. return this.getSelectedIds().size > 0;
  79. },
  80. multiSelected() {
  81. return this.getSelectedIds().size > 1;
  82. },
  83. getSelectedIds() {
  84. return new Set([...this.records.keys()].filter(id => this.records.get(id)));
  85. },
  86. isSelected(itemId) {
  87. return !!this.records.get(itemId);
  88. },
  89. deselectAll() {
  90. this.records.forEach((_, id) => this.records.set(id, false));
  91. this.trigger();
  92. },
  93. toggleSelect(itemId) {
  94. if (!this.records.has(itemId)) {
  95. return;
  96. }
  97. const newState = !this.records.get(itemId);
  98. this.records.set(itemId, newState);
  99. if (newState) {
  100. this.lastSelected = itemId;
  101. }
  102. this.trigger();
  103. },
  104. toggleSelectAll() {
  105. const allSelected = !this.allSelected();
  106. this.lastSelected = null;
  107. this.records.forEach((_, id) => this.records.set(id, allSelected));
  108. this.trigger();
  109. },
  110. shiftToggleItems(itemId) {
  111. if (!this.records.has(itemId)) {
  112. return;
  113. }
  114. if (!this.lastSelected) {
  115. this.toggleSelect(itemId);
  116. return;
  117. }
  118. const ids = GroupStore.getAllItemIds();
  119. const lastIdx = ids.findIndex(id => id === this.lastSelected);
  120. const currentIdx = ids.findIndex(id => id === itemId);
  121. if (lastIdx === -1 || currentIdx === -1) {
  122. return;
  123. }
  124. const newValue = !this.records.get(itemId);
  125. const selected =
  126. lastIdx < currentIdx
  127. ? ids.slice(lastIdx, currentIdx)
  128. : ids.slice(currentIdx, lastIdx);
  129. [...selected, this.lastSelected, itemId]
  130. .filter(id => this.records.has(id))
  131. .forEach(id => this.records.set(id, newValue));
  132. this.lastSelected = itemId;
  133. this.trigger();
  134. },
  135. };
  136. const SelectedGroupStore = createStore(makeSafeRefluxStore(storeConfig));
  137. export default SelectedGroupStore;