@blokkli/editor 2.0.0-alpha.14 → 2.0.0-alpha.15
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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +867 -2
- package/dist/modules/drupal/graphql/base/fragment.paragraphsBlokkliEditState.graphql +4 -0
- package/dist/modules/drupal/graphql/features/publishNew.graphql +1 -4
- package/dist/modules/drupal/graphql/features/scheduler.graphql +31 -0
- package/dist/modules/drupal/index.mjs +17 -0
- package/dist/modules/drupal/runtime/adapter/index.js +31 -1
- package/dist/runtime/adapter/index.d.ts +36 -0
- package/dist/runtime/blokkliPlugins/MenuButton/index.vue.d.ts +2 -2
- package/dist/runtime/components/Edit/Features/EntityTitle/index.vue +33 -1
- package/dist/runtime/components/Edit/Features/Publish/Dialog/Item.vue +41 -14
- package/dist/runtime/components/Edit/Features/Publish/Dialog/Item.vue.d.ts +2 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/PublishOption.vue +47 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/PublishOption.vue.d.ts +19 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/ScheduleDate.vue +183 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/ScheduleDate.vue.d.ts +13 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/Summary.vue +83 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/Summary.vue.d.ts +9 -0
- package/dist/runtime/components/Edit/Features/Publish/Dialog/index.vue +381 -129
- package/dist/runtime/components/Edit/Features/Publish/Dialog/index.vue.d.ts +3 -14
- package/dist/runtime/components/Edit/Features/Publish/index.vue +34 -20
- package/dist/runtime/components/Edit/Form/Datepicker/index.vue +198 -0
- package/dist/runtime/components/Edit/Form/Datepicker/index.vue.d.ts +15 -0
- package/dist/runtime/components/Edit/Konami/Game/index.vue +8 -1
- package/dist/runtime/components/Edit/index.d.ts +2 -1
- package/dist/runtime/components/Edit/index.js +2 -0
- package/dist/runtime/css/output.css +1 -1
- package/dist/runtime/helpers/stateProvider.d.ts +2 -1
- package/dist/runtime/helpers/stateProvider.js +31 -0
- package/dist/runtime/helpers/uiProvider.d.ts +2 -0
- package/dist/runtime/helpers/uiProvider.js +23 -0
- package/dist/runtime/icons/calendar-clock.svg +1 -0
- package/dist/runtime/icons/eye-off.svg +1 -0
- package/dist/runtime/types/index.d.ts +9 -1
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
mutation pbUnschedule(
|
|
2
|
+
$entityType: EntityType!
|
|
3
|
+
$entityUuid: String!
|
|
4
|
+
$langcode: String
|
|
5
|
+
) {
|
|
6
|
+
state: paragraphsEditMutationState(
|
|
7
|
+
entityType: $entityType
|
|
8
|
+
entityUuid: $entityUuid
|
|
9
|
+
) {
|
|
10
|
+
action: unschedule {
|
|
11
|
+
...paragraphsBlokkliMutationResult
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
mutation pbSchedule(
|
|
17
|
+
$entityType: EntityType!
|
|
18
|
+
$entityUuid: String!
|
|
19
|
+
$langcode: String
|
|
20
|
+
$date: String!
|
|
21
|
+
$revisionLogMessage: String
|
|
22
|
+
) {
|
|
23
|
+
state: paragraphsEditMutationState(
|
|
24
|
+
entityType: $entityType
|
|
25
|
+
entityUuid: $entityUuid
|
|
26
|
+
) {
|
|
27
|
+
action: schedule(date: $date, revisionLogMessage: $revisionLogMessage) {
|
|
28
|
+
...paragraphsBlokkliMutationResult
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -181,6 +181,23 @@ const index = defineBlokkliModule({
|
|
|
181
181
|
}
|
|
182
182
|
if (editStateFields.has("publishOptions") && queryFields.has("pbSearchEditStates")) {
|
|
183
183
|
addGraphqlDocument("features/publishNew.graphql");
|
|
184
|
+
const publishOptionsFragmentFields = [
|
|
185
|
+
"canPublish",
|
|
186
|
+
"isRevisionable",
|
|
187
|
+
"hasRevisionLogMessage",
|
|
188
|
+
"lastChanged",
|
|
189
|
+
"revisionLogMessage"
|
|
190
|
+
];
|
|
191
|
+
if (editMutationStateFields.has("unschedule")) {
|
|
192
|
+
addGraphqlDocument("features/scheduler.graphql");
|
|
193
|
+
publishOptionsFragmentFields.push("canSchedule", "publishOn");
|
|
194
|
+
}
|
|
195
|
+
const publishOptionsFragment = `
|
|
196
|
+
fragment paragraphsBlokkliPublishOptions on ParagraphsBlokkliPublishOptions {
|
|
197
|
+
${publishOptionsFragmentFields.join("\n ")}
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
graphql.addDocument("blokkli:publishOptions", publishOptionsFragment);
|
|
184
201
|
} else {
|
|
185
202
|
addGraphqlDocument("features/publish.graphql");
|
|
186
203
|
}
|
|
@@ -10,6 +10,17 @@ import {
|
|
|
10
10
|
useRouter
|
|
11
11
|
} from "#imports";
|
|
12
12
|
import { ParagraphsBlokkliRemoteVideoProvider } from "#graphql-operations";
|
|
13
|
+
function mapPublishOptions(publishOptions) {
|
|
14
|
+
return {
|
|
15
|
+
canPublish: !!publishOptions.canPublish,
|
|
16
|
+
isRevisionable: !!publishOptions.isRevisionable,
|
|
17
|
+
hasRevisionLogMessage: !!publishOptions.hasRevisionLogMessage,
|
|
18
|
+
lastChanged: publishOptions.lastChanged ?? null,
|
|
19
|
+
canSchedule: !!publishOptions.canSchedule,
|
|
20
|
+
publishOn: publishOptions.publishOn ?? null,
|
|
21
|
+
revisionLogMessage: publishOptions.revisionLogMessage ?? null
|
|
22
|
+
};
|
|
23
|
+
}
|
|
13
24
|
function mapPluginConfigInputs(inputs) {
|
|
14
25
|
return inputs.map((input) => {
|
|
15
26
|
if (input.__typename === "ParagraphsBlokkliConfigInputText") {
|
|
@@ -136,6 +147,10 @@ export default defineBlokkliEditAdapter(
|
|
|
136
147
|
const fields = state?.mutatedState?.fields || [];
|
|
137
148
|
const violations = state.mutatedState?.violations || [];
|
|
138
149
|
const entity = state.entity;
|
|
150
|
+
if (!state.publishOptions) {
|
|
151
|
+
throw new Error("Missing publish options.");
|
|
152
|
+
}
|
|
153
|
+
const publishOptions = mapPublishOptions(state.publishOptions);
|
|
139
154
|
const mutatedOptions = state.mutatedState?.mutatedOptions || {};
|
|
140
155
|
Object.keys(mutatedOptions).forEach((uuid) => {
|
|
141
156
|
mutatedOptions[uuid] = mutatedOptions[uuid]?.paragraphs_blokkli_data || {};
|
|
@@ -175,6 +190,7 @@ export default defineBlokkliEditAdapter(
|
|
|
175
190
|
// PHP and its arrays...
|
|
176
191
|
mutatedHostOptions: Array.isArray(mutatedHostOptions) ? {} : mutatedHostOptions
|
|
177
192
|
},
|
|
193
|
+
publishOptions,
|
|
178
194
|
entity,
|
|
179
195
|
mutatedEntity: state.mutatedEntity,
|
|
180
196
|
translationState
|
|
@@ -324,7 +340,7 @@ export default defineBlokkliEditAdapter(
|
|
|
324
340
|
if (!options) {
|
|
325
341
|
throw new Error("Failed to load publish options.");
|
|
326
342
|
}
|
|
327
|
-
return options;
|
|
343
|
+
return mapPublishOptions(options);
|
|
328
344
|
});
|
|
329
345
|
}
|
|
330
346
|
if (hasQuery("pbGetImportSourceEntities")) {
|
|
@@ -865,6 +881,20 @@ export default defineBlokkliEditAdapter(
|
|
|
865
881
|
});
|
|
866
882
|
};
|
|
867
883
|
}
|
|
884
|
+
if (hasMutation("pbUnschedule")) {
|
|
885
|
+
adapter.unscheduleEditState = (options) => useGraphqlMutation("pbUnschedule", {
|
|
886
|
+
entityType: options.hostEntityType.toUpperCase(),
|
|
887
|
+
entityUuid: options.hostEntityUuid
|
|
888
|
+
}).then(mapMutation);
|
|
889
|
+
}
|
|
890
|
+
if (hasMutation("pbSchedule")) {
|
|
891
|
+
adapter.scheduleEditState = (options) => useGraphqlMutation("pbSchedule", {
|
|
892
|
+
entityType: options.hostEntityType.toUpperCase(),
|
|
893
|
+
entityUuid: options.hostEntityUuid,
|
|
894
|
+
date: options.date,
|
|
895
|
+
revisionLogMessage: options.revisionLogMessage
|
|
896
|
+
}).then(mapMutation);
|
|
897
|
+
}
|
|
868
898
|
return adapter;
|
|
869
899
|
}
|
|
870
900
|
);
|
|
@@ -161,6 +161,34 @@ export type BlokkliAdapterPublishOptions = {
|
|
|
161
161
|
*/
|
|
162
162
|
revisionLogMessage?: string;
|
|
163
163
|
};
|
|
164
|
+
export type BlokkliAdapterScheduleOptions = {
|
|
165
|
+
/**
|
|
166
|
+
* The host entity type.
|
|
167
|
+
*/
|
|
168
|
+
hostEntityType: string;
|
|
169
|
+
/**
|
|
170
|
+
* The host entity UUID.
|
|
171
|
+
*/
|
|
172
|
+
hostEntityUuid: string;
|
|
173
|
+
/**
|
|
174
|
+
* The revision log message.
|
|
175
|
+
*/
|
|
176
|
+
revisionLogMessage?: string;
|
|
177
|
+
/**
|
|
178
|
+
* The date and time when the edit state should be published.
|
|
179
|
+
*/
|
|
180
|
+
date: string;
|
|
181
|
+
};
|
|
182
|
+
export type BlokkliAdapterUnscheduleOptions = {
|
|
183
|
+
/**
|
|
184
|
+
* The host entity type.
|
|
185
|
+
*/
|
|
186
|
+
hostEntityType: string;
|
|
187
|
+
/**
|
|
188
|
+
* The host entity UUID.
|
|
189
|
+
*/
|
|
190
|
+
hostEntityUuid: string;
|
|
191
|
+
};
|
|
164
192
|
export interface BlokkliAdapter<T> {
|
|
165
193
|
/**
|
|
166
194
|
* Load the state.
|
|
@@ -308,6 +336,14 @@ export interface BlokkliAdapter<T> {
|
|
|
308
336
|
* Publish all changes.
|
|
309
337
|
*/
|
|
310
338
|
publish?: (options: BlokkliAdapterPublishOptions) => Promise<MutationResponseLike<T | undefined | null>>;
|
|
339
|
+
/**
|
|
340
|
+
* Schedule an edit state.
|
|
341
|
+
*/
|
|
342
|
+
scheduleEditState?: (options: BlokkliAdapterScheduleOptions) => Promise<MutationResponseLike<T | undefined | null>>;
|
|
343
|
+
/**
|
|
344
|
+
* Unschedule an already scheduled edit state.
|
|
345
|
+
*/
|
|
346
|
+
unscheduleEditState?: (options: BlokkliAdapterUnscheduleOptions) => Promise<MutationResponseLike<T | undefined | null>>;
|
|
311
347
|
/**
|
|
312
348
|
* Set a specific history index.
|
|
313
349
|
*/
|
|
@@ -5,7 +5,7 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
5
5
|
description: string;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
icon?: BlokkliIcon;
|
|
8
|
-
type?: "success" | "danger";
|
|
8
|
+
type?: "success" | "danger" | "yellow";
|
|
9
9
|
weight?: number;
|
|
10
10
|
secondary?: boolean;
|
|
11
11
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
@@ -16,7 +16,7 @@ declare const _default: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
|
16
16
|
description: string;
|
|
17
17
|
disabled?: boolean;
|
|
18
18
|
icon?: BlokkliIcon;
|
|
19
|
-
type?: "success" | "danger";
|
|
19
|
+
type?: "success" | "danger" | "yellow";
|
|
20
20
|
weight?: number;
|
|
21
21
|
secondary?: boolean;
|
|
22
22
|
}> & Readonly<{
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="#bk-toolbar-title">
|
|
3
|
+
<button
|
|
4
|
+
v-if="scheduledDate"
|
|
5
|
+
class="bk-toolbar-title-scheduled"
|
|
6
|
+
@click.prevent="eventBus.emit('publish:show-dialog')"
|
|
7
|
+
>
|
|
8
|
+
<Icon name="calendar-clock" />
|
|
9
|
+
<div class="bk-toolbar-title-scheduled-text">
|
|
10
|
+
<div>{{ formattedScheduledDate }}</div>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="bk-tooltip">
|
|
13
|
+
<div>
|
|
14
|
+
{{
|
|
15
|
+
$t("scheduledFor", "The changes will be published on this date.")
|
|
16
|
+
}}
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</button>
|
|
3
20
|
<button
|
|
4
21
|
ref="buttonEl"
|
|
5
22
|
class="bk-toolbar-button"
|
|
@@ -36,15 +53,30 @@
|
|
|
36
53
|
import defineCommands from "#blokkli/helpers/composables/defineCommands";
|
|
37
54
|
import { useBlokkli, defineBlokkliFeature, ref, computed } from "#imports";
|
|
38
55
|
import defineTourItem from "#blokkli/helpers/composables/defineTourItem";
|
|
56
|
+
import { Icon } from "#blokkli/components";
|
|
39
57
|
defineBlokkliFeature({
|
|
40
58
|
id: "entity-title",
|
|
41
59
|
icon: "title",
|
|
42
60
|
label: "Entity Title",
|
|
43
61
|
description: "Renders the title and status of the page entity."
|
|
44
62
|
});
|
|
45
|
-
const { state, eventBus, $t } = useBlokkli();
|
|
63
|
+
const { state, eventBus, $t, ui } = useBlokkli();
|
|
46
64
|
const { entity, mutations } = state;
|
|
47
65
|
const buttonEl = ref(null);
|
|
66
|
+
const scheduledDate = computed(() => state.publishOptions.value?.publishOn);
|
|
67
|
+
const formattedScheduledDate = computed(() => {
|
|
68
|
+
if (!scheduledDate.value) {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
return ui.formatDate(scheduledDate.value, {
|
|
72
|
+
weekday: "short",
|
|
73
|
+
year: "numeric",
|
|
74
|
+
month: "2-digit",
|
|
75
|
+
day: "2-digit",
|
|
76
|
+
hour: "2-digit",
|
|
77
|
+
minute: "2-digit"
|
|
78
|
+
});
|
|
79
|
+
});
|
|
48
80
|
const statusPublished = computed(
|
|
49
81
|
() => $t("pageIsPublished", "Page is published")
|
|
50
82
|
);
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
<tr
|
|
3
3
|
:class="{
|
|
4
4
|
'bk-is-success': isSuccess,
|
|
5
|
-
'bk-is-error': isError
|
|
5
|
+
'bk-is-error': isError,
|
|
6
|
+
'bk-is-warning': isScheduled && isSelected && !isSuccess
|
|
6
7
|
}"
|
|
7
8
|
>
|
|
8
9
|
<td>
|
|
@@ -24,7 +25,9 @@
|
|
|
24
25
|
{{ bundleLabel }}
|
|
25
26
|
</div>
|
|
26
27
|
</div>
|
|
27
|
-
<div v-if="isCurrent" class="bk-pill">
|
|
28
|
+
<div v-if="isCurrent" class="bk-pill">
|
|
29
|
+
{{ $t("publishCurrentPage", "Current page") }}
|
|
30
|
+
</div>
|
|
28
31
|
</label>
|
|
29
32
|
</td>
|
|
30
33
|
<td>
|
|
@@ -36,7 +39,8 @@
|
|
|
36
39
|
<span
|
|
37
40
|
class="bk-status-indicator"
|
|
38
41
|
:class="{
|
|
39
|
-
'bk-is-success': newStatus.status
|
|
42
|
+
'bk-is-success': newStatus.status === true,
|
|
43
|
+
'bk-is-warning': newStatus.status === 'scheduled'
|
|
40
44
|
}"
|
|
41
45
|
/>
|
|
42
46
|
</div>
|
|
@@ -55,7 +59,8 @@
|
|
|
55
59
|
<span
|
|
56
60
|
class="bk-status-indicator"
|
|
57
61
|
:class="{
|
|
58
|
-
'bk-is-success': newStatus.status
|
|
62
|
+
'bk-is-success': newStatus.status === true,
|
|
63
|
+
'bk-is-warning': newStatus.status === 'scheduled'
|
|
59
64
|
}"
|
|
60
65
|
/>
|
|
61
66
|
</template>
|
|
@@ -65,8 +70,9 @@
|
|
|
65
70
|
</template>
|
|
66
71
|
|
|
67
72
|
<script setup>
|
|
68
|
-
import { computed } from "#imports";
|
|
73
|
+
import { computed, useBlokkli } from "#imports";
|
|
69
74
|
import { Icon } from "#blokkli/components";
|
|
75
|
+
const { $t } = useBlokkli();
|
|
70
76
|
const props = defineProps({
|
|
71
77
|
hostEntityType: { type: String, required: true },
|
|
72
78
|
hostEntityUuid: { type: String, required: true },
|
|
@@ -76,7 +82,9 @@ const props = defineProps({
|
|
|
76
82
|
isCurrent: { type: Boolean, required: true },
|
|
77
83
|
shouldPublish: { type: Boolean, required: true },
|
|
78
84
|
isMutating: { type: Boolean, required: true },
|
|
79
|
-
mutationStatus: { type: Object, required: false }
|
|
85
|
+
mutationStatus: { type: Object, required: false },
|
|
86
|
+
isScheduled: { type: Boolean, required: true },
|
|
87
|
+
scheduleDate: { type: String, required: true }
|
|
80
88
|
});
|
|
81
89
|
const modelValue = defineModel({ type: Array, ...{
|
|
82
90
|
default: () => {
|
|
@@ -92,10 +100,12 @@ const isError = computed(
|
|
|
92
100
|
);
|
|
93
101
|
const mutationStatusLabel = computed(() => {
|
|
94
102
|
if (isSuccess.value) {
|
|
95
|
-
if (props.
|
|
96
|
-
return "
|
|
103
|
+
if (props.isScheduled) {
|
|
104
|
+
return $t("publishPublicationScheduled", "Publication scheduled");
|
|
105
|
+
} else if (props.entity.status && props.shouldPublish) {
|
|
106
|
+
return $t("publishSuccessfullyPublished", "Successfully published");
|
|
97
107
|
} else {
|
|
98
|
-
return "
|
|
108
|
+
return $t("publishSuccessfullySaved", "Successfully saved");
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
return null;
|
|
@@ -104,14 +114,31 @@ const isCurrentlyPublished = computed(() => props.entity.status);
|
|
|
104
114
|
const title = computed(() => props.entity.label);
|
|
105
115
|
const bundleLabel = computed(() => props.entity.bundleLabel);
|
|
106
116
|
const newStatus = computed(() => {
|
|
107
|
-
if (isSelected.value &&
|
|
108
|
-
return {
|
|
117
|
+
if (isSelected.value && props.isScheduled) {
|
|
118
|
+
return {
|
|
119
|
+
label: $t("publishWillBeScheduled", "Will be scheduled"),
|
|
120
|
+
status: "scheduled"
|
|
121
|
+
};
|
|
122
|
+
} else if (isSelected.value && isCurrentlyPublished.value && !props.shouldPublish) {
|
|
123
|
+
return {
|
|
124
|
+
label: $t("publishRemainsPublished", "Remains published"),
|
|
125
|
+
status: true
|
|
126
|
+
};
|
|
109
127
|
} else if (isSelected.value && !isCurrentlyPublished.value && props.shouldPublish) {
|
|
110
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
label: $t("publishWillBePublished", "Will be published"),
|
|
130
|
+
status: true
|
|
131
|
+
};
|
|
111
132
|
} else if (isSelected.value && isCurrentlyPublished.value) {
|
|
112
|
-
return {
|
|
133
|
+
return {
|
|
134
|
+
label: $t("publishRemainsPublished", "Remains published"),
|
|
135
|
+
status: true
|
|
136
|
+
};
|
|
113
137
|
} else if (isSelected.value && !isCurrentlyPublished.value) {
|
|
114
|
-
return {
|
|
138
|
+
return {
|
|
139
|
+
label: $t("publishRemainsUnpublished", "Remains unpublished"),
|
|
140
|
+
status: false
|
|
141
|
+
};
|
|
115
142
|
}
|
|
116
143
|
return { label: "", status: false };
|
|
117
144
|
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<label
|
|
3
|
+
class="bk-publish-option"
|
|
4
|
+
:class="[
|
|
5
|
+
'bk-is-' + color,
|
|
6
|
+
{
|
|
7
|
+
'bk-is-disabled': disabled
|
|
8
|
+
}
|
|
9
|
+
]"
|
|
10
|
+
>
|
|
11
|
+
<input
|
|
12
|
+
v-model="value"
|
|
13
|
+
type="radio"
|
|
14
|
+
:value="id"
|
|
15
|
+
required
|
|
16
|
+
name="publish-mode"
|
|
17
|
+
:disabled
|
|
18
|
+
/>
|
|
19
|
+
<div class="bk-publish-option-icon">
|
|
20
|
+
<Icon :name="icon" />
|
|
21
|
+
<div class="bk-publish-option-icon-check">
|
|
22
|
+
<Icon name="check" />
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div>
|
|
26
|
+
<div class="bk-publish-option-label">
|
|
27
|
+
{{ label }}
|
|
28
|
+
</div>
|
|
29
|
+
<div class="bk-publish-option-description">
|
|
30
|
+
{{ description }}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</label>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup>
|
|
37
|
+
import { Icon } from "#blokkli/components";
|
|
38
|
+
defineProps({
|
|
39
|
+
id: { type: String, required: true },
|
|
40
|
+
label: { type: String, required: true },
|
|
41
|
+
description: { type: String, required: true },
|
|
42
|
+
icon: { type: null, required: true },
|
|
43
|
+
color: { type: String, required: true },
|
|
44
|
+
disabled: { type: Boolean, required: false }
|
|
45
|
+
});
|
|
46
|
+
const value = defineModel({ type: String });
|
|
47
|
+
</script>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BlokkliIcon } from '#blokkli-build/icons';
|
|
2
|
+
export type PublishOptionProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description: string;
|
|
6
|
+
icon: BlokkliIcon;
|
|
7
|
+
color: 'lime' | 'yellow' | 'red';
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
};
|
|
10
|
+
type __VLS_Props = PublishOptionProps;
|
|
11
|
+
type __VLS_PublicProps = __VLS_Props & {
|
|
12
|
+
modelValue?: string;
|
|
13
|
+
};
|
|
14
|
+
declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
15
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
16
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
17
|
+
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="bk-schedule-date">
|
|
3
|
+
<div class="bk-schedule-date-picker">
|
|
4
|
+
<FormDatepicker
|
|
5
|
+
v-model="selectedDate"
|
|
6
|
+
:min="minDate"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
:error="!!error"
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
<div v-if="selectedDate" class="bk-schedule-date-time">
|
|
12
|
+
<div v-if="formattedDateTime" class="bk-schedule-date-formatted">
|
|
13
|
+
{{ formattedDateTime }}
|
|
14
|
+
</div>
|
|
15
|
+
<div class="bk-schedule-date-time-input">
|
|
16
|
+
<input
|
|
17
|
+
id="schedule-time"
|
|
18
|
+
v-model="selectedTime"
|
|
19
|
+
type="time"
|
|
20
|
+
class="bk-form-input"
|
|
21
|
+
:disabled="disabled"
|
|
22
|
+
:class="{
|
|
23
|
+
'bk-is-invalid': error
|
|
24
|
+
}"
|
|
25
|
+
/>
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
class="bk-button bk-schedule-date-time-button bk-is-icon-only"
|
|
29
|
+
:disabled="disabled"
|
|
30
|
+
@click="decrementHour"
|
|
31
|
+
>
|
|
32
|
+
<Icon name="minus" />
|
|
33
|
+
</button>
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
class="bk-button bk-schedule-date-time-button bk-is-icon-only"
|
|
37
|
+
:disabled="disabled"
|
|
38
|
+
@click="incrementHour"
|
|
39
|
+
>
|
|
40
|
+
<Icon name="plus" />
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="bk-schedule-date-presets">
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
class="bk-button bk-is-small"
|
|
48
|
+
:disabled="disabled"
|
|
49
|
+
@click="setTomorrow"
|
|
50
|
+
>
|
|
51
|
+
{{ $t("publishScheduleTomorrow", "Tomorrow") }}
|
|
52
|
+
</button>
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
class="bk-button bk-is-small"
|
|
56
|
+
:disabled="disabled"
|
|
57
|
+
@click="setInSevenDays"
|
|
58
|
+
>
|
|
59
|
+
{{ $t("publishScheduleInSevenDays", "In 7 days") }}
|
|
60
|
+
</button>
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
class="bk-button bk-is-small"
|
|
64
|
+
:disabled="disabled"
|
|
65
|
+
@click="setNextMonday"
|
|
66
|
+
>
|
|
67
|
+
{{ $t("publishScheduleNextMonday", "Next Monday") }}
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
<div
|
|
71
|
+
class="bk-schedule-date-info"
|
|
72
|
+
v-text="
|
|
73
|
+
$t(
|
|
74
|
+
'publishScheduledInfo',
|
|
75
|
+
'You can still make changes until the scheduled publication date.'
|
|
76
|
+
)
|
|
77
|
+
"
|
|
78
|
+
/>
|
|
79
|
+
<div v-if="error" class="bk-schedule-date-error">
|
|
80
|
+
{{ error }}
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<script setup>
|
|
87
|
+
import { ref, computed, watch, useBlokkli } from "#imports";
|
|
88
|
+
import { FormDatepicker, Icon } from "#blokkli/components";
|
|
89
|
+
const { $t, ui } = useBlokkli();
|
|
90
|
+
defineProps({
|
|
91
|
+
disabled: { type: Boolean, required: false },
|
|
92
|
+
error: { type: String, required: false }
|
|
93
|
+
});
|
|
94
|
+
const modelValue = defineModel({ type: String });
|
|
95
|
+
const selectedDate = ref("");
|
|
96
|
+
const selectedTime = ref("12:00");
|
|
97
|
+
const formattedDateTime = computed(() => {
|
|
98
|
+
if (!selectedDate.value || !selectedTime.value) {
|
|
99
|
+
return "";
|
|
100
|
+
}
|
|
101
|
+
const dateTimeString = `${selectedDate.value}T${selectedTime.value}:00`;
|
|
102
|
+
const date = new Date(dateTimeString);
|
|
103
|
+
return ui.formatDate(date, {
|
|
104
|
+
weekday: "long",
|
|
105
|
+
year: "numeric",
|
|
106
|
+
month: "long",
|
|
107
|
+
day: "numeric",
|
|
108
|
+
hour: "2-digit",
|
|
109
|
+
minute: "2-digit"
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
if (modelValue.value) {
|
|
113
|
+
const date = new Date(modelValue.value);
|
|
114
|
+
selectedDate.value = formatDate(date);
|
|
115
|
+
selectedTime.value = formatTime(date);
|
|
116
|
+
}
|
|
117
|
+
const minDate = computed(() => {
|
|
118
|
+
const today = /* @__PURE__ */ new Date();
|
|
119
|
+
return formatDate(today);
|
|
120
|
+
});
|
|
121
|
+
function formatDate(date) {
|
|
122
|
+
const year = date.getFullYear();
|
|
123
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
124
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
125
|
+
return `${year}-${month}-${day}`;
|
|
126
|
+
}
|
|
127
|
+
function formatTime(date) {
|
|
128
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
129
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
130
|
+
return `${hours}:${minutes}`;
|
|
131
|
+
}
|
|
132
|
+
watch([selectedDate, selectedTime], () => {
|
|
133
|
+
if (!selectedDate.value) {
|
|
134
|
+
modelValue.value = void 0;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const dateTimeString = `${selectedDate.value}T${selectedTime.value}:00`;
|
|
138
|
+
const date = new Date(dateTimeString);
|
|
139
|
+
modelValue.value = date.toISOString();
|
|
140
|
+
});
|
|
141
|
+
watch(modelValue, (newValue) => {
|
|
142
|
+
if (!newValue) {
|
|
143
|
+
selectedDate.value = "";
|
|
144
|
+
selectedTime.value = "12:00";
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const date = new Date(newValue);
|
|
148
|
+
const newDate = formatDate(date);
|
|
149
|
+
const newTime = formatTime(date);
|
|
150
|
+
if (newDate !== selectedDate.value || newTime !== selectedTime.value) {
|
|
151
|
+
selectedDate.value = newDate;
|
|
152
|
+
selectedTime.value = newTime;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
function setTomorrow() {
|
|
156
|
+
const tomorrow = /* @__PURE__ */ new Date();
|
|
157
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
158
|
+
selectedDate.value = formatDate(tomorrow);
|
|
159
|
+
}
|
|
160
|
+
function setInSevenDays() {
|
|
161
|
+
const date = /* @__PURE__ */ new Date();
|
|
162
|
+
date.setDate(date.getDate() + 7);
|
|
163
|
+
selectedDate.value = formatDate(date);
|
|
164
|
+
}
|
|
165
|
+
function setNextMonday() {
|
|
166
|
+
const today = /* @__PURE__ */ new Date();
|
|
167
|
+
const dayOfWeek = today.getDay();
|
|
168
|
+
const daysUntilMonday = dayOfWeek === 0 ? 1 : 8 - dayOfWeek;
|
|
169
|
+
const nextMonday = /* @__PURE__ */ new Date();
|
|
170
|
+
nextMonday.setDate(today.getDate() + daysUntilMonday);
|
|
171
|
+
selectedDate.value = formatDate(nextMonday);
|
|
172
|
+
}
|
|
173
|
+
function incrementHour() {
|
|
174
|
+
const [hours = 0, minutes = 0] = selectedTime.value.split(":").map(Number);
|
|
175
|
+
const newHours = (hours + 1) % 24;
|
|
176
|
+
selectedTime.value = `${String(newHours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
|
177
|
+
}
|
|
178
|
+
function decrementHour() {
|
|
179
|
+
const [hours = 0, minutes = 0] = selectedTime.value.split(":").map(Number);
|
|
180
|
+
const newHours = hours === 0 ? 23 : hours - 1;
|
|
181
|
+
selectedTime.value = `${String(newHours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
|
|
182
|
+
}
|
|
183
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
disabled?: boolean;
|
|
3
|
+
error?: string;
|
|
4
|
+
};
|
|
5
|
+
type __VLS_PublicProps = __VLS_Props & {
|
|
6
|
+
modelValue?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
11
|
+
"onUpdate:modelValue"?: ((value: string | undefined) => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
|
+
export default _default;
|