UserHotKeys.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <template>
  2. <div class="table col column no-wrap">
  3. <!-- header -->
  4. <div class="table-row row">
  5. <div class="desc q-pa-sm bg-blue-2">Команда</div>
  6. <div class="hotKeys col q-pa-sm bg-blue-2 row no-wrap">
  7. <div style="width: 80px">Сочетание клавиш</div>
  8. <q-input ref="input" class="q-ml-sm col"
  9. outlined dense rounded
  10. bg-color="grey-4"
  11. placeholder="Найти"
  12. v-model="search"
  13. @click.stop
  14. />
  15. <div v-show="!readonly" class="q-ml-sm column justify-center">
  16. <q-btn class="bg-grey-4 text-grey-6" style="height: 35px; width: 35px" rounded flat icon="la la-broom" @click="defaultHotKeyAll">
  17. <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
  18. Установить все сочетания по умолчанию
  19. </q-tooltip>
  20. </q-btn>
  21. </div>
  22. </div>
  23. </div>
  24. <!-- body -->
  25. <div class="table-row row" v-for="(action, index) in tableData" :key="index">
  26. <div class="desc q-pa-sm">{{ rstore.readerActions[action] }}</div>
  27. <div class="hotKeys col q-pa-sm">
  28. <q-chip
  29. :color="collisions[code] ? 'red' : 'grey-7'"
  30. :removable="!readonly" :clickable="collisions[code] ? true : false"
  31. text-color="white" v-for="(code, index) in value[action]" :key="index" @remove="removeCode(action, code)"
  32. @click="collisionWarning(code)"
  33. >
  34. {{ code }}
  35. </q-chip>
  36. </div>
  37. <div v-show="!readonly" class="column q-pa-xs">
  38. <q-icon
  39. name="la la-plus-circle"
  40. class="button bg-green-8 text-white"
  41. @click="addHotKey(action)"
  42. v-ripple
  43. :disabled="value[action].length >= maxCodesLength"
  44. >
  45. <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
  46. Добавить сочетание клавиш
  47. </q-tooltip>
  48. </q-icon>
  49. <q-icon
  50. name="la la-broom"
  51. class="button text-grey-5"
  52. @click="defaultHotKey(action)"
  53. v-ripple
  54. >
  55. <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
  56. По умолчанию
  57. </q-tooltip>
  58. </q-icon>
  59. </div>
  60. </div>
  61. </div>
  62. </template>
  63. <script>
  64. //-----------------------------------------------------------------------------
  65. import Vue from 'vue';
  66. import Component from 'vue-class-component';
  67. import rstore from '../../../../store/modules/reader';
  68. //import * as utils from '../../share/utils';
  69. const UserHotKeysProps = Vue.extend({
  70. props: {
  71. value: Object,
  72. readonly: Boolean,
  73. }
  74. });
  75. export default @Component({
  76. watch: {
  77. search: function() {
  78. this.updateTableData();
  79. },
  80. value: function() {
  81. this.checkCollisions();
  82. this.updateTableData();
  83. }
  84. },
  85. })
  86. class UserHotKeys extends UserHotKeysProps {
  87. search = '';
  88. rstore = {};
  89. tableData = [];
  90. collisions = {};
  91. maxCodesLength = 10;
  92. created() {
  93. this.rstore = rstore;
  94. }
  95. mounted() {
  96. this.checkCollisions();
  97. this.updateTableData();
  98. }
  99. get mode() {
  100. return this.$store.state.config.mode;
  101. }
  102. updateTableData() {
  103. let result = rstore.hotKeys.map(hk => hk.name).filter(name => (this.mode == 'liberama.top' || name != 'libs'));
  104. const search = this.search.toLowerCase();
  105. const codesIncludeSearch = (action) => {
  106. for (const code of this.value[action]) {
  107. if (code.toLowerCase().includes(search))
  108. return true;
  109. }
  110. return false;
  111. };
  112. result = result.filter(item => {
  113. return !search ||
  114. rstore.readerActions[item].toLowerCase().includes(search) ||
  115. codesIncludeSearch(item)
  116. });
  117. this.tableData = result;
  118. }
  119. checkCollisions() {
  120. const cols = {};
  121. for (const [action, codes] of Object.entries(this.value)) {
  122. codes.forEach(code => {
  123. if (!cols[code])
  124. cols[code] = [];
  125. if (cols[code].indexOf(action) < 0)
  126. cols[code].push(action);
  127. });
  128. }
  129. const result = {};
  130. for (const [code, actions] of Object.entries(cols)) {
  131. if (actions.length > 1)
  132. result[code] = actions;
  133. }
  134. this.collisions = result;
  135. }
  136. collisionWarning(code) {
  137. if (this.collisions[code]) {
  138. const descs = this.collisions[code].map(action => `<b>${rstore.readerActions[action]}</b>`);
  139. this.$root.stdDialog.alert(`Сочетание '${code}' одновременно назначено<br>следующим командам:<br>${descs.join('<br>')}<br><br>
  140. Возможно неожиданное поведение.`, 'Предупреждение');
  141. }
  142. }
  143. removeCode(action, code) {
  144. let codes = Array.from(this.value[action]);
  145. const index = codes.indexOf(code);
  146. if (index >= 0) {
  147. codes.splice(index, 1);
  148. const newValue = Object.assign({}, this.value, {[action]: codes});
  149. this.$emit('input', newValue);
  150. }
  151. }
  152. async addHotKey(action) {
  153. if (this.value[action].length >= this.maxCodesLength)
  154. return;
  155. try {
  156. const result = await this.$root.stdDialog.getHotKey(`Добавить сочетание для:<br><b>${rstore.readerActions[action]}</b>`, '');
  157. if (result) {
  158. let codes = Array.from(this.value[action]);
  159. if (codes.indexOf(result) < 0) {
  160. codes.push(result);
  161. const newValue = Object.assign({}, this.value, {[action]: codes});
  162. this.$emit('input', newValue);
  163. this.$nextTick(() => {
  164. this.collisionWarning(result);
  165. });
  166. }
  167. }
  168. } catch (e) {
  169. //
  170. }
  171. }
  172. async defaultHotKey(action) {
  173. try {
  174. if (await this.$root.stdDialog.confirm(`Подтвердите сброс сочетаний клавиш<br>в значения по умолчанию для команды:<br><b>${rstore.readerActions[action]}</b>`, ' ')) {
  175. const codes = Array.from(rstore.settingDefaults.userHotKeys[action]);
  176. const newValue = Object.assign({}, this.value, {[action]: codes});
  177. this.$emit('input', newValue);
  178. }
  179. } catch (e) {
  180. //
  181. }
  182. }
  183. async defaultHotKeyAll() {
  184. try {
  185. if (await this.$root.stdDialog.confirm('Подтвердите сброс сочетаний клавиш<br>для ВСЕХ команд в значения по умолчанию:', ' ')) {
  186. const newValue = Object.assign({}, rstore.settingDefaults.userHotKeys);
  187. this.$emit('input', newValue);
  188. }
  189. } catch (e) {
  190. //
  191. }
  192. }
  193. }
  194. //-----------------------------------------------------------------------------
  195. </script>
  196. <style scoped>
  197. .table {
  198. border-left: 1px solid grey;
  199. border-top: 1px solid grey;
  200. }
  201. .table-row {
  202. border-right: 1px solid grey;
  203. border-bottom: 1px solid grey;
  204. }
  205. .table-row:nth-child(even) {
  206. background-color: #f7f7f7;
  207. }
  208. .table-row:hover {
  209. background-color: #f0f0f0;
  210. }
  211. .desc {
  212. width: 130px;
  213. overflow-wrap: break-word;
  214. word-wrap: break-word;
  215. white-space: normal;
  216. }
  217. .hotKeys {
  218. border-left: 1px solid grey;
  219. }
  220. .button {
  221. font-size: 25px;
  222. border-radius: 25px;
  223. cursor: pointer;
  224. }
  225. </style>