maglevcms 1.8.0 → 2.0.0.beta1
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.
- checksums.yaml +4 -4
- data/.yarn/install-state.gz +0 -0
- data/README.md +4 -4
- data/Rakefile +2 -1
- data/app/components/maglev/block_component.rb +1 -1
- data/app/components/maglev/content/base.rb +1 -1
- data/app/components/maglev/content/link.rb +4 -0
- data/app/components/maglev/section_component.rb +2 -2
- data/app/controllers/concerns/maglev/fetchers_concern.rb +3 -1
- data/app/controllers/concerns/maglev/resource_id_concern.rb +10 -0
- data/app/controllers/concerns/maglev/standalone_sections_concern.rb +6 -0
- data/app/controllers/concerns/maglev/{ui_locale_concern.rb → user_interface_locale_concern.rb} +1 -1
- data/app/controllers/maglev/api/collection_items_controller.rb +7 -0
- data/app/controllers/maglev/api/page_clones_controller.rb +1 -2
- data/app/controllers/maglev/api_controller.rb +1 -1
- data/app/controllers/maglev/application_controller.rb +1 -5
- data/app/controllers/maglev/assets/active_storage_proxy_controller.rb +17 -0
- data/app/controllers/maglev/assets/proxy_controller.rb +21 -0
- data/app/controllers/maglev/editor_controller.rb +1 -1
- data/app/controllers/maglev/page_preview_controller.rb +3 -3
- data/app/frontend/admin/controllers/screenshot_controller.js +29 -1
- data/app/frontend/editor/assets/remixicons/clipboard-line.svg +1 -0
- data/app/frontend/editor/assets/remixicons/ri-draggable.svg +1 -0
- data/app/frontend/editor/assets/remixicons/ri-folders-line.svg +1 -0
- data/app/frontend/editor/assets/remixicons/ri-more-fill.svg +1 -0
- data/app/frontend/editor/assets/remixicons/ri-shadow-line.svg +1 -0
- data/app/frontend/editor/components/errors/forbidden.vue +24 -0
- data/app/frontend/editor/components/errors/stale-record.vue +2 -2
- data/app/frontend/editor/components/header-nav/device-toggler.vue +6 -3
- data/app/frontend/editor/components/header-nav/index.vue +13 -42
- data/app/frontend/editor/components/header-nav/locale-toggler/index.vue +1 -1
- data/app/frontend/editor/components/header-nav/page-info.vue +41 -0
- data/app/frontend/editor/components/header-nav/preview-button.vue +6 -1
- data/app/frontend/editor/components/header-nav/separator.vue +1 -1
- data/app/frontend/editor/components/image-library/index.vue +26 -29
- data/app/frontend/editor/components/kit/collection-item-input.vue +9 -0
- data/app/frontend/editor/components/kit/copy-paste-button.vue +26 -0
- data/app/frontend/editor/components/kit/dropdown.vue +5 -0
- data/app/frontend/editor/components/kit/icon-button.vue +24 -0
- data/app/frontend/editor/components/kit/icon.vue +2 -3
- data/app/frontend/editor/components/kit/index.js +4 -2
- data/app/frontend/editor/components/kit/link-input.vue +1 -1
- data/app/frontend/editor/components/kit/modal.vue +1 -1
- data/app/frontend/editor/components/kit/page-icon.vue +1 -1
- data/app/frontend/editor/components/kit/rich-text-input/link-buttons.vue +1 -1
- data/app/frontend/editor/components/kit/select-input.vue +4 -2
- data/app/frontend/editor/components/link-picker/actions.vue +1 -1
- data/app/frontend/editor/components/link-picker/index.vue +1 -1
- data/app/frontend/editor/components/link-picker/page.vue +2 -0
- data/app/frontend/editor/components/page/form/main.vue +1 -1
- data/app/frontend/editor/components/page/list/actions-button.vue +159 -0
- data/app/frontend/editor/components/page/list/index.vue +15 -24
- data/app/frontend/editor/components/page/list/list-item.vue +10 -61
- data/app/frontend/editor/components/section-highlighter/index.vue +47 -46
- data/app/frontend/editor/components/section-list/add-button.vue +39 -0
- data/app/frontend/editor/components/section-list/index.vue +30 -15
- data/app/frontend/editor/components/section-list/list-item.vue +23 -8
- data/app/frontend/editor/components/section-pane/block-list/index.vue +1 -0
- data/app/frontend/editor/components/section-pane/block-list/list-item.vue +9 -6
- data/app/frontend/editor/components/section-pane/block-tree/new-nested-block-button.vue +2 -2
- data/app/frontend/editor/components/section-pane/block-tree/tree-node.vue +2 -2
- data/app/frontend/editor/components/sidebar-nav/index.vue +102 -0
- data/app/frontend/editor/components/sidebar-nav/link.vue +60 -0
- data/app/frontend/editor/components/theme-section-list/category.vue +47 -0
- data/app/frontend/editor/components/theme-section-list/index.vue +7 -31
- data/app/frontend/editor/components/theme-section-list/list-item.vue +10 -3
- data/app/frontend/editor/design/components/modal.scss +1 -1
- data/app/frontend/editor/design/components/tooltip.scss +6 -0
- data/app/frontend/editor/design/transitions.scss +1 -1
- data/app/frontend/editor/layouts/app.vue +1 -1
- data/app/frontend/editor/layouts/default.vue +2 -2
- data/app/frontend/editor/layouts/slide-pane.vue +1 -1
- data/app/frontend/editor/locales/editor.ar.json +265 -0
- data/app/frontend/editor/locales/editor.en.json +23 -15
- data/app/frontend/editor/locales/editor.es.json +18 -10
- data/app/frontend/editor/locales/editor.fr.json +28 -13
- data/app/frontend/editor/locales/editor.pt-BR.json +18 -10
- data/app/frontend/editor/locales/index.js +3 -0
- data/app/frontend/editor/mixins/error-modal.js +7 -2
- data/app/frontend/editor/mixins/preview-transformation.js +0 -1
- data/app/frontend/editor/plugins/i18n.js +2 -17
- data/app/frontend/editor/services/__tests__/section.spec.js +6 -0
- data/app/frontend/editor/services/collection-item.js +8 -0
- data/app/frontend/editor/services/page.js +1 -1
- data/app/frontend/editor/services/section.js +30 -0
- data/app/frontend/editor/services/site.js +4 -4
- data/app/frontend/editor/services/theme.js +1 -0
- data/app/frontend/editor/spec/__mocks__/page.js +3 -1
- data/app/frontend/editor/spec/__mocks__/services.js +1 -0
- data/app/frontend/editor/store/__tests__/getters.spec.js +56 -1
- data/app/frontend/editor/store/actions/index.js +1 -0
- data/app/frontend/editor/store/actions/site.js +8 -4
- data/app/frontend/editor/store/getters.js +11 -0
- data/app/frontend/editor/views/page-preview.vue +2 -1
- data/app/frontend/images/favicon.svg +11 -0
- data/app/frontend/images/logo.svg +14 -0
- data/app/frontend/live-preview-client/iframe-decorator.js +17 -15
- data/app/frontend/live-preview-client/rails.js +1 -1
- data/app/helpers/maglev/application_helper.rb +7 -2
- data/app/helpers/maglev/editor_helper.rb +12 -5
- data/app/models/concerns/maglev/sections_concern.rb +4 -0
- data/app/models/maglev/asset.rb +13 -14
- data/app/models/maglev/page/path_concern.rb +23 -8
- data/app/models/maglev/page.rb +17 -18
- data/app/models/maglev/page_path.rb +18 -18
- data/app/models/maglev/section/setting.rb +1 -1
- data/app/models/maglev/section.rb +18 -9
- data/app/models/maglev/setting_types/link.rb +1 -1
- data/app/models/maglev/site.rb +13 -24
- data/app/services/maglev/app_container.rb +2 -2
- data/app/services/maglev/extract_locale.rb +3 -2
- data/app/services/maglev/fetch_page.rb +2 -0
- data/app/services/maglev/fetch_theme_layout.rb +2 -0
- data/app/services/maglev/get_page_fullpath.rb +1 -2
- data/app/services/maglev/remove_section_type.rb +50 -0
- data/app/services/maglev/rename_section_type.rb +57 -0
- data/app/services/maglev/search_pages.rb +2 -1
- data/app/views/layouts/maglev/admin/application.html.erb +1 -1
- data/app/views/maglev/admin/sections/previews/show.html.erb +16 -1
- data/app/views/maglev/api/collection_items/show.json.jbuilder +7 -0
- data/app/views/maglev/api/page_clones/create.json.jbuilder +3 -0
- data/app/views/maglev/editor/show.html.erb +6 -5
- data/config/locales/activerecord.ar.yml +13 -0
- data/config/locales/activerecord.en.yml +8 -2
- data/config/locales/activerecord.es.yml +4 -0
- data/config/locales/activerecord.fr.yml +6 -2
- data/config/locales/activerecord.pt-BR.yml +13 -0
- data/config/routes.rb +3 -4
- data/db/migrate/20200831101942_create_maglev_section_content.rb +10 -2
- data/db/migrate/20210819092740_switch_to_localized_page_fields.rb +13 -3
- data/db/migrate/20211008064437_add_locales_to_sites.rb +7 -1
- data/db/migrate/20211013210954_translate_section_content.rb +16 -2
- data/db/migrate/20211203224112_add_open_graph_tags_to_pages.rb +9 -3
- data/db/migrate/20220612092235_add_style_to_sites.rb +5 -1
- data/lib/commands/maglev/change_site_locales_command.rb +61 -0
- data/lib/commands/maglev/create_site_command.rb +28 -0
- data/lib/commands/maglev/sections/remove_command.rb +48 -0
- data/lib/commands/maglev/sections/rename_command.rb +49 -0
- data/lib/generators/maglev/templates/install/config/initializers/maglev.rb +4 -1
- data/lib/generators/maglev/templates/theme/app/views/theme/layout.html.erb.tt +8 -2
- data/lib/maglev/errors.rb +1 -0
- data/lib/maglev/preview_constraint.rb +5 -1
- data/lib/maglev/theme_filesystem_loader.rb +7 -0
- data/lib/maglev/version.rb +1 -1
- data/lib/maglev.rb +13 -3
- data/lib/tasks/maglev/icons.rake +123 -0
- data/lib/tasks/maglev/vite.rake +62 -0
- data/lib/tasks/maglev_tasks.rake +9 -107
- data/tailwind.config.js +1 -0
- metadata +48 -33
- data/app/controllers/maglev/assets_controller.rb +0 -10
- data/app/frontend/editor/components/kit/list-item-button.vue +0 -16
- data/app/frontend/editor/components/sidebar-nav.vue +0 -125
- data/app/frontend/editor/plugins/maglev_dummy.js +0 -2
- data/app/frontend/images/favicon.png +0 -0
- data/app/frontend/images/logo.png +0 -0
@@ -66,5 +66,14 @@ export default {
|
|
66
66
|
},
|
67
67
|
},
|
68
68
|
},
|
69
|
+
watch: {
|
70
|
+
value: {
|
71
|
+
async handler(value) {
|
72
|
+
if (value === 'any')
|
73
|
+
this.selectedCollectionItem = await this.services.collectionItem.findOne(this.collectionId, 'any')
|
74
|
+
},
|
75
|
+
immediate: true,
|
76
|
+
},
|
77
|
+
}
|
69
78
|
}
|
70
79
|
</script>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<template>
|
2
|
+
<button @click.stop.prevent="copyToClipboard">
|
3
|
+
<uikit-icon name="clipboard-line" size="0.75rem" v-if="!copied" class="text-gray-500 hover:text-gray-900 transition-colors duration-200" />
|
4
|
+
<uikit-icon name="ri-check-line" size="0.75rem" class="text-green-500" v-else />
|
5
|
+
</button>
|
6
|
+
</template>
|
7
|
+
|
8
|
+
<script>
|
9
|
+
export default {
|
10
|
+
name: 'CopyPasteButton',
|
11
|
+
props: {
|
12
|
+
textToCopy: { type: String, required: true },
|
13
|
+
},
|
14
|
+
data() {
|
15
|
+
return { copied: false }
|
16
|
+
},
|
17
|
+
methods: {
|
18
|
+
copyToClipboard() {
|
19
|
+
this.copied = true
|
20
|
+
navigator.clipboard.writeText(this.textToCopy)
|
21
|
+
const timeout = setTimeout((() => { this.copied = false }).bind(this), 2000)
|
22
|
+
this.$once('hook:beforeDestroy', () => clearTimeout(timeout))
|
23
|
+
},
|
24
|
+
},
|
25
|
+
}
|
26
|
+
</script>
|
@@ -6,6 +6,7 @@
|
|
6
6
|
:placement="placement"
|
7
7
|
class="flex"
|
8
8
|
v-on:auto-hide="close"
|
9
|
+
:popoverClass="popoverClass"
|
9
10
|
>
|
10
11
|
<div
|
11
12
|
class="z-10 relative flex items-center focus:outline-none select-none cursor-pointer w-full"
|
@@ -32,6 +33,10 @@ export default {
|
|
32
33
|
validator: (value) =>
|
33
34
|
['top', 'bottom', 'right', 'left'].indexOf(value) !== -1,
|
34
35
|
},
|
36
|
+
popoverClass: {
|
37
|
+
type: String,
|
38
|
+
default: ''
|
39
|
+
}
|
35
40
|
},
|
36
41
|
data() {
|
37
42
|
return {
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<template>
|
2
|
+
<button
|
3
|
+
class="h-7 w-7 flex items-center justify-center rounded-full focus:outline-none transition-colors duration-200"
|
4
|
+
:class="{
|
5
|
+
'bg-gray-600 text-gray-200 hover:bg-gray-900 hover:text-gray-100': dark,
|
6
|
+
'bg-gray-600 bg-opacity-0 hover:text-gray-900 text-gray-800 hover:bg-opacity-10': !dark
|
7
|
+
}"
|
8
|
+
v-on="$listeners"
|
9
|
+
v-bind="$attrs"
|
10
|
+
>
|
11
|
+
<uikit-icon :name="iconName" size="1.15rem" />
|
12
|
+
</button>
|
13
|
+
</template>
|
14
|
+
|
15
|
+
<script>
|
16
|
+
export default {
|
17
|
+
name: 'UIKitIconButton',
|
18
|
+
inheritAttrs: false,
|
19
|
+
props: {
|
20
|
+
iconName: { type: String, required: true },
|
21
|
+
dark: { type: Boolean, default: false },
|
22
|
+
},
|
23
|
+
}
|
24
|
+
</script>
|
@@ -28,9 +28,8 @@ export default {
|
|
28
28
|
},
|
29
29
|
computed: {
|
30
30
|
icon() {
|
31
|
-
|
32
|
-
|
33
|
-
)
|
31
|
+
const path = `../../assets/${this.library}/${this.name}.svg`
|
32
|
+
return defineAsyncComponent(() => this.icons[path]())
|
34
33
|
},
|
35
34
|
},
|
36
35
|
}
|
@@ -8,6 +8,7 @@ import Accordion from './accordion.vue'
|
|
8
8
|
import Dropdown from './dropdown.vue'
|
9
9
|
import ConfirmationButton from './confirmation-button.vue'
|
10
10
|
import SubmitButton from './submit-button.vue'
|
11
|
+
import CopyPasteButton from './copy-paste-button.vue'
|
11
12
|
import ImageInput from './image-input.vue'
|
12
13
|
import IconInput from './icon-input.vue'
|
13
14
|
import LinkInput from './link-input.vue'
|
@@ -22,9 +23,9 @@ import PageIcon from './page-icon.vue'
|
|
22
23
|
import ColorInput from './color-input.vue'
|
23
24
|
import SimpleSelect from './simple-select.vue'
|
24
25
|
import CollectionItemInput from './collection-item-input.vue'
|
25
|
-
import ListItemButton from './list-item-button.vue'
|
26
26
|
import Divider from './divider.vue'
|
27
27
|
import Hint from './hint.vue'
|
28
|
+
import IconButton from './icon-button.vue'
|
28
29
|
|
29
30
|
Vue.component('v-popoper', VPopover)
|
30
31
|
Vue.component('uikit-icon', Icon)
|
@@ -49,6 +50,7 @@ Vue.component('uikit-page-icon', PageIcon)
|
|
49
50
|
Vue.component('uikit-color-input', ColorInput)
|
50
51
|
Vue.component('uikit-simple-select', SimpleSelect)
|
51
52
|
Vue.component('uikit-collection-item-input', CollectionItemInput)
|
52
|
-
Vue.component('uikit-list-item-button', ListItemButton)
|
53
53
|
Vue.component('uikit-divider', Divider)
|
54
54
|
Vue.component('uikit-hint', Hint)
|
55
|
+
Vue.component('uikit-icon-button', IconButton)
|
56
|
+
Vue.component('uikit-copy-paste-button', CopyPasteButton)
|
@@ -110,7 +110,7 @@ export default {
|
|
110
110
|
this.openModal({
|
111
111
|
title: this.$t('linkPicker.title'),
|
112
112
|
component: LinkPicker,
|
113
|
-
props: { currentLink: this.value },
|
113
|
+
props: { currentLink: this.value, modalClass: 'h-144 w-120' },
|
114
114
|
listeners: {
|
115
115
|
select: (link) => this.setLink(link),
|
116
116
|
},
|
@@ -77,7 +77,7 @@ export default {
|
|
77
77
|
this.openModal({
|
78
78
|
title: this.$t('linkPicker.insertTitle'),
|
79
79
|
component: LinkPicker,
|
80
|
-
props: { currentLink: this.sanitizeLink(), mode: 'insert' },
|
80
|
+
props: { currentLink: this.sanitizeLink(), mode: 'insert', modalClass: 'h-144 w-120' },
|
81
81
|
listeners: {
|
82
82
|
select: (link) => this.setLink(link),
|
83
83
|
},
|
@@ -45,7 +45,7 @@
|
|
45
45
|
</div>
|
46
46
|
</button>
|
47
47
|
<div
|
48
|
-
class="absolute w-full z-
|
48
|
+
class="absolute w-full z-20 -mt-1 rounded-b shadow-sm bg-gray-100"
|
49
49
|
@keydown="naviguate"
|
50
50
|
v-if="isOpen"
|
51
51
|
>
|
@@ -57,6 +57,7 @@
|
|
57
57
|
v-model="q"
|
58
58
|
:placeholder="searchPlaceholder"
|
59
59
|
ref="input"
|
60
|
+
autocomplete="off"
|
60
61
|
/>
|
61
62
|
</div>
|
62
63
|
|
@@ -64,7 +65,7 @@
|
|
64
65
|
{{ emptyLabel }}
|
65
66
|
</div>
|
66
67
|
|
67
|
-
<div v-if="list">
|
68
|
+
<div v-if="list" :class="listClass">
|
68
69
|
<div
|
69
70
|
v-for="(item, index) in list"
|
70
71
|
:key="item.id"
|
@@ -106,6 +107,7 @@ export default {
|
|
106
107
|
clearEnabled: { type: Boolean, default: false },
|
107
108
|
withLabel: { type: Boolean, default: true },
|
108
109
|
buttonClass: { type: [Object, String], default: () => ({}) },
|
110
|
+
listClass: { type: [Object, String], default: () => ({}) },
|
109
111
|
},
|
110
112
|
data() {
|
111
113
|
return {
|
@@ -8,6 +8,7 @@
|
|
8
8
|
:emptyLabel="$t(`linkPicker.page.input.emptyLabel`)"
|
9
9
|
:fetchList="(q) => services.page.findAll({ q })"
|
10
10
|
v-model="page"
|
11
|
+
listClass="overflow-y-scroll h-48"
|
11
12
|
>
|
12
13
|
<template v-slot:value>
|
13
14
|
{{ page.title }}
|
@@ -33,6 +34,7 @@
|
|
33
34
|
:fetchList="() => fetchPageSectionNames()"
|
34
35
|
:clearEnabled="true"
|
35
36
|
v-model="pageSection"
|
37
|
+
listClass="overflow-y-scroll h-48"
|
36
38
|
>
|
37
39
|
<template v-slot:value>
|
38
40
|
{{ pageSection.name }}
|
@@ -0,0 +1,159 @@
|
|
1
|
+
<template>
|
2
|
+
<uikit-dropdown v-on="$listeners" popoverClass="tooltip-menu" ref="dropdown">
|
3
|
+
<template v-slot:button>
|
4
|
+
<uikit-icon-button :iconName="compact ? 'ri-more-fill' : 'ri-more-2-fill'" class="shrink-0" />
|
5
|
+
</template>
|
6
|
+
<template v-slot:content>
|
7
|
+
<div class="flex flex-col w-48 text-gray-800">
|
8
|
+
<button
|
9
|
+
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
10
|
+
@click.stop="editPage"
|
11
|
+
>
|
12
|
+
<uikit-icon name="ri-settings-5-line" />
|
13
|
+
<span class="ml-2 whitespace-nowrap">{{
|
14
|
+
$t('page.actions.edit')
|
15
|
+
}}</span>
|
16
|
+
</button>
|
17
|
+
<button
|
18
|
+
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
19
|
+
@click.stop="clonePage"
|
20
|
+
>
|
21
|
+
<uikit-icon name="ri-shadow-line" />
|
22
|
+
<span class="ml-2">{{ $t('page.actions.clone') }}</span>
|
23
|
+
</button>
|
24
|
+
<button
|
25
|
+
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
26
|
+
@click.stop="hidePage"
|
27
|
+
v-if="!compact &&isVisible"
|
28
|
+
>
|
29
|
+
<uikit-icon name="ri-eye-off-line" />
|
30
|
+
<span class="ml-2">{{ $t('page.actions.hide') }}</span>
|
31
|
+
</button>
|
32
|
+
|
33
|
+
<button
|
34
|
+
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
35
|
+
@click.stop="showPage"
|
36
|
+
v-if="!compact && !isVisible"
|
37
|
+
>
|
38
|
+
<uikit-icon name="ri-eye-line" />
|
39
|
+
<span class="ml-2">{{ $t('page.actions.show') }}</span>
|
40
|
+
</button>
|
41
|
+
|
42
|
+
<button
|
43
|
+
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
44
|
+
:class="{ 'text-green-500': copied }"
|
45
|
+
@click.stop="copyUrlToClipboard"
|
46
|
+
>
|
47
|
+
<uikit-icon name="clipboard-line" v-if="!copied" />
|
48
|
+
<uikit-icon name="ri-check-line" v-else />
|
49
|
+
<span class="ml-2" v-if="!copied">{{ $t('page.actions.copyUrlToClipboard') }}</span>
|
50
|
+
<span class="ml-2" v-else>{{ $t('page.actions.copyUrlToClipboardSuccess') }}</span>
|
51
|
+
</button>
|
52
|
+
|
53
|
+
<uikit-confirmation-button
|
54
|
+
@confirm="deletePage"
|
55
|
+
ref="deleteDropdown"
|
56
|
+
v-if="!isIndexPage"
|
57
|
+
>
|
58
|
+
<button
|
59
|
+
class="flex items-center w-full px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
60
|
+
>
|
61
|
+
<uikit-icon name="delete-bin-line" />
|
62
|
+
<span class="ml-2">{{ $t('page.actions.delete') }}</span>
|
63
|
+
</button>
|
64
|
+
</uikit-confirmation-button>
|
65
|
+
</div>
|
66
|
+
</template>
|
67
|
+
</uikit-dropdown>
|
68
|
+
</template>
|
69
|
+
|
70
|
+
<script>
|
71
|
+
import { mapGetters } from 'vuex'
|
72
|
+
import EditPageModal from '@/components/page/edit.vue'
|
73
|
+
|
74
|
+
export default {
|
75
|
+
name: 'PageActionsButton',
|
76
|
+
props: {
|
77
|
+
page: { type: Object, required: true },
|
78
|
+
compact: { type: Boolean, default: false },
|
79
|
+
},
|
80
|
+
data() {
|
81
|
+
return {
|
82
|
+
copied: false,
|
83
|
+
}
|
84
|
+
},
|
85
|
+
computed: {
|
86
|
+
...mapGetters(['currentPageUrl']),
|
87
|
+
isIndexPage() {
|
88
|
+
return this.services.page.isIndex(this.page)
|
89
|
+
},
|
90
|
+
isCurrentPage() {
|
91
|
+
return this.currentPage.id === this.page.id
|
92
|
+
},
|
93
|
+
isVisible() {
|
94
|
+
return this.page.visible
|
95
|
+
},
|
96
|
+
},
|
97
|
+
methods: {
|
98
|
+
copyUrlToClipboard() {
|
99
|
+
this.copied = true
|
100
|
+
navigator.clipboard.writeText(this.currentPageUrl)
|
101
|
+
const timeout = setTimeout((() => { this.copied = false }).bind(this), 2000)
|
102
|
+
this.$once('hook:beforeDestroy', () => clearTimeout(timeout))
|
103
|
+
},
|
104
|
+
editPage() {
|
105
|
+
this.$refs.dropdown.close()
|
106
|
+
if (!this.compact) {
|
107
|
+
this.openEditPageModal()
|
108
|
+
} else if (this.$route.name !== 'editPageSettings') {
|
109
|
+
this.$router.push({ name: 'editPageSettings' })
|
110
|
+
}
|
111
|
+
},
|
112
|
+
openEditPageModal() {
|
113
|
+
this.openModal({
|
114
|
+
title: this.$t('page.edit.title'),
|
115
|
+
component: EditPageModal,
|
116
|
+
closeOnClick: false,
|
117
|
+
props: {
|
118
|
+
page: this.page,
|
119
|
+
insideModal: true,
|
120
|
+
modalClass: 'w-120 h-144',
|
121
|
+
},
|
122
|
+
listeners: {
|
123
|
+
'on-update': (editedPage) => this.onUpdate(editedPage),
|
124
|
+
},
|
125
|
+
})
|
126
|
+
},
|
127
|
+
onUpdate(editedPage) {
|
128
|
+
this.closeDropdown()
|
129
|
+
if (this.isCurrentPage) this.setCurrentPageSettings(editedPage)
|
130
|
+
this.$emit('on-update')
|
131
|
+
},
|
132
|
+
clonePage() {
|
133
|
+
this.services.page.clone(this.page.id).then(page => this.$emit('on-clone', page))
|
134
|
+
},
|
135
|
+
showPage() {
|
136
|
+
this.services.page
|
137
|
+
.setVisible(this.page.id, true)
|
138
|
+
.then(() => this.$emit('on-update'))
|
139
|
+
},
|
140
|
+
hidePage() {
|
141
|
+
this.services.page
|
142
|
+
.setVisible(this.page.id, false)
|
143
|
+
.then(() => this.$emit('on-update'))
|
144
|
+
},
|
145
|
+
deletePage() {
|
146
|
+
this.closeDropdown()
|
147
|
+
this.services.page.destroy(this.page.id).then(() => {
|
148
|
+
this.$emit('on-delete')
|
149
|
+
if (this.isCurrentPage)
|
150
|
+
this.$router.push({ name: 'editPage', params: { pageId: 'index' } })
|
151
|
+
})
|
152
|
+
},
|
153
|
+
closeDropdown() {
|
154
|
+
if (!this.$refs.deleteDropdown) return
|
155
|
+
this.$refs.deleteDropdown.close()
|
156
|
+
},
|
157
|
+
}
|
158
|
+
}
|
159
|
+
</script>
|
@@ -1,28 +1,19 @@
|
|
1
1
|
<template>
|
2
2
|
<div>
|
3
|
-
<
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
:key="page.id"
|
18
|
-
:page="page"
|
19
|
-
@on-update="fetch"
|
20
|
-
@on-clone="fetch"
|
21
|
-
@on-delete="fetch"
|
22
|
-
@on-dropdown-toggle="onDropdownToggle"
|
23
|
-
/>
|
24
|
-
</div>
|
25
|
-
</transition>
|
3
|
+
<div key="empty-list" class="pt-4 text-center" v-if="isEmpty">
|
4
|
+
No pages found
|
5
|
+
</div>
|
6
|
+
<div key="list" v-else>
|
7
|
+
<list-item
|
8
|
+
v-for="page in previewablePages"
|
9
|
+
:key="page.id"
|
10
|
+
:page="page"
|
11
|
+
@on-update="fetch"
|
12
|
+
@on-clone="fetch"
|
13
|
+
@on-delete="fetch"
|
14
|
+
@on-dropdown-toggle="onDropdownToggle"
|
15
|
+
/>
|
16
|
+
</div>
|
26
17
|
</div>
|
27
18
|
</template>
|
28
19
|
|
@@ -42,7 +33,7 @@ export default {
|
|
42
33
|
},
|
43
34
|
computed: {
|
44
35
|
isEmpty() {
|
45
|
-
return this.previewablePages.length === 0
|
36
|
+
return this.previewablePages.length === 0 && !this.isLoading
|
46
37
|
},
|
47
38
|
previewablePages() {
|
48
39
|
return this.pages.filter((page) => !!page.previewUrl && !page.static)
|
@@ -1,13 +1,14 @@
|
|
1
1
|
<template>
|
2
2
|
<div
|
3
|
-
class="flex items-center
|
3
|
+
class="flex items-center pl-6 pr-2 hover:bg-editor-primary hover:bg-opacity-5 transition-colors duration-200"
|
4
4
|
>
|
5
5
|
<router-link
|
6
6
|
:to="{ name: 'editPage', params: { pageId: page.path } }"
|
7
|
-
class="flex
|
7
|
+
class="flex items-center text-gray-800 overflow-hidden w-full py-3.5"
|
8
|
+
:title="page.title"
|
8
9
|
>
|
9
|
-
<uikit-page-icon :page="page" />
|
10
|
-
<span class="ml-4">{{ page.title }}</span>
|
10
|
+
<uikit-page-icon :page="page" class="shrink-0" />
|
11
|
+
<span class="ml-4 truncate">{{ page.title }}</span>
|
11
12
|
<uikit-icon
|
12
13
|
class="ml-4 text-gray-400"
|
13
14
|
name="ri-eye-off-line"
|
@@ -16,72 +17,20 @@
|
|
16
17
|
/>
|
17
18
|
</router-link>
|
18
19
|
<div class="ml-auto pr-2 relative">
|
19
|
-
<
|
20
|
-
<template v-slot:button>
|
21
|
-
<button
|
22
|
-
class="px-1 py-1 rounded-full bg-editor-primary bg-opacity-0 hover:text-gray-900 text-gray-600 focus:outline-none hover:bg-opacity-10 transition-colors duration-200"
|
23
|
-
>
|
24
|
-
<uikit-icon name="ri-more-2-fill" size="1.25rem" />
|
25
|
-
</button>
|
26
|
-
</template>
|
27
|
-
<template v-slot:content>
|
28
|
-
<div class="flex flex-col w-48 text-gray-800">
|
29
|
-
<button
|
30
|
-
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
31
|
-
@click.stop="openEditPageModal"
|
32
|
-
>
|
33
|
-
<uikit-icon name="ri-settings-5-line" />
|
34
|
-
<span class="ml-2 whitespace-nowrap">{{
|
35
|
-
$t('page.list.item.edit')
|
36
|
-
}}</span>
|
37
|
-
</button>
|
38
|
-
<button
|
39
|
-
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
40
|
-
@click.stop="clonePage"
|
41
|
-
>
|
42
|
-
<uikit-icon name="ri-file-copy-line" />
|
43
|
-
<span class="ml-2">{{ $t('page.list.item.clone') }}</span>
|
44
|
-
</button>
|
45
|
-
<button
|
46
|
-
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
47
|
-
@click.stop="hidePage"
|
48
|
-
v-if="isVisible"
|
49
|
-
>
|
50
|
-
<uikit-icon name="ri-eye-off-line" />
|
51
|
-
<span class="ml-2">{{ $t('page.list.item.hide') }}</span>
|
52
|
-
</button>
|
53
|
-
<button
|
54
|
-
class="flex items-center px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
55
|
-
@click.stop="showPage"
|
56
|
-
v-if="!isVisible"
|
57
|
-
>
|
58
|
-
<uikit-icon name="ri-eye-line" />
|
59
|
-
<span class="ml-2">{{ $t('page.list.item.show') }}</span>
|
60
|
-
</button>
|
61
|
-
<uikit-confirmation-button
|
62
|
-
@confirm="deletePage"
|
63
|
-
ref="deleteDropdown"
|
64
|
-
v-if="!isIndexPage"
|
65
|
-
>
|
66
|
-
<button
|
67
|
-
class="flex items-center w-full px-4 py-4 hover:bg-gray-100 transition-colors duration-200 focus:outline-none"
|
68
|
-
>
|
69
|
-
<uikit-icon name="delete-bin-line" />
|
70
|
-
<span class="ml-2">{{ $t('page.list.item.delete') }}</span>
|
71
|
-
</button>
|
72
|
-
</uikit-confirmation-button>
|
73
|
-
</div>
|
74
|
-
</template>
|
75
|
-
</uikit-dropdown>
|
20
|
+
<page-actions-button :page="page" v-on="$listeners" />
|
76
21
|
</div>
|
77
22
|
</div>
|
78
23
|
</template>
|
79
24
|
|
80
25
|
<script>
|
81
26
|
import EditPageModal from '@/components/page/edit.vue'
|
27
|
+
import PageActionsButton from './actions-button.vue'
|
82
28
|
|
83
29
|
export default {
|
84
30
|
name: 'PageListItem',
|
31
|
+
components: {
|
32
|
+
PageActionsButton,
|
33
|
+
},
|
85
34
|
props: {
|
86
35
|
page: { type: Object, required: true },
|
87
36
|
},
|