selectedGroupStore.tsx 3.8 KB

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