orio-ui 1.23.2 → 1.24.0
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/README.md +3 -3
- package/dist/module.json +1 -1
- package/dist/runtime/canvas.d.ts +1 -1
- package/dist/runtime/canvas.js +4 -1
- package/dist/runtime/components/Calendar.d.vue.ts +33 -0
- package/dist/runtime/components/Calendar.vue +251 -0
- package/dist/runtime/components/Calendar.vue.d.ts +33 -0
- package/dist/runtime/components/Canvas/context.d.ts +9 -0
- package/dist/runtime/components/Canvas/index.d.vue.ts +3 -0
- package/dist/runtime/components/Canvas/index.vue +19 -2
- package/dist/runtime/components/Canvas/index.vue.d.ts +3 -0
- package/dist/runtime/components/Canvas/tools/exportTool.d.ts +53 -6
- package/dist/runtime/components/Canvas/tools/exportTool.js +79 -32
- package/dist/runtime/components/Form.d.vue.ts +1 -1
- package/dist/runtime/components/Form.vue.d.ts +1 -1
- package/dist/runtime/components/date/Picker.d.vue.ts +26 -0
- package/dist/runtime/components/date/Picker.vue +54 -0
- package/dist/runtime/components/date/Picker.vue.d.ts +26 -0
- package/dist/runtime/components/date/PickerTrigger.d.vue.ts +23 -0
- package/dist/runtime/components/date/PickerTrigger.vue +80 -0
- package/dist/runtime/components/date/PickerTrigger.vue.d.ts +23 -0
- package/dist/runtime/components/date/RangePicker.d.vue.ts +28 -0
- package/dist/runtime/components/date/RangePicker.vue +148 -0
- package/dist/runtime/components/date/RangePicker.vue.d.ts +28 -0
- package/dist/runtime/components/view/Dates.d.vue.ts +2 -5
- package/dist/runtime/components/view/Dates.vue +17 -23
- package/dist/runtime/components/view/Dates.vue.d.ts +2 -5
- package/dist/runtime/i18n/en.json +8 -5
- package/dist/runtime/i18n/uk.json +8 -5
- package/dist/runtime/index.d.ts +4 -2
- package/dist/runtime/index.js +6 -2
- package/dist/runtime/utils/date.d.ts +10 -0
- package/dist/runtime/utils/date.js +38 -0
- package/package.json +1 -1
- package/dist/runtime/components/DatePicker.d.vue.ts +0 -15
- package/dist/runtime/components/DatePicker.vue +0 -24
- package/dist/runtime/components/DatePicker.vue.d.ts +0 -15
- package/dist/runtime/components/DateRangePicker.d.vue.ts +0 -18
- package/dist/runtime/components/DateRangePicker.vue +0 -67
- package/dist/runtime/components/DateRangePicker.vue.d.ts +0 -18
|
@@ -6,11 +6,15 @@ const MIME_TYPES = {
|
|
|
6
6
|
jpeg: "image/jpeg",
|
|
7
7
|
webp: "image/webp"
|
|
8
8
|
};
|
|
9
|
+
export const DEFAULT_EXPORT_OPTIONS = {
|
|
10
|
+
format: "png",
|
|
11
|
+
quality: 0.92,
|
|
12
|
+
filename: "canvas",
|
|
13
|
+
scale: 1
|
|
14
|
+
};
|
|
9
15
|
function renderNodesTo(target, nodes, tools) {
|
|
10
16
|
const toolMap = new Map(tools.map((t) => [t.id, t]));
|
|
11
|
-
const ordered = [...nodes].sort(
|
|
12
|
-
(a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0)
|
|
13
|
-
);
|
|
17
|
+
const ordered = [...nodes].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
14
18
|
for (const node of ordered) {
|
|
15
19
|
const renderFn = toolMap.get(node.type)?.render;
|
|
16
20
|
if (!renderFn) continue;
|
|
@@ -39,6 +43,67 @@ function downloadBlob(blob, filename) {
|
|
|
39
43
|
a.remove();
|
|
40
44
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
41
45
|
}
|
|
46
|
+
function resolveFormat(format) {
|
|
47
|
+
return MIME_TYPES[format] ? format : "png";
|
|
48
|
+
}
|
|
49
|
+
function sanitizeFilename(name) {
|
|
50
|
+
return (name ?? "canvas").replace(/[^A-Za-z0-9._-]/g, "_") || "canvas";
|
|
51
|
+
}
|
|
52
|
+
export async function renderCanvasSnapshot(snapshot, options = {}) {
|
|
53
|
+
const format = resolveFormat(options.format);
|
|
54
|
+
const quality = options.quality ?? DEFAULT_EXPORT_OPTIONS.quality;
|
|
55
|
+
const scale = options.scale ?? DEFAULT_EXPORT_OPTIONS.scale;
|
|
56
|
+
if (!Number.isFinite(scale) || scale <= 0) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`[orio-canvas] invalid export option: scale must be a finite number > 0 (got ${scale})`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (format !== "png" && options.quality !== void 0 && (!Number.isFinite(quality) || quality < 0 || quality > 1)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`[orio-canvas] invalid export option: quality must be a finite number in [0, 1] (got ${quality})`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const mime = MIME_TYPES[format];
|
|
67
|
+
const ext = format === "jpeg" ? "jpg" : format;
|
|
68
|
+
const filename = `${sanitizeFilename(options.filename)}.${ext}`;
|
|
69
|
+
const out = document.createElement("canvas");
|
|
70
|
+
const w = Math.max(1, Math.round(snapshot.width * scale));
|
|
71
|
+
const h = Math.max(1, Math.round(snapshot.height * scale));
|
|
72
|
+
out.width = w;
|
|
73
|
+
out.height = h;
|
|
74
|
+
const c = out.getContext("2d");
|
|
75
|
+
if (!c) {
|
|
76
|
+
throw new Error("[orio-canvas] failed to acquire 2D context for export");
|
|
77
|
+
}
|
|
78
|
+
c.scale(scale, scale);
|
|
79
|
+
if (format === "jpeg") {
|
|
80
|
+
c.fillStyle = "#ffffff";
|
|
81
|
+
c.fillRect(0, 0, snapshot.width, snapshot.height);
|
|
82
|
+
}
|
|
83
|
+
renderNodesTo(c, snapshot.nodes, snapshot.tools);
|
|
84
|
+
const encoderQuality = format === "png" ? void 0 : quality;
|
|
85
|
+
const blob = await new Promise((resolve, reject) => {
|
|
86
|
+
out.toBlob(
|
|
87
|
+
(b) => b ? resolve(b) : reject(new Error("[orio-canvas] toBlob returned null")),
|
|
88
|
+
mime,
|
|
89
|
+
encoderQuality
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
const dataURL = out.toDataURL(mime, encoderQuality);
|
|
93
|
+
const file = new File([blob], filename, { type: mime });
|
|
94
|
+
return { blob, dataURL, file, format, width: w, height: h, filename };
|
|
95
|
+
}
|
|
96
|
+
export async function performExport(snapshot, options = {}) {
|
|
97
|
+
const result = await renderCanvasSnapshot(snapshot, options);
|
|
98
|
+
if (options.onExport) {
|
|
99
|
+
await options.onExport(result);
|
|
100
|
+
}
|
|
101
|
+
const shouldDownload = options.download ?? !options.onExport;
|
|
102
|
+
if (shouldDownload) {
|
|
103
|
+
downloadBlob(result.blob, result.filename);
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
42
107
|
export function exportTool(options = {}) {
|
|
43
108
|
return defineCanvasTool({
|
|
44
109
|
id: "export",
|
|
@@ -47,43 +112,25 @@ export function exportTool(options = {}) {
|
|
|
47
112
|
kind: "action",
|
|
48
113
|
tooltip: ExportTooltip,
|
|
49
114
|
defaultOptions: {
|
|
50
|
-
|
|
51
|
-
quality: 0.92,
|
|
52
|
-
filename: "canvas",
|
|
53
|
-
scale: 1,
|
|
115
|
+
...DEFAULT_EXPORT_OPTIONS,
|
|
54
116
|
...options
|
|
55
117
|
},
|
|
56
118
|
disabled(api) {
|
|
57
119
|
return api.nodes.value.length === 0;
|
|
58
120
|
},
|
|
59
121
|
action(api) {
|
|
60
|
-
const { quality, scale } = api.options;
|
|
61
122
|
const { width, height } = api.size();
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const h = Math.max(1, Math.round(height * scale));
|
|
69
|
-
out.width = w;
|
|
70
|
-
out.height = h;
|
|
71
|
-
const c = out.getContext("2d");
|
|
72
|
-
if (!c) return;
|
|
73
|
-
c.scale(scale, scale);
|
|
74
|
-
if (knownFormat === "jpeg") {
|
|
75
|
-
c.fillStyle = "#ffffff";
|
|
76
|
-
c.fillRect(0, 0, width, height);
|
|
77
|
-
}
|
|
78
|
-
renderNodesTo(c, api.nodes.value, api.getTools());
|
|
79
|
-
out.toBlob(
|
|
80
|
-
(blob) => {
|
|
81
|
-
if (!blob) return;
|
|
82
|
-
downloadBlob(blob, `${sanitized}.${ext}`);
|
|
123
|
+
performExport(
|
|
124
|
+
{
|
|
125
|
+
nodes: api.nodes.value,
|
|
126
|
+
tools: api.getTools(),
|
|
127
|
+
width,
|
|
128
|
+
height
|
|
83
129
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
130
|
+
api.options
|
|
131
|
+
).catch((err) => {
|
|
132
|
+
console.error("[orio-canvas] export failed", err);
|
|
133
|
+
});
|
|
87
134
|
}
|
|
88
135
|
});
|
|
89
136
|
}
|
|
@@ -12,8 +12,8 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
|
|
|
12
12
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<(FormProps & {
|
|
13
13
|
modelValue?: T;
|
|
14
14
|
}) & {
|
|
15
|
-
"onUpdate:modelValue"?: ((value: T | undefined) => any) | undefined;
|
|
16
15
|
onSubmit?: (() => any) | undefined;
|
|
16
|
+
"onUpdate:modelValue"?: ((value: T | undefined) => any) | undefined;
|
|
17
17
|
}> & (typeof globalThis extends {
|
|
18
18
|
__VLS_PROPS_FALLBACK: infer P;
|
|
19
19
|
} ? P : {});
|
|
@@ -12,8 +12,8 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
|
|
|
12
12
|
props: import("vue").PublicProps & __VLS_PrettifyLocal<(FormProps & {
|
|
13
13
|
modelValue?: T;
|
|
14
14
|
}) & {
|
|
15
|
-
"onUpdate:modelValue"?: ((value: T | undefined) => any) | undefined;
|
|
16
15
|
onSubmit?: (() => any) | undefined;
|
|
16
|
+
"onUpdate:modelValue"?: ((value: T | undefined) => any) | undefined;
|
|
17
17
|
}> & (typeof globalThis extends {
|
|
18
18
|
__VLS_PROPS_FALLBACK: infer P;
|
|
19
19
|
} ? P : {});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ControlProps } from "../ControlElement.vue.js";
|
|
2
|
+
import type { CalendarMarker } from "../Calendar.vue.js";
|
|
3
|
+
interface Props extends ControlProps {
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
min?: string | null;
|
|
6
|
+
max?: string | null;
|
|
7
|
+
markers?: CalendarMarker[];
|
|
8
|
+
getMarker?: (iso: string) => CalendarMarker | null;
|
|
9
|
+
isDisabled?: (iso: string) => boolean;
|
|
10
|
+
}
|
|
11
|
+
type __VLS_Props = Props;
|
|
12
|
+
type __VLS_ModelProps = {
|
|
13
|
+
modelValue?: string | null;
|
|
14
|
+
};
|
|
15
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
16
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
17
|
+
"update:modelValue": (value: string | null) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
19
|
+
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
20
|
+
}>, {
|
|
21
|
+
markers: CalendarMarker[];
|
|
22
|
+
min: string | null;
|
|
23
|
+
max: string | null;
|
|
24
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
25
|
+
declare const _default: typeof __VLS_export;
|
|
26
|
+
export default _default;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
import { formatDate } from "../../utils/date";
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
placeholder: { type: String, required: false },
|
|
7
|
+
min: { type: [String, null], required: false, default: null },
|
|
8
|
+
max: { type: [String, null], required: false, default: null },
|
|
9
|
+
markers: { type: Array, required: false, default: () => [] },
|
|
10
|
+
getMarker: { type: Function, required: false },
|
|
11
|
+
isDisabled: { type: Function, required: false },
|
|
12
|
+
appearance: { type: String, required: false },
|
|
13
|
+
error: { type: [String, null], required: false },
|
|
14
|
+
group: { type: Boolean, required: false },
|
|
15
|
+
id: { type: String, required: false },
|
|
16
|
+
label: { type: String, required: false },
|
|
17
|
+
layout: { type: String, required: false },
|
|
18
|
+
size: { type: String, required: false },
|
|
19
|
+
fill: { type: Boolean, required: false }
|
|
20
|
+
});
|
|
21
|
+
const value = defineModel({ type: [String, null], ...{ default: null } });
|
|
22
|
+
const { locale, t } = useI18n();
|
|
23
|
+
const display = computed(() => formatDate(value.value, locale.value));
|
|
24
|
+
const placeholderText = computed(
|
|
25
|
+
() => props.placeholder ?? t("datePicker.placeholder")
|
|
26
|
+
);
|
|
27
|
+
const calendarIsDisabled = computed(() => (iso) => {
|
|
28
|
+
if (props.min && iso < props.min) return true;
|
|
29
|
+
if (props.max && iso > props.max) return true;
|
|
30
|
+
return props.isDisabled?.(iso) ?? false;
|
|
31
|
+
});
|
|
32
|
+
function pick(iso, toggle) {
|
|
33
|
+
value.value = iso;
|
|
34
|
+
toggle(false);
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<orio-date-picker-trigger
|
|
40
|
+
v-bind="props"
|
|
41
|
+
:text="display"
|
|
42
|
+
:placeholder="placeholderText"
|
|
43
|
+
>
|
|
44
|
+
<template #default="{ toggle }">
|
|
45
|
+
<orio-calendar
|
|
46
|
+
:selected="value"
|
|
47
|
+
:markers
|
|
48
|
+
:get-marker="getMarker"
|
|
49
|
+
:is-disabled="calendarIsDisabled"
|
|
50
|
+
@select="pick($event, toggle)"
|
|
51
|
+
/>
|
|
52
|
+
</template>
|
|
53
|
+
</orio-date-picker-trigger>
|
|
54
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ControlProps } from "../ControlElement.vue.js";
|
|
2
|
+
import type { CalendarMarker } from "../Calendar.vue.js";
|
|
3
|
+
interface Props extends ControlProps {
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
min?: string | null;
|
|
6
|
+
max?: string | null;
|
|
7
|
+
markers?: CalendarMarker[];
|
|
8
|
+
getMarker?: (iso: string) => CalendarMarker | null;
|
|
9
|
+
isDisabled?: (iso: string) => boolean;
|
|
10
|
+
}
|
|
11
|
+
type __VLS_Props = Props;
|
|
12
|
+
type __VLS_ModelProps = {
|
|
13
|
+
modelValue?: string | null;
|
|
14
|
+
};
|
|
15
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
16
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
17
|
+
"update:modelValue": (value: string | null) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
19
|
+
"onUpdate:modelValue"?: ((value: string | null) => any) | undefined;
|
|
20
|
+
}>, {
|
|
21
|
+
markers: CalendarMarker[];
|
|
22
|
+
min: string | null;
|
|
23
|
+
max: string | null;
|
|
24
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
25
|
+
declare const _default: typeof __VLS_export;
|
|
26
|
+
export default _default;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ControlProps } from "../ControlElement.vue.js";
|
|
2
|
+
interface Props extends ControlProps {
|
|
3
|
+
text?: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
}
|
|
6
|
+
declare var __VLS_21: {
|
|
7
|
+
toggle: any;
|
|
8
|
+
};
|
|
9
|
+
type __VLS_Slots = {} & {
|
|
10
|
+
default?: (props: typeof __VLS_21) => any;
|
|
11
|
+
};
|
|
12
|
+
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
13
|
+
text: string;
|
|
14
|
+
placeholder: string;
|
|
15
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
19
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
20
|
+
new (): {
|
|
21
|
+
$slots: S;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
const props = defineProps({
|
|
4
|
+
text: { type: String, required: false, default: "" },
|
|
5
|
+
placeholder: { type: String, required: false, default: "" },
|
|
6
|
+
appearance: { type: String, required: false },
|
|
7
|
+
error: { type: [String, null], required: false },
|
|
8
|
+
group: { type: Boolean, required: false },
|
|
9
|
+
id: { type: String, required: false },
|
|
10
|
+
label: { type: String, required: false },
|
|
11
|
+
layout: { type: String, required: false },
|
|
12
|
+
size: { type: String, required: false },
|
|
13
|
+
fill: { type: Boolean, required: false }
|
|
14
|
+
});
|
|
15
|
+
const controlProps = computed(() => {
|
|
16
|
+
const { text, placeholder, ...rest } = props;
|
|
17
|
+
return rest;
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<orio-control-element v-slot="{ id }" v-bind="controlProps">
|
|
23
|
+
<orio-popover position="bottom-right" :offset="5">
|
|
24
|
+
<template #default="{ toggle, isOpen }">
|
|
25
|
+
<button
|
|
26
|
+
:id
|
|
27
|
+
type="button"
|
|
28
|
+
class="date-trigger"
|
|
29
|
+
:aria-expanded="isOpen"
|
|
30
|
+
@click="toggle()"
|
|
31
|
+
>
|
|
32
|
+
<span :class="{ placeholder: !text }">
|
|
33
|
+
{{ text || placeholder }}
|
|
34
|
+
</span>
|
|
35
|
+
<orio-icon name="calendar" />
|
|
36
|
+
</button>
|
|
37
|
+
</template>
|
|
38
|
+
<template #content="{ toggle }">
|
|
39
|
+
<slot :toggle />
|
|
40
|
+
</template>
|
|
41
|
+
</orio-popover>
|
|
42
|
+
</orio-control-element>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<style scoped>
|
|
46
|
+
.date-trigger {
|
|
47
|
+
width: 100%;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
justify-content: space-between;
|
|
51
|
+
gap: var(--control-gap);
|
|
52
|
+
background: var(--color-bg);
|
|
53
|
+
border: 1px solid var(--color-border);
|
|
54
|
+
border-radius: var(--control-radius);
|
|
55
|
+
padding: var(--control-py) var(--control-px);
|
|
56
|
+
color: var(--color-text);
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
user-select: none;
|
|
59
|
+
transition: border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
|
|
60
|
+
}
|
|
61
|
+
.date-trigger:hover {
|
|
62
|
+
border-color: var(--color-accent);
|
|
63
|
+
background-color: var(--color-surface);
|
|
64
|
+
}
|
|
65
|
+
.date-trigger[aria-expanded=true], .date-trigger:focus-visible {
|
|
66
|
+
border-color: var(--color-accent);
|
|
67
|
+
box-shadow: 0 0 0 2px var(--color-surface);
|
|
68
|
+
outline: none;
|
|
69
|
+
}
|
|
70
|
+
.date-trigger .placeholder {
|
|
71
|
+
color: var(--color-muted);
|
|
72
|
+
}
|
|
73
|
+
.date-trigger .icon {
|
|
74
|
+
color: var(--color-muted);
|
|
75
|
+
transition: color 0.2s ease;
|
|
76
|
+
}
|
|
77
|
+
.date-trigger:hover .icon {
|
|
78
|
+
color: var(--color-accent);
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ControlProps } from "../ControlElement.vue.js";
|
|
2
|
+
interface Props extends ControlProps {
|
|
3
|
+
text?: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
}
|
|
6
|
+
declare var __VLS_21: {
|
|
7
|
+
toggle: any;
|
|
8
|
+
};
|
|
9
|
+
type __VLS_Slots = {} & {
|
|
10
|
+
default?: (props: typeof __VLS_21) => any;
|
|
11
|
+
};
|
|
12
|
+
declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
13
|
+
text: string;
|
|
14
|
+
placeholder: string;
|
|
15
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
17
|
+
declare const _default: typeof __VLS_export;
|
|
18
|
+
export default _default;
|
|
19
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
20
|
+
new (): {
|
|
21
|
+
$slots: S;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ControlProps } from "../ControlElement.vue.js";
|
|
2
|
+
import type { CalendarMarker } from "../Calendar.vue.js";
|
|
3
|
+
import { type DateRange } from "../../utils/date.js";
|
|
4
|
+
export type { DateRange };
|
|
5
|
+
interface Props extends ControlProps {
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
min?: string | null;
|
|
8
|
+
max?: string | null;
|
|
9
|
+
markers?: CalendarMarker[];
|
|
10
|
+
getMarker?: (iso: string) => CalendarMarker | null;
|
|
11
|
+
isDisabled?: (iso: string) => boolean;
|
|
12
|
+
}
|
|
13
|
+
type __VLS_Props = Props;
|
|
14
|
+
type __VLS_ModelProps = {
|
|
15
|
+
modelValue?: DateRange;
|
|
16
|
+
};
|
|
17
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
18
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
19
|
+
"update:modelValue": (value: DateRange) => any;
|
|
20
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
21
|
+
"onUpdate:modelValue"?: ((value: DateRange) => any) | undefined;
|
|
22
|
+
}>, {
|
|
23
|
+
markers: CalendarMarker[];
|
|
24
|
+
min: string | null;
|
|
25
|
+
max: string | null;
|
|
26
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref, watch } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
import {
|
|
5
|
+
addMonths,
|
|
6
|
+
formatDate,
|
|
7
|
+
formatISO,
|
|
8
|
+
parseISO,
|
|
9
|
+
startOfMonth
|
|
10
|
+
} from "../../utils/date";
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
placeholder: { type: String, required: false },
|
|
13
|
+
min: { type: [String, null], required: false, default: null },
|
|
14
|
+
max: { type: [String, null], required: false, default: null },
|
|
15
|
+
markers: { type: Array, required: false, default: () => [] },
|
|
16
|
+
getMarker: { type: Function, required: false },
|
|
17
|
+
isDisabled: { type: Function, required: false },
|
|
18
|
+
appearance: { type: String, required: false },
|
|
19
|
+
error: { type: [String, null], required: false },
|
|
20
|
+
group: { type: Boolean, required: false },
|
|
21
|
+
id: { type: String, required: false },
|
|
22
|
+
label: { type: String, required: false },
|
|
23
|
+
layout: { type: String, required: false },
|
|
24
|
+
size: { type: String, required: false },
|
|
25
|
+
fill: { type: Boolean, required: false }
|
|
26
|
+
});
|
|
27
|
+
const range = defineModel({ type: Object, ...{
|
|
28
|
+
default: () => ({ start: null, end: null })
|
|
29
|
+
} });
|
|
30
|
+
const { locale, t } = useI18n();
|
|
31
|
+
const display = computed(() => {
|
|
32
|
+
const start = formatDate(range.value?.start, locale.value);
|
|
33
|
+
const end = formatDate(range.value?.end, locale.value);
|
|
34
|
+
if (start && end) return `${start} \u2013 ${end}`;
|
|
35
|
+
return start || end || "";
|
|
36
|
+
});
|
|
37
|
+
const placeholderText = computed(
|
|
38
|
+
() => props.placeholder ?? t("dateRangePicker.placeholder")
|
|
39
|
+
);
|
|
40
|
+
const seedMonth = startOfMonth(parseISO(range.value?.start) ?? /* @__PURE__ */ new Date());
|
|
41
|
+
const leftAnchor = ref(formatISO(seedMonth));
|
|
42
|
+
const rightAnchor = ref(formatISO(addMonths(seedMonth, 1)));
|
|
43
|
+
watch(
|
|
44
|
+
() => range.value?.start,
|
|
45
|
+
(start) => {
|
|
46
|
+
const date = parseISO(start);
|
|
47
|
+
if (!date) return;
|
|
48
|
+
const monthIso = formatISO(startOfMonth(date));
|
|
49
|
+
if (leftAnchor.value === monthIso || rightAnchor.value === monthIso) return;
|
|
50
|
+
leftAnchor.value = monthIso;
|
|
51
|
+
rightAnchor.value = formatISO(addMonths(startOfMonth(date), 1));
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
const hovering = ref(null);
|
|
55
|
+
const previewEnd = computed(() => {
|
|
56
|
+
if (range.value?.end) return range.value.end;
|
|
57
|
+
if (!hovering.value || !range.value?.start) return null;
|
|
58
|
+
const start = parseISO(range.value.start);
|
|
59
|
+
const hover = parseISO(hovering.value);
|
|
60
|
+
if (!start || !hover) return null;
|
|
61
|
+
return hover > start ? hovering.value : null;
|
|
62
|
+
});
|
|
63
|
+
const previewStart = computed(() => {
|
|
64
|
+
if (range.value?.start && range.value?.end) return range.value.start;
|
|
65
|
+
if (!hovering.value || !range.value?.start) return range.value?.start ?? null;
|
|
66
|
+
const start = parseISO(range.value.start);
|
|
67
|
+
const hover = parseISO(hovering.value);
|
|
68
|
+
if (!start || !hover) return range.value.start;
|
|
69
|
+
return hover < start ? hovering.value : range.value.start;
|
|
70
|
+
});
|
|
71
|
+
const previewMarker = computed(() => {
|
|
72
|
+
if (!previewStart.value || !previewEnd.value) return null;
|
|
73
|
+
return {
|
|
74
|
+
variant: "accent",
|
|
75
|
+
start: previewStart.value,
|
|
76
|
+
end: previewEnd.value
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
const calendarGetMarker = computed(() => (iso) => {
|
|
80
|
+
const preview = previewMarker.value;
|
|
81
|
+
if (preview && iso >= preview.start && iso <= preview.end) return preview;
|
|
82
|
+
return props.getMarker?.(iso) ?? null;
|
|
83
|
+
});
|
|
84
|
+
const calendarIsDisabled = computed(() => (iso) => {
|
|
85
|
+
if (props.min && iso < props.min) return true;
|
|
86
|
+
if (props.max && iso > props.max) return true;
|
|
87
|
+
return props.isDisabled?.(iso) ?? false;
|
|
88
|
+
});
|
|
89
|
+
function pick(iso, toggle) {
|
|
90
|
+
const current = range.value ?? { start: null, end: null };
|
|
91
|
+
if (!current.start || current.start && current.end) {
|
|
92
|
+
range.value = { start: iso, end: null };
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const startDate = parseISO(current.start);
|
|
96
|
+
const picked = parseISO(iso);
|
|
97
|
+
if (!startDate || !picked) return;
|
|
98
|
+
if (picked < startDate) {
|
|
99
|
+
range.value = { start: iso, end: current.start };
|
|
100
|
+
} else {
|
|
101
|
+
range.value = { start: current.start, end: iso };
|
|
102
|
+
}
|
|
103
|
+
hovering.value = null;
|
|
104
|
+
toggle(false);
|
|
105
|
+
}
|
|
106
|
+
function onHover(iso) {
|
|
107
|
+
hovering.value = iso;
|
|
108
|
+
}
|
|
109
|
+
function clearHover() {
|
|
110
|
+
hovering.value = null;
|
|
111
|
+
}
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<orio-date-picker-trigger
|
|
116
|
+
v-bind="props"
|
|
117
|
+
:text="display"
|
|
118
|
+
:placeholder="placeholderText"
|
|
119
|
+
>
|
|
120
|
+
<template #default="{ toggle }">
|
|
121
|
+
<div class="range-content" @mouseleave="clearHover">
|
|
122
|
+
<orio-calendar
|
|
123
|
+
v-model:anchor="leftAnchor"
|
|
124
|
+
:markers
|
|
125
|
+
:get-marker="calendarGetMarker"
|
|
126
|
+
:is-disabled="calendarIsDisabled"
|
|
127
|
+
@select="pick($event, toggle)"
|
|
128
|
+
@day-enter="onHover"
|
|
129
|
+
/>
|
|
130
|
+
<orio-calendar
|
|
131
|
+
v-model:anchor="rightAnchor"
|
|
132
|
+
:markers
|
|
133
|
+
:get-marker="calendarGetMarker"
|
|
134
|
+
:is-disabled="calendarIsDisabled"
|
|
135
|
+
@select="pick($event, toggle)"
|
|
136
|
+
@day-enter="onHover"
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
|
+
</orio-date-picker-trigger>
|
|
141
|
+
</template>
|
|
142
|
+
|
|
143
|
+
<style scoped>
|
|
144
|
+
.range-content {
|
|
145
|
+
display: flex;
|
|
146
|
+
gap: 0.75rem;
|
|
147
|
+
}
|
|
148
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ControlProps } from "../ControlElement.vue.js";
|
|
2
|
+
import type { CalendarMarker } from "../Calendar.vue.js";
|
|
3
|
+
import { type DateRange } from "../../utils/date.js";
|
|
4
|
+
export type { DateRange };
|
|
5
|
+
interface Props extends ControlProps {
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
min?: string | null;
|
|
8
|
+
max?: string | null;
|
|
9
|
+
markers?: CalendarMarker[];
|
|
10
|
+
getMarker?: (iso: string) => CalendarMarker | null;
|
|
11
|
+
isDisabled?: (iso: string) => boolean;
|
|
12
|
+
}
|
|
13
|
+
type __VLS_Props = Props;
|
|
14
|
+
type __VLS_ModelProps = {
|
|
15
|
+
modelValue?: DateRange;
|
|
16
|
+
};
|
|
17
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
18
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
19
|
+
"update:modelValue": (value: DateRange) => any;
|
|
20
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
21
|
+
"onUpdate:modelValue"?: ((value: DateRange) => any) | undefined;
|
|
22
|
+
}>, {
|
|
23
|
+
markers: CalendarMarker[];
|
|
24
|
+
min: string | null;
|
|
25
|
+
max: string | null;
|
|
26
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
startDate: string;
|
|
3
|
-
endDate?: string | null;
|
|
4
|
-
}
|
|
1
|
+
import { type DateRange } from "../../utils/date.js";
|
|
5
2
|
interface Props {
|
|
6
|
-
dates:
|
|
3
|
+
dates: DateRange;
|
|
7
4
|
month?: boolean;
|
|
8
5
|
size?: "small" | "medium" | "large";
|
|
9
6
|
type?: "text" | "title" | "subtitle" | "italics";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, toRefs } from "vue";
|
|
3
3
|
import { useI18n } from "vue-i18n";
|
|
4
|
+
import { formatDate } from "../../utils/date";
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
dates: { type: Object, required: true },
|
|
6
7
|
month: { type: Boolean, required: false },
|
|
@@ -8,33 +9,26 @@ const props = defineProps({
|
|
|
8
9
|
type: { type: String, required: false, default: "italics" }
|
|
9
10
|
});
|
|
10
11
|
const { dates } = toRefs(props);
|
|
11
|
-
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}).format(new Date(value));
|
|
24
|
-
}
|
|
25
|
-
const startDate = computed(() => formatMonthYear(dates.value.startDate));
|
|
26
|
-
const endDate = computed(() => {
|
|
27
|
-
if (dates.value.endDate === void 0) return null;
|
|
28
|
-
return dates.value.endDate !== null ? formatMonthYear(dates.value.endDate) : t("dates.present");
|
|
29
|
-
});
|
|
12
|
+
const { locale } = useI18n();
|
|
13
|
+
const formatOptions = computed(() => ({
|
|
14
|
+
day: props.month ? void 0 : "numeric",
|
|
15
|
+
month: "short",
|
|
16
|
+
year: "numeric"
|
|
17
|
+
}));
|
|
18
|
+
const startText = computed(
|
|
19
|
+
() => formatDate(dates.value?.start, locale.value, formatOptions.value)
|
|
20
|
+
);
|
|
21
|
+
const endText = computed(
|
|
22
|
+
() => formatDate(dates.value?.end, locale.value, formatOptions.value)
|
|
23
|
+
);
|
|
30
24
|
</script>
|
|
31
25
|
|
|
32
26
|
<template>
|
|
33
27
|
<div class="view-date">
|
|
34
|
-
<orio-view-text :model-value="
|
|
35
|
-
<template v-if="
|
|
36
|
-
<span v-if="
|
|
37
|
-
<orio-view-text :model-value="
|
|
28
|
+
<orio-view-text :model-value="startText" :type :size />
|
|
29
|
+
<template v-if="endText">
|
|
30
|
+
<span v-if="startText"> - </span>
|
|
31
|
+
<orio-view-text :model-value="endText" :type :size />
|
|
38
32
|
</template>
|
|
39
33
|
</div>
|
|
40
34
|
</template>
|