@oxygen-cms/ui 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/{.eslintrc.js → .eslintrc.json} +3 -3
  2. package/package.json +14 -4
  3. package/src/CrudApi.js +23 -9
  4. package/src/MediaApi.js +3 -3
  5. package/src/PagesApi.js +47 -0
  6. package/src/PartialsApi.js +30 -0
  7. package/src/components/AuthenticatedLayout.vue +3 -1
  8. package/src/components/CodeEditor.vue +1 -1
  9. package/src/components/GroupsChooser.vue +2 -2
  10. package/src/components/GroupsList.vue +2 -2
  11. package/src/components/PageActions.vue +119 -0
  12. package/src/components/PageEdit.vue +164 -0
  13. package/src/components/PageNestedPagination.vue +27 -0
  14. package/src/components/PageNestedRow.vue +52 -0
  15. package/src/components/PageStatusIcon.vue +33 -0
  16. package/src/components/PageTable.vue +156 -0
  17. package/src/components/PartialActions.vue +28 -0
  18. package/src/components/PartialList.vue +74 -0
  19. package/src/components/PartialStatusIcon.vue +29 -0
  20. package/src/components/PartialTable.vue +65 -0
  21. package/src/components/ResourceList.vue +132 -0
  22. package/src/components/UserManagement.vue +2 -2
  23. package/src/components/UserProfileForm.vue +1 -1
  24. package/src/components/UserProfilePage.vue +1 -1
  25. package/src/components/content/CommandsList.vue +108 -0
  26. package/src/components/content/ContentEditor.vue +490 -0
  27. package/src/components/content/GridCellNodeView.vue +82 -0
  28. package/src/components/content/GridRowNodeView.vue +53 -0
  29. package/src/components/content/HtmlNodeView.vue +89 -0
  30. package/src/components/content/MarkMenu.vue +116 -0
  31. package/src/components/content/MediaNodeView.vue +83 -0
  32. package/src/components/content/ObjectLinkNodeView.vue +181 -0
  33. package/src/components/content/PartialNodeView.vue +217 -0
  34. package/src/components/content/commands.js +72 -0
  35. package/src/components/content/suggestion.js +211 -0
  36. package/src/components/media/MediaChooseDirectory.vue +3 -3
  37. package/src/components/media/MediaDirectory.vue +1 -1
  38. package/src/components/media/MediaInsertModal.vue +11 -2
  39. package/src/components/media/MediaItem.vue +1 -1
  40. package/src/components/media/MediaItemPreview.vue +18 -2
  41. package/src/components/media/MediaList.vue +4 -5
  42. package/src/components/media/MediaUpload.vue +1 -1
  43. package/src/components/media/media.scss +1 -0
  44. package/src/components/pages/PageList.vue +65 -0
  45. package/src/components/users/CreateUserModal.vue +1 -1
  46. package/src/components/util.css +1 -1
  47. package/src/icons.js +33 -5
  48. package/src/main.js +4 -0
  49. package/src/modules/PagesPartials.js +74 -2
  50. package/src/styles/pages-table.scss +34 -0
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ {
2
2
  "env": {
3
3
  "browser": true,
4
4
  "es2021": true,
@@ -11,7 +11,7 @@ module.exports = {
11
11
  "prettier"
12
12
  ],
13
13
  "parserOptions": {
14
- "ecmaVersion": 12,
14
+ "ecmaVersion": 2022,
15
15
  "sourceType": "module"
16
16
  },
17
17
  "plugins": [
@@ -20,4 +20,4 @@ module.exports = {
20
20
  ],
21
21
  "rules": {
22
22
  }
23
- };
23
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@oxygen-cms/ui",
3
- "version": "1.7.2",
3
+ "version": "1.8.1",
4
4
  "description": "Various utilities for UI-building in Vue.js",
5
5
  "main": "none",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/oxygen-cms/ui.git"
9
9
  },
10
+ "type": "module",
10
11
  "author": "Chris Chamberlain <chris@chamberlain.id.au>",
11
12
  "license": "MIT",
12
13
  "private": false,
@@ -15,6 +16,12 @@
15
16
  "@fortawesome/free-regular-svg-icons": "^5.15.2",
16
17
  "@fortawesome/free-solid-svg-icons": "^5.15.2",
17
18
  "@fortawesome/vue-fontawesome": "^0.1.10",
19
+ "@tiptap/core": "^2.0.0-beta.209",
20
+ "@tiptap/extension-link": "^2.0.0-beta.209",
21
+ "@tiptap/extension-underline": "^2.0.0-beta.209",
22
+ "@tiptap/starter-kit": "^2.0.0-beta.209",
23
+ "@tiptap/suggestion": "^2.0.0-beta.209",
24
+ "@tiptap/vue-2": "^2.0.0-beta.209",
18
25
  "autoprefixer": "^9.8.5",
19
26
  "brace": "^0.11.1",
20
27
  "buefy": "^0.9.10",
@@ -23,6 +30,8 @@
23
30
  "downloadjs": "^1.4.7",
24
31
  "libphonenumber-js": "^1.9.11",
25
32
  "lodash": "^4.17.21",
33
+ "portal-vue": "^2.1.7",
34
+ "tippy.js": "^6.3.7",
26
35
  "title-case": "^3.0.3",
27
36
  "ua-parser-js": "^0.7.24",
28
37
  "v-hotkey": "^0.8.0",
@@ -32,16 +41,17 @@
32
41
  "vue-router": "^3.5.1",
33
42
  "vue-template-compiler": "^2.6.11",
34
43
  "vue2-ace-editor": "^0.0.15",
35
- "vuex": "^3.6.2"
44
+ "vuex": "^3.6.2",
45
+ "zeed-dom": "^0.9.26"
36
46
  },
37
47
  "devDependencies": {
38
48
  "@babel/core": "^7.13.1",
39
49
  "@babel/preset-env": "^7.13.5",
40
50
  "babel-jest": "^26.6.3",
41
51
  "babel-polyfill": "^6.26.0",
42
- "eslint": "^7.32.0",
52
+ "eslint": "^8.0",
43
53
  "eslint-config-prettier": "^8.3.0",
44
- "eslint-plugin-jest": "^24.4.0",
54
+ "eslint-plugin-jest": "^27.2.1",
45
55
  "eslint-plugin-vue": "^7.17.0",
46
56
  "jest": "^26.6.3",
47
57
  "regenerator-runtime": "^0.13.7",
package/src/CrudApi.js CHANGED
@@ -9,12 +9,13 @@ export const getApiRoot = () => {
9
9
 
10
10
  export class CrudApi {
11
11
 
12
- constructor($buefy) {
12
+ static setBuefy($buefy) {
13
13
  this.$buefy = $buefy;
14
14
  }
15
15
 
16
16
  request(method) {
17
- return FetchBuilder.default(this.$buefy, method);
17
+ if(!CrudApi.$buefy) { throw new Error("need to call setBuefy() first"); }
18
+ return FetchBuilder.default(CrudApi.$buefy, method);
18
19
  }
19
20
 
20
21
  static getResourceName() {
@@ -31,12 +32,18 @@ export class CrudApi {
31
32
  return m;
32
33
  }
33
34
 
34
- async list(inTrash, page, searchQuery) {
35
+ static convertModelFromAPI(data) {
36
+ return data;
37
+ }
38
+
39
+ async list({ inTrash, page, q, sortField, sortOrder }) {
35
40
  return this.request('get')
36
41
  .withQueryParams({
37
- page: (searchQuery !== null && searchQuery !== '' ) ? null : page,
42
+ page: (q !== null && q !== '' ) ? null : page,
43
+ sortField: sortField,
44
+ sortOrder: sortOrder,
38
45
  trash: (inTrash ? 'true' : 'false'),
39
- q: (searchQuery !== null && searchQuery !== '' ) ? searchQuery : null
46
+ q: (q !== null && q !== '' ) ? q : null
40
47
  })
41
48
  .fetch(this.constructor.getResourceRoot());
42
49
  }
@@ -77,7 +84,7 @@ export class CrudApi {
77
84
 
78
85
  async confirmForceDelete(id) {
79
86
  const promise = new Promise((resolve) => {
80
- this.$buefy.dialog.confirm({
87
+ CrudApi.$buefy.dialog.confirm({
81
88
  message: 'Are you sure you want to delete this record forever?',
82
89
  onConfirm: resolve
83
90
  });
@@ -85,20 +92,20 @@ export class CrudApi {
85
92
 
86
93
  await promise;
87
94
  let data = await this.forceDelete(id);
88
- this.$buefy.toast.open(morphToNotification(data));
95
+ CrudApi.$buefy.toast.open(morphToNotification(data));
89
96
  return data;
90
97
  }
91
98
 
92
99
  async restoreAndNotify(id) {
93
100
  let data = await this.request('post')
94
101
  .fetch(this.constructor.getResourceRoot() + '/' + id + '/restore');
95
- this.$buefy.toast.open(morphToNotification(data));
102
+ CrudApi.$buefy.toast.open(morphToNotification(data));
96
103
  return data;
97
104
  }
98
105
 
99
106
  async deleteAndNotify(id) {
100
107
  let data = await this.delete(id);
101
- this.$buefy.toast.open(morphToNotification(data));
108
+ CrudApi.$buefy.toast.open(morphToNotification(data));
102
109
  return data;
103
110
  }
104
111
 
@@ -111,4 +118,11 @@ export class CrudApi {
111
118
  return this.request('post')
112
119
  .fetch(this.constructor.getResourceRoot() + '/' + id + '/make-head');
113
120
  }
121
+
122
+ async publish(id) {
123
+ let data = await this.request('post').fetch(this.constructor.getResourceRoot() + '/' + id + '/publish');
124
+ CrudApi.$buefy.toast.open(morphToNotification(data));
125
+ return data.item;
126
+ }
127
+
114
128
  }
package/src/MediaApi.js CHANGED
@@ -33,12 +33,12 @@ export default class MediaApi extends CrudApi {
33
33
  .fetch(this.constructor.getResourceRoot());
34
34
  }
35
35
 
36
- async list(inTrash, searchQuery, page, path) {
36
+ async list({ inTrash, q, page, path }) {
37
37
  return this.request('get')
38
38
  .withQueryParams({
39
39
  path: path,
40
- q: (searchQuery !== null && searchQuery !== '' ) ? searchQuery : null,
41
- page: (searchQuery !== null && searchQuery !== '' ) ? null : page,
40
+ q: (q !== null && q !== '' ) ? q : null,
41
+ page: (q !== null && q !== '' ) ? null : page,
42
42
  trash: (inTrash ? 'true' : 'false'),
43
43
  })
44
44
  .fetch(this.constructor.getResourceRoot());
@@ -0,0 +1,47 @@
1
+ import { CrudApi } from './CrudApi';
2
+ import {getApiHost} from "./api.js";
3
+
4
+ export default class PagesApi extends CrudApi {
5
+
6
+ static STAGE_DRAFT = 0;
7
+ static STAGE_PENDING_REVIEW = 1;
8
+ static STAGE_PUBLISHED = 2;
9
+ static STAGE_ARCHIVED = 3;
10
+
11
+ static getResourceName() {
12
+ return 'pages';
13
+ }
14
+
15
+ static prepareModelForAPI(data) {
16
+ let m = { ...data };
17
+ delete m.id;
18
+ delete m.createdAt;
19
+ delete m.updatedAt;
20
+ delete m.deletedAt;
21
+ delete m.createdBy;
22
+ delete m.updatedBy;
23
+ delete m.headVersion;
24
+ return m;
25
+ }
26
+
27
+ static slugToUrl(slug) {
28
+ return getApiHost() + slug.trimStart('/');
29
+ }
30
+
31
+ async list({ inTrash, page, q, path, sortField, sortOrder }) {
32
+ if(!path) {
33
+ path = q ? null : '/';
34
+ }
35
+ return this.request('get')
36
+ .withQueryParams({
37
+ path: path,
38
+ page: (q !== null && q !== '' ) ? null : page,
39
+ trash: (inTrash ? 'true' : 'false'),
40
+ q: (q !== null && q !== '' ) ? q : null,
41
+ sortField,
42
+ sortOrder
43
+ })
44
+ .fetch(this.constructor.getResourceRoot());
45
+ }
46
+
47
+ }
@@ -0,0 +1,30 @@
1
+ import { CrudApi } from './CrudApi';
2
+
3
+ export default class PartialsApi extends CrudApi {
4
+
5
+ static STAGE_DRAFT = 0;
6
+ static STAGE_PUBLISHED = 1;
7
+
8
+ static getResourceName() {
9
+ return 'partials';
10
+ }
11
+
12
+ static prepareModelForAPI(data) {
13
+ let m = { ...data };
14
+ delete m.id;
15
+ return m;
16
+ }
17
+
18
+ async list({ inTrash, page, q, sortField, sortOrder }) {
19
+ return this.request('get')
20
+ .withQueryParams({
21
+ page: (q !== null && q !== '' ) ? null : page,
22
+ trash: (inTrash ? 'true' : 'false'),
23
+ q: (q !== null && q !== '' ) ? q : null,
24
+ sortField,
25
+ sortOrder
26
+ })
27
+ .fetch(this.constructor.getResourceRoot());
28
+ }
29
+
30
+ }
@@ -51,6 +51,8 @@
51
51
  <router-view></router-view>
52
52
  </transition>
53
53
 
54
+ <portal-target name="editors"></portal-target>
55
+
54
56
  </div>
55
57
  </div>
56
58
  </template>
@@ -66,7 +68,7 @@ export default {
66
68
  emits: ["logout"],
67
69
  data() {
68
70
  return {
69
- usersApi: new UsersApi(this.$buefy),
71
+ usersApi: new UsersApi(),
70
72
  setCollapsed: false,
71
73
  requestedCollapsed: false
72
74
  }
@@ -79,7 +79,7 @@ export default {
79
79
  props: {
80
80
  value: { type: String, default: null },
81
81
  height: { type: String, required: true },
82
- lang: { type: String, required: true }
82
+ lang: { type: String, required: true }
83
83
  },
84
84
  data() {
85
85
  return {
@@ -30,7 +30,7 @@ export default {
30
30
  },
31
31
  data() {
32
32
  return {
33
- groupsApi: new GroupsApi(this.$buefy),
33
+ groupsApi: new GroupsApi(),
34
34
  loading: true,
35
35
  users: []
36
36
  }
@@ -46,7 +46,7 @@ export default {
46
46
  methods: {
47
47
  async fetchData(name) {
48
48
  this.loading = true;
49
- this.users = (await this.groupsApi.list(false, 1, name)).items;
49
+ this.users = (await this.groupsApi.list({ inTrash: false, page: 1, q: name })).items;
50
50
  this.loading = false;
51
51
  }
52
52
  }
@@ -86,7 +86,7 @@ export default {
86
86
  components: { UserJoined, GenericEditableField, EditButtonOnRowHover },
87
87
  data() {
88
88
  return {
89
- groupsApi: new GroupsApi(this.$buefy),
89
+ groupsApi: new GroupsApi(),
90
90
  paginatedItems: {items: null, totalItems: null, itemsPerPage: null, loading: false, currentPage: 1},
91
91
  }
92
92
  },
@@ -96,7 +96,7 @@ export default {
96
96
  methods: {
97
97
  async fetchData() {
98
98
  this.paginatedItems.loading = true;
99
- let data = await this.groupsApi.list(false, this.paginatedItems.currentPage, null);
99
+ let data = await this.groupsApi.list({ inTrash: false, page: this.paginatedItems.currentPage, q: null });
100
100
  this.paginatedItems.items = data.items;
101
101
  this.paginatedItems.totalItems = data.totalItems;
102
102
  this.paginatedItems.itemsPerPage = data.itemsPerPage;
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <div class="page-actions">
3
+ <b-button v-if="item.stage !== STAGE_PUBLISHED" rounded size="is-small" icon-left="globe-asia" class="mr-2" @click="publish">Publish</b-button>
4
+
5
+ <b-dropdown
6
+ ref="moveDropdown"
7
+ position="is-top-left"
8
+ append-to-body
9
+ aria-role="menu"
10
+ trap-focus
11
+ class="move-page-dropdown"
12
+ >
13
+ <template #trigger>
14
+ <b-button rounded size="is-small" icon-left="folder-open" class="mr-2">Move</b-button>
15
+ </template>
16
+
17
+ <b-dropdown-item
18
+ aria-role="menu-item"
19
+ custom
20
+ paddingless>
21
+ <div class="modal-card" style="width: auto; overflow: visible">
22
+ <header class="modal-card-head">
23
+ <p class="modal-card-title">Parent page for "{{ item.title }}"</p>
24
+ </header>
25
+ <section class="modal-card-body" style="overflow: visible;">
26
+ <b-field>
27
+ <b-autocomplete
28
+ v-model.lazy="movePageSearchQuery"
29
+ :disabled="isLoading"
30
+ open-on-focus
31
+ :data="pagesList"
32
+ :custom-formatter="data => data.title + ' - ' + data.slug"
33
+ placeholder="Search for pages..."
34
+ clearable
35
+ @select="setParentPage">
36
+ <template #empty>No results found</template>
37
+ </b-autocomplete>
38
+ </b-field>
39
+ </section>
40
+ <footer class="modal-card-foot is-flex">
41
+ <div class="is-flex-grow-1"></div>
42
+ <b-button
43
+ label="Close"
44
+ @click="close"/>
45
+ </footer>
46
+ </div>
47
+ </b-dropdown-item>
48
+ </b-dropdown>
49
+ </div>
50
+ </template>
51
+
52
+ <script>
53
+ import PagesApi from "../PagesApi.js";
54
+ import {morphToNotification} from "../api.js";
55
+
56
+ export default {
57
+ name: "PageActions",
58
+ props: {
59
+ item: { type: Object, required: true }
60
+ },
61
+ created() {
62
+ this.isLoading = true;
63
+ this.fetchData()
64
+ },
65
+ data() {
66
+ return {
67
+ STAGE_PUBLISHED: PagesApi.STAGE_PUBLISHED,
68
+ pagesApi: new PagesApi(),
69
+ movePageSearchQuery: '',
70
+ isLoading: false,
71
+ pagesList: []
72
+ }
73
+ },
74
+ watch: {
75
+ 'movePageSearchQuery': 'fetchData'
76
+ },
77
+ methods: {
78
+ async fetchData() {
79
+ let data = await this.pagesApi.list({ inTrash: false, page: 1, q: this.movePageSearchQuery });
80
+ this.pagesList = data.items;
81
+ this.pagesList.sort((a, b) => {
82
+ return a.title.localeCompare(b.title);
83
+ });
84
+ this.isLoading = false;
85
+ },
86
+ async publish() {
87
+ let item = await this.pagesApi.publish(this.item.id);
88
+ this.$emit('update', item);
89
+ },
90
+ async setParentPage(parentPage) {
91
+ let data = await this.pagesApi.update({id: this.item.id, parent: parentPage.id, autoConvertToDraft: 'no', version: false});
92
+ this.$buefy.toast.open(morphToNotification(data));
93
+ this.$emit('reload');
94
+ },
95
+ close() {
96
+ this.$refs.moveDropdown.toggle();
97
+ }
98
+ }
99
+ }
100
+ </script>
101
+
102
+ <style scoped>
103
+ .modal-card-title {
104
+ flex-shrink: unset;
105
+ flex-grow: unset;
106
+ }
107
+ </style>
108
+
109
+ <style>
110
+ .move-page-dropdown .dropdown-content {
111
+ padding-top: 0;
112
+ padding-bottom: 0;
113
+ }
114
+
115
+ .move-page-dropdown .dropdown-menu {
116
+ overflow: visible !important;
117
+ min-width: 20rem;
118
+ }
119
+ </style>
@@ -0,0 +1,164 @@
1
+ <template>
2
+ <div class="full-height is-flex is-flex-direction-column is-align-items-stretch">
3
+ <div class="page-details is-flex is-flex-direction-row">
4
+ <div class="page-title is-flex-grow-1">
5
+ <transition name="fade" mode="out-in">
6
+ <b-skeleton v-if="loading" key="skeleton"></b-skeleton>
7
+ <span v-else key="name" class="title">Edit Page - {{ model.title }}</span>
8
+ </transition>
9
+ </div>
10
+ <div class="is-flex-grow-1"></div>
11
+ <div>
12
+ <b-switch :value="editable" @input="v => editable = v">Editable?</b-switch>
13
+ <b-button icon-left="cog" @click="editPageModalActive = true">Page Settings</b-button>
14
+ <b-button type="is-primary" icon-left="save" @click="save">Save</b-button>
15
+ </div>
16
+ </div>
17
+ <b-modal :active="editPageModalActive" has-modal-card trap-focus aria-modal auto-focus @update:active="v => editPageModalActive = v">
18
+ <div class="modal-card is-relative">
19
+ <div class="modal-card-head">
20
+ <p class="modal-card-title">Edit Page Settings</p>
21
+ </div>
22
+ <div class="modal-card-body">
23
+ <b-loading :active="loading" :is-full-page="false"></b-loading>
24
+ <div v-if="!loading">
25
+ <b-field label="Title">
26
+ <b-input v-model="model.title" placeholder="Title"></b-input>
27
+ </b-field>
28
+ <b-field>
29
+ <template #label>
30
+ URL
31
+ <b-tooltip multilined position="is-right" type="is-dark" label="The URL at which this page is located. It can only contain letters, numbers, slashes and dashes. Valid examples include: '/', 'my-page-123', 'some/nested/page'">
32
+ <b-icon size="is-small" icon="question-circle"></b-icon>
33
+ </b-tooltip>
34
+ </template>
35
+ <b-input v-model="model.slug" placeholder="slug"></b-input>
36
+ </b-field>
37
+ <b-field label="Description">
38
+ <b-input v-model="model.description" type="textarea" placeholder="page description"></b-input>
39
+ </b-field>
40
+ <b-field>
41
+ <template #label>
42
+ Tags
43
+ <b-tooltip multilined position="is-right" type="is-dark" label="A list of keywords for this page. Used for SEO">
44
+ <b-icon size="is-small" icon="question-circle"></b-icon>
45
+ </b-tooltip>
46
+ </template>
47
+ <b-taginput v-model="model.tags"></b-taginput>
48
+ </b-field>
49
+ <!-- <b-collapse-->
50
+ <!-- class="card"-->
51
+ <!-- animation="slide"-->
52
+ <!-- :open="false">-->
53
+ <!-- <template #trigger="props">-->
54
+ <!-- <div-->
55
+ <!-- class="card-header"-->
56
+ <!-- role="button"-->
57
+ <!-- :aria-expanded="props.open"-->
58
+ <!-- >-->
59
+ <!-- <p class="card-header-title">-->
60
+ <!-- Advanced Options-->
61
+ <!-- </p>-->
62
+ <!-- <a class="card-header-icon">-->
63
+ <!-- <b-icon-->
64
+ <!-- :icon="props.open ? 'caret-down' : 'caret-up'">-->
65
+ <!-- </b-icon>-->
66
+ <!-- </a>-->
67
+ <!-- </div>-->
68
+ <!-- </template>-->
69
+ <!-- <div class="card-content">-->
70
+ <!-- <div class="content">-->
71
+ <div class="field">
72
+ <label class="label">
73
+ Metadata
74
+ <b-tooltip multilined label="An HTML field used to inject custom metadata into the page. Used for SEO." position="is-right" type="is-dark">
75
+ <b-icon size="is-small" icon="question-circle"></b-icon>
76
+ </b-tooltip>
77
+ </label>
78
+ <div class="control">
79
+ <CodeEditor v-model="model.meta" lang="html" height="10rem" />
80
+ </div>
81
+ </div>
82
+ <div class="field">
83
+ <label class="label">Options</label>
84
+ <div class="control">
85
+ <CodeEditor v-model="model.options" lang="json" height="10rem" />
86
+ </div>
87
+ </div>
88
+ <!-- </div>-->
89
+ <!-- </div>-->
90
+ <!-- </b-collapse>-->
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </b-modal>
95
+ <div class="is-relative editor-parent">
96
+ <b-loading :active="loading" :is-full-page="false"></b-loading>
97
+ <ContentEditor v-if="!loading" :editable="editable" :expanded="true" :content="model.richContent" @update:content="v => model.richContent = v" />
98
+ </div>
99
+ </div>
100
+ </template>
101
+
102
+ <script>
103
+ import ContentEditor from "./content/ContentEditor.vue";
104
+ import PagesApi from "../PagesApi";
105
+ import CodeEditor from "./CodeEditor.vue";
106
+ import {morphToNotification} from "../api.js";
107
+ export default {
108
+ name: "PageCreate",
109
+ components: {ContentEditor, CodeEditor},
110
+ data() {
111
+ return {
112
+ loading: true,
113
+ model: null,
114
+ serverModel: null,
115
+ editable: true,
116
+ editPageModalActive: false,
117
+ pagesApi: new PagesApi(),
118
+ // content: { type: 'doc', content: [{type:'paragraph',content:[{type:'text',text:'I’m running Tiptap with Vue.js. 🎉'}]}]}
119
+ }
120
+ },
121
+ mounted() {
122
+ this.fetchData()
123
+ },
124
+ methods: {
125
+ async fetchData() {
126
+ this.loading = true;
127
+ let res = await this.pagesApi.get(this.$route.params.id);
128
+ console.log(res);
129
+ this.setModel(res.item);
130
+ },
131
+ setModel(model) {
132
+ this.loading = false;
133
+ this.model = model;
134
+ this.serverModel = model;
135
+ },
136
+ async save() {
137
+ // this.submittingForm = true;
138
+ let response = await this.pagesApi.update(this.model);
139
+ // this.submittingForm = false;
140
+ this.setModel(response.item);
141
+ this.$buefy.toast.open(morphToNotification(response));
142
+ }
143
+ }
144
+ }
145
+ </script>
146
+
147
+ <style scoped>
148
+ @import "../components/util.css";
149
+
150
+ .page-details {
151
+ padding: 1rem;
152
+ }
153
+
154
+ .editor-parent {
155
+ flex: 1;
156
+ overflow-y: auto;
157
+ }
158
+
159
+ /*.editor-gutter {*/
160
+ /* width: 160px;*/
161
+ /* background-color: #444;*/
162
+ /* box-shadow: inset -5px 0 10px 2px #222;*/
163
+ /*}*/
164
+ </style>
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <tr v-if="pagesByParent[item.id] && pagesByParent[item.id].totalItems > pagesByParent[item.id].itemsPerPage" :class="'row-depth-' + depth">
3
+ <td></td>
4
+ <td colspan="5">
5
+ <div class="is-flex">
6
+ <div class="is-flex-grow-1"></div>
7
+ <b-pagination :total="pagesByParent[item.id].totalItems" :current="pagesByParent[item.id].currentPage" :per-page="pagesByParent[item.id].itemsPerPage" @change="p => paginate(item, p)"></b-pagination>
8
+ </div>
9
+ </td>
10
+ </tr>
11
+ </template>
12
+
13
+ <script>
14
+ export default {
15
+ name: "PageNestedPagination",
16
+ props: {
17
+ item: Object,
18
+ pagesByParent: Object,
19
+ depth: Number,
20
+ paginate: Function
21
+ }
22
+ }
23
+ </script>
24
+
25
+ <style scoped>
26
+
27
+ </style>