2
0

PageActionsCol.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template lang="pug">
  2. .page-actions.column.items-stretch.order-last(:class='editorStore.isActive ? `is-editor` : ``')
  3. template(v-if='userStore.can(`edit:pages`)')
  4. q-btn.q-py-md(
  5. flat
  6. icon='las la-pen-nib'
  7. :color='editorStore.isActive ? `white` : `deep-orange-9`'
  8. aria-label='Page Properties'
  9. @click='togglePageProperties'
  10. )
  11. q-tooltip(anchor='center left' self='center right') Page Properties
  12. q-btn.q-py-md(
  13. v-if='flagsStore.experimental'
  14. flat
  15. icon='las la-project-diagram'
  16. :color='editorStore.isActive ? `white` : `deep-orange-9`'
  17. aria-label='Page Data'
  18. @click='togglePageData'
  19. disable
  20. )
  21. q-tooltip(anchor='center left' self='center right') Page Data
  22. q-btn.q-py-md(
  23. v-if='editorStore.isActive'
  24. flat
  25. color='white'
  26. :text-color='hasPendingAssets ? `white` : `deep-orange-3`'
  27. aria-label='Pending Asset Uploads'
  28. )
  29. q-icon(name='mdi-image-sync-outline')
  30. q-badge.page-actions-pending-badge(
  31. v-if='hasPendingAssets'
  32. color='white'
  33. text-color='orange-9'
  34. rounded
  35. floating
  36. )
  37. strong {{ editorStore.pendingAssets.length * 1 }}
  38. q-tooltip(anchor='center left' self='center right') Pending Asset Uploads
  39. q-menu(
  40. ref='menuPendingAssets'
  41. anchor='top left'
  42. self='top right'
  43. :offset='[10, 0]'
  44. )
  45. q-card(style='width: 450px;')
  46. q-card-section.card-header
  47. q-icon(name='img:/_assets/icons/color-data-pending.svg', left, size='sm')
  48. span Pending Asset Uploads
  49. q-card-section(v-if='!hasPendingAssets') There are no assets pending uploads.
  50. q-list(v-else, separator)
  51. q-item(
  52. v-for='item of editorStore.pendingAssets'
  53. :key='item.id'
  54. )
  55. q-item-section(side)
  56. q-icon(name='las la-file-image')
  57. q-item-section {{ item.fileName }}
  58. q-item-section(side)
  59. q-btn.acrylic-btn(
  60. color='negative'
  61. round
  62. icon='las la-times'
  63. size='xs'
  64. flat
  65. @click='removePendingAsset(item)'
  66. )
  67. q-card-section.card-actions
  68. em.text-caption Assets that are pasted or dropped onto this page will be held here until the page is saved.
  69. q-separator.q-my-sm(inset)
  70. q-btn.q-py-md(
  71. flat
  72. icon='las la-history'
  73. :color='editorStore.isActive ? `white` : `grey`'
  74. aria-label='Page History'
  75. @click='notImplemented'
  76. )
  77. q-tooltip(anchor='center left' self='center right') Page History
  78. q-btn.q-py-md(
  79. flat
  80. icon='las la-code'
  81. :color='editorStore.isActive ? `white` : `grey`'
  82. aria-label='Page Source'
  83. @click='viewPageSource'
  84. )
  85. q-tooltip(anchor='center left' self='center right') Page Source
  86. template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
  87. q-separator.q-my-sm(inset)
  88. q-btn.q-py-sm(
  89. flat
  90. icon='las la-ellipsis-h'
  91. :color='editorStore.isActive ? `deep-orange-2` : `grey`'
  92. aria-label='Page Actions'
  93. )
  94. q-tooltip(anchor='center left' self='center right') Page Actions
  95. q-menu.translucent-menu(
  96. anchor='top left'
  97. self='top right'
  98. auto-close
  99. transition-show='jump-left'
  100. )
  101. q-list(padding, style='min-width: 225px;')
  102. q-item(clickable, disabled, v-if='userStore.can(`manage:pages`)')
  103. q-item-section.items-center(avatar)
  104. q-icon(color='deep-orange-9', name='las la-atom', size='sm')
  105. q-item-section
  106. q-item-label Convert Page
  107. q-item(clickable, v-if='userStore.can(`edit:pages`)', @click='rerenderPage')
  108. q-item-section.items-center(avatar)
  109. q-icon(color='deep-orange-9', name='las la-magic', size='sm')
  110. q-item-section
  111. q-item-label Rerender Page
  112. q-item(clickable, disabled)
  113. q-item-section.items-center(avatar)
  114. q-icon(color='deep-orange-9', name='las la-sun', size='sm')
  115. q-item-section
  116. q-item-label View Backlinks
  117. q-space
  118. template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
  119. q-btn.q-py-sm(
  120. v-if='userStore.can(`create:pages`)'
  121. flat
  122. icon='las la-copy'
  123. :color='editorStore.isActive ? `deep-orange-2` : `grey`'
  124. aria-label='Duplicate Page'
  125. @click='duplicatePage'
  126. )
  127. q-tooltip(anchor='center left' self='center right') Duplicate Page
  128. q-btn.q-py-sm(
  129. v-if='userStore.can(`manage:pages`)'
  130. flat
  131. icon='las la-share'
  132. :color='editorStore.isActive ? `deep-orange-2` : `grey`'
  133. aria-label='Rename / Move Page'
  134. @click='renamePage'
  135. )
  136. q-tooltip(anchor='center left' self='center right') Rename / Move Page
  137. q-btn.q-py-sm(
  138. v-if='userStore.can(`delete:pages`)'
  139. flat
  140. icon='las la-trash'
  141. :color='editorStore.isActive ? `deep-orange-2` : `grey`'
  142. aria-label='Delete Page'
  143. @click='deletePage'
  144. :class='editorStore.isActive ? `q-pb-md` : ``'
  145. )
  146. q-tooltip(anchor='center left' self='center right') Delete Page
  147. span.page-actions-mode(v-else) {{ t('common.actions.newPage') }}
  148. </template>
  149. <script setup>
  150. import { useQuasar } from 'quasar'
  151. import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue'
  152. import { useRouter, useRoute } from 'vue-router'
  153. import { useI18n } from 'vue-i18n'
  154. import { useEditorStore } from '@/stores/editor'
  155. import { useFlagsStore } from '@/stores/flags'
  156. import { usePageStore } from '@/stores/page'
  157. import { useSiteStore } from '@/stores/site'
  158. import { useUserStore } from '@/stores/user'
  159. // QUASAR
  160. const $q = useQuasar()
  161. // STORES
  162. const editorStore = useEditorStore()
  163. const flagsStore = useFlagsStore()
  164. const pageStore = usePageStore()
  165. const siteStore = useSiteStore()
  166. const userStore = useUserStore()
  167. // ROUTER
  168. const router = useRouter()
  169. const route = useRoute()
  170. // I18N
  171. const { t } = useI18n()
  172. // REFS
  173. const menuPendingAssets = ref(null)
  174. // COMPUTED
  175. const hasPendingAssets = computed(() => editorStore.pendingAssets?.length > 0)
  176. // METHODS
  177. function togglePageProperties () {
  178. siteStore.$patch({
  179. sideDialogComponent: 'PagePropertiesDialog',
  180. sideDialogShown: true
  181. })
  182. }
  183. function togglePageData () {
  184. siteStore.$patch({
  185. sideDialogComponent: 'PageDataDialog',
  186. sideDialogShown: true
  187. })
  188. }
  189. function viewPageSource () {
  190. siteStore.$patch({ overlay: 'PageSource', overlayOpts: { } })
  191. }
  192. function rerenderPage () {
  193. $q.dialog({
  194. component: defineAsyncComponent(() => import('../components/RerenderPageDialog.vue')),
  195. componentProps: {
  196. id: pageStore.id
  197. }
  198. }).onOk(() => {
  199. pageStore.pageLoad({ id: pageStore.id })
  200. })
  201. }
  202. function duplicatePage () {
  203. $q.dialog({
  204. component: defineAsyncComponent(() => import('../components/TreeBrowserDialog.vue')),
  205. componentProps: {
  206. mode: 'duplicatePage',
  207. folderPath: '',
  208. itemId: pageStore.id,
  209. itemTitle: pageStore.title,
  210. itemFileName: pageStore.path
  211. }
  212. }).onOk((newPageOpts) => {
  213. pageStore.pageDuplicate({ sourecePageId: pageStore.id, path: newPageOpts.path, title: newPageOpts.title })
  214. })
  215. }
  216. function renamePage () {
  217. $q.dialog({
  218. component: defineAsyncComponent(() => import('../components/TreeBrowserDialog.vue')),
  219. componentProps: {
  220. mode: 'renamePage',
  221. folderPath: '',
  222. itemId: pageStore.id,
  223. itemTitle: pageStore.title,
  224. itemFileName: pageStore.path
  225. }
  226. }).onOk(() => {
  227. // TODO: change route to new location
  228. })
  229. }
  230. function deletePage () {
  231. $q.dialog({
  232. component: defineAsyncComponent(() => import('../components/PageDeleteDialog.vue')),
  233. componentProps: {
  234. pageId: pageStore.id,
  235. pageName: pageStore.title
  236. }
  237. }).onOk(() => {
  238. router.replace('/')
  239. })
  240. }
  241. function removePendingAsset (item) {
  242. URL.revokeObjectURL(item.blobUrl)
  243. editorStore.pendingAssets = editorStore.pendingAssets.filter(a => a.id !== item.id)
  244. if (editorStore.pendingAssets.length < 1) {
  245. menuPendingAssets.value.hide()
  246. }
  247. }
  248. function notImplemented () {
  249. $q.notify({
  250. type: 'negative',
  251. message: 'Not implemented'
  252. })
  253. }
  254. </script>
  255. <style lang="scss">
  256. .page-actions {
  257. flex: 0 0 56px;
  258. @at-root .body--light & {
  259. background-color: $grey-3;
  260. }
  261. @at-root .body--dark & {
  262. background-color: $dark-4;
  263. }
  264. &.is-editor {
  265. @at-root .body--light & {
  266. background-color: $deep-orange-9;
  267. }
  268. @at-root .body--dark & {
  269. background-color: $deep-orange-9;
  270. }
  271. }
  272. &-mode {
  273. writing-mode: vertical-rl;
  274. text-orientation: mixed;
  275. padding: 1.75rem 1rem 1.75rem 0;
  276. color: $deep-orange-3;
  277. font-weight: 500;
  278. }
  279. &-pending-badge {
  280. animation: pageActionsBadgePulsate 2s ease infinite;
  281. }
  282. }
  283. @keyframes pageActionsBadgePulsate {
  284. 0% {
  285. transform: translate(0, 0);
  286. }
  287. 50% {
  288. transform: translate(3px, -3px);
  289. }
  290. 100% {
  291. transform: translate(0, 0);
  292. }
  293. }
  294. </style>