LibraryOptions.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <template>
  2. <div class="library-options">
  3. <b-form-group
  4. class="mode-select-group"
  5. :label="$t('options.display.title')"
  6. v-slot="{ ariaDescribedby }"
  7. >
  8. <b-form-radio-group
  9. id="mode-select"
  10. v-model="activeMode" :options="modes"
  11. name="mode-select" :aria-describedby="ariaDescribedby"
  12. buttons button-variant="outline-dark"
  13. />
  14. <b-button
  15. v-if="activeMode === 'map'"
  16. variant="outline-dark" class="d-block mt-2"
  17. @click="$store.dispatch('ROLL_RANDOM_NODES')"
  18. >
  19. {{ $t('options.display.shuffle') }}
  20. </b-button>
  21. <b-dropdown
  22. v-if="activeMode === 'list'"
  23. :text="$t('options.display.go-to-char')"
  24. variant="outline-dark" class="char-select d-block mt-2"
  25. dropright
  26. >
  27. <b-dropdown-item-button
  28. class="text-center"
  29. v-for="char in availableCharacters" :key="char"
  30. @click="scrollToChar(char)"
  31. >
  32. {{ char }}
  33. </b-dropdown-item-button>
  34. </b-dropdown>
  35. </b-form-group>
  36. <!-- LIST + MAP ONLY -->
  37. <b-form-group
  38. class="filters-group"
  39. v-if="mode !== 'tree'"
  40. :label="$t('options.filters.title')"
  41. >
  42. <div class="filters-mini-group mb-1">
  43. <b-form-checkbox
  44. v-model="tagsIsActive" name="tags-button"
  45. button class="toggle-btn"
  46. :class="tagsIsActive ? null : 'collapsed'"
  47. :aria-expanded="tagsIsActive ? 'true' : 'false'"
  48. aria-controls="tags-select"
  49. button-variant="outline-dark"
  50. >
  51. {{ $t('options.filters.choices.tags') }}
  52. </b-form-checkbox>
  53. <multiple-tags-select
  54. v-if="tagsIsActive"
  55. id="tags-select" :button-text="$t('options.filters.add')"
  56. v-model="activeTags" :options="tagsOptions"
  57. />
  58. </div>
  59. <div class="filters-mini-group align-items-center">
  60. <b-form-checkbox
  61. v-model="strangenessIsActive" name="tags-button"
  62. button class="toggle-btn"
  63. :class="strangenessIsActive ? null : 'collapsed'"
  64. :aria-expanded="strangenessIsActive ? 'true' : 'false'"
  65. aria-controls="strangeness-input"
  66. button-variant="outline-dark"
  67. >
  68. {{ $t('options.filters.choices.strangeness') }}
  69. </b-form-checkbox>
  70. <div v-if="strangenessIsActive" class="strangeness-input-wrapper">
  71. <b-form-input
  72. id="strangeness-input"
  73. type="range" min="1" max="5"
  74. v-model="activeStrangeness"
  75. />
  76. </div>
  77. </div>
  78. </b-form-group>
  79. <!-- LIST + MAP ONLY -->
  80. <search-input v-if="mode !== 'tree'" v-model="activeSearch" />
  81. <!-- TREE ONLY -->
  82. <b-form-group v-else id="depart-group" :label="$t('options.display.select-text')">
  83. <b-dropdown id="text-depart-select" variant="outline-dark" right>
  84. <template #button-content>
  85. <node-view-title v-if="activeNode" :node="activeNode" />
  86. </template>
  87. <b-dropdown-item v-for="node in nodesDepartsOptions" :key="node.id" @click="activeNodeId = node.id">
  88. <node-view-title :node="node" block />
  89. </b-dropdown-item>
  90. </b-dropdown>
  91. </b-form-group>
  92. </div>
  93. </template>
  94. <script>
  95. import { mapGetters } from 'vuex'
  96. import { MultipleTagsSelect, SearchInput } from '@/components/formItems'
  97. import { NodeViewTitle } from '@/components/nodes'
  98. export default {
  99. name: 'LibraryOptions',
  100. components: {
  101. NodeViewTitle,
  102. MultipleTagsSelect,
  103. SearchInput
  104. },
  105. data () {
  106. return {
  107. modes: [
  108. { text: this.$t('options.display.choices.tree-map'), value: 'tree' },
  109. { text: this.$t('options.display.choices.card-map'), value: 'map' },
  110. { text: this.$t('options.display.choices.card-list'), value: 'list' }
  111. ],
  112. selectedTags: [],
  113. tagsIsActive: false,
  114. strangenessIsActive: false
  115. }
  116. },
  117. computed: {
  118. ...mapGetters([
  119. 'nodesDepartsOptions',
  120. 'nodesDeparts',
  121. 'node',
  122. 'orderedTextsDepart',
  123. 'tagsOptions',
  124. 'nodebook',
  125. 'mode',
  126. 'nodeDepartId',
  127. 'search',
  128. 'tags',
  129. 'strangeness'
  130. ]),
  131. availableCharacters () {
  132. if (!this.orderedTextsDepart) return []
  133. return this.orderedTextsDepart.map(group => group[0])
  134. },
  135. activeNodeId: {
  136. get () { return this.nodeDepartId },
  137. set (value) {
  138. this.$store.dispatch('SET_NODE_DEPART_ID', value)
  139. }
  140. },
  141. activeNode () {
  142. return this.node(this.nodeDepartId)
  143. },
  144. activeMode: {
  145. get () { return this.mode },
  146. set (value) {
  147. this.$store.dispatch('UPDATE_QUERY_MODE', value)
  148. }
  149. },
  150. activeSearch: {
  151. get () { return this.search },
  152. set (value) {
  153. this.$store.commit('SET_SEARCH', value)
  154. }
  155. },
  156. activeTags: {
  157. get () { return this.tags },
  158. set (value) {
  159. this.$store.commit('UPDATE_TAGS', value)
  160. }
  161. },
  162. activeStrangeness: {
  163. get () { return this.strangeness },
  164. set (value) {
  165. this.$store.commit('UPDATE_STRANGENESS', parseInt(value))
  166. }
  167. }
  168. },
  169. watch: {
  170. strangenessIsActive (activate) {
  171. if (activate) {
  172. this.$store.commit('UPDATE_STRANGENESS', 1)
  173. } else {
  174. this.$store.commit('UPDATE_STRANGENESS', undefined)
  175. }
  176. },
  177. tagsIsActive () {
  178. this.$store.commit('UPDATE_TAGS', [])
  179. }
  180. },
  181. methods: {
  182. scrollToChar (char) {
  183. const h = document.querySelector(`h3[id=${char}]`)
  184. if (h) {
  185. h.scrollIntoView({
  186. behavior: navigator.userAgent.indexOf('Safari') !== -1 ? 'instant' : 'smooth'
  187. })
  188. }
  189. }
  190. }
  191. }
  192. </script>
  193. <style lang="scss" scoped>
  194. .library-options {
  195. padding: 0 $header-spacer-sm $header-spacer-sm;
  196. background-color: $body-bg;
  197. width: 100%;
  198. @include media-breakpoint-down($layout-bp-down) {
  199. font-size: 0.9rem;
  200. ::v-deep {
  201. .btn, .form-control, .input-group-text {
  202. font-size: inherit;
  203. }
  204. }
  205. }
  206. @include media-breakpoint-up($size-bp) {
  207. padding: 0 $header-spacer $header-spacer;
  208. display: flex;
  209. justify-content: space-between;
  210. }
  211. ::v-deep {
  212. @include media-breakpoint-up($size-bp) {
  213. flex-wrap: wrap;
  214. fieldset {
  215. margin-bottom: 0;
  216. &:last-child {
  217. flex-shrink: 2;
  218. }
  219. }
  220. }
  221. > :last-child {
  222. margin-bottom: 0;
  223. }
  224. .mode-select-group {
  225. > div {
  226. line-height: 0;
  227. }
  228. label {
  229. border-color: transparent;
  230. &:not(:disabled):not(.disabled):active,
  231. &:not(:disabled):not(.disabled).active {
  232. background-color: transparent;
  233. color: $black;
  234. }
  235. }
  236. .char-select {
  237. line-height: 1;
  238. .dropdown-menu {
  239. max-height: 200px;
  240. overflow: auto;
  241. min-width: 3rem;
  242. }
  243. }
  244. }
  245. .filters-group {
  246. flex-grow: 2;
  247. @include media-breakpoint-up($size-bp) {
  248. padding: 0 2rem;
  249. max-width: 40%;
  250. }
  251. .filters-mini-group {
  252. display: flex;
  253. line-height: 0;
  254. }
  255. .toggle-btn label {
  256. border-color: transparent;
  257. margin-right: .5rem;
  258. &:not(:disabled):not(.disabled):active,
  259. &:not(:disabled):not(.disabled).active {
  260. border-color: $black;
  261. background-color: transparent;
  262. color: $black;
  263. }
  264. }
  265. .strangeness-input-wrapper {
  266. min-width: 150px;
  267. &::before,
  268. &::after {
  269. content: '';
  270. display: inline-block;
  271. width: 3px;
  272. height: 20px;
  273. background-color: $black;
  274. position: absolute;
  275. }
  276. &::after {
  277. margin-left: -2px;
  278. }
  279. }
  280. }
  281. }
  282. #depart-group {
  283. width: 100%;
  284. @include media-breakpoint-up($size-bp) {
  285. max-width: 50%;
  286. }
  287. }
  288. #text-depart-select ::v-deep {
  289. width: 100%;
  290. .dropdown-menu {
  291. max-height: 300px;
  292. min-width: 100%;
  293. overflow-y: auto;
  294. overflow-x: hidden;
  295. margin-left: 0;
  296. }
  297. .dropdown-toggle {
  298. border-radius: 0 !important;
  299. width: 100%;
  300. padding-right: 1rem;
  301. padding-top: .15em;
  302. .node-view-title {
  303. width: 100%;
  304. text-align: left;
  305. display: inline-block;
  306. margin: 0;
  307. line-height: 1.25;
  308. &-container {
  309. text-overflow: ellipsis;
  310. overflow: hidden;
  311. }
  312. }
  313. &::after {
  314. margin-left: 0;
  315. vertical-align: .35em;
  316. }
  317. }
  318. .dropdown-item {
  319. white-space: unset;
  320. }
  321. }
  322. }
  323. </style>