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
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A delightful, lightweight component library for Nuxt 3+ applications. Built with
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
✨ **
|
|
11
|
+
✨ **58 Components** - Beautiful, accessible components ready to use
|
|
12
12
|
🎨 **Themeable** - 5 built-in accent themes with light/dark mode support
|
|
13
13
|
🚀 **Auto-imported** - Works seamlessly with Nuxt's auto-import system
|
|
14
14
|
📦 **Tree-shakeable** - Only bundle what you use
|
|
@@ -67,7 +67,7 @@ function handleClick() {
|
|
|
67
67
|
|
|
68
68
|
## What's Included
|
|
69
69
|
|
|
70
|
-
### Components (
|
|
70
|
+
### Components (58)
|
|
71
71
|
|
|
72
72
|
#### Form Controls
|
|
73
73
|
|
|
@@ -194,7 +194,7 @@ npm run docs:dev
|
|
|
194
194
|
orio-ui/
|
|
195
195
|
├── src/
|
|
196
196
|
│ ├── runtime/
|
|
197
|
-
│ │ ├── components/ #
|
|
197
|
+
│ │ ├── components/ # 58 Vue components
|
|
198
198
|
│ │ ├── composables/ # 13 composables
|
|
199
199
|
│ │ ├── assets/css/ # Theme CSS files
|
|
200
200
|
│ │ └── utils/ # Icon registry
|
package/dist/module.json
CHANGED
package/dist/runtime/canvas.d.ts
CHANGED
|
@@ -18,4 +18,4 @@ export { rotateTool, type RotateToolOptions, } from "./components/Canvas/tools/r
|
|
|
18
18
|
export { resizeTool, type ResizeToolOptions, } from "./components/Canvas/tools/resizeTool.js";
|
|
19
19
|
export { transformTool, type TransformToolOptions, } from "./components/Canvas/tools/transformTool.js";
|
|
20
20
|
export { imageTool, type ImageToolOptions, type ImageNodeData, } from "./components/Canvas/tools/imageTool.js";
|
|
21
|
-
export { exportTool, type ExportToolOptions, type ExportFormat, } from "./components/Canvas/tools/exportTool.js";
|
|
21
|
+
export { exportTool, performExport, renderCanvasSnapshot, DEFAULT_EXPORT_OPTIONS, type ExportToolOptions, type ExportOptions, type ExportResult, type ExportFormat, type CanvasSnapshot, } from "./components/Canvas/tools/exportTool.js";
|
package/dist/runtime/canvas.js
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type MarkerVariant = "accent" | "success" | "alert" | "danger" | "muted";
|
|
2
|
+
export interface CalendarMarker {
|
|
3
|
+
variant: MarkerVariant;
|
|
4
|
+
start: string;
|
|
5
|
+
end: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CalendarProps {
|
|
8
|
+
selected?: string | null;
|
|
9
|
+
markers?: CalendarMarker[];
|
|
10
|
+
getMarker?: (iso: string) => CalendarMarker | null;
|
|
11
|
+
isDisabled?: (iso: string) => boolean;
|
|
12
|
+
weekStartsOn?: 0 | 1;
|
|
13
|
+
}
|
|
14
|
+
type __VLS_Props = CalendarProps;
|
|
15
|
+
type __VLS_ModelProps = {
|
|
16
|
+
"anchor"?: string | null;
|
|
17
|
+
};
|
|
18
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
19
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
20
|
+
select: (iso: string) => any;
|
|
21
|
+
dayEnter: (iso: string) => any;
|
|
22
|
+
"update:anchor": (value: string | null) => any;
|
|
23
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
24
|
+
onSelect?: ((iso: string) => any) | undefined;
|
|
25
|
+
onDayEnter?: ((iso: string) => any) | undefined;
|
|
26
|
+
"onUpdate:anchor"?: ((value: string | null) => any) | undefined;
|
|
27
|
+
}>, {
|
|
28
|
+
selected: string | null;
|
|
29
|
+
markers: CalendarMarker[];
|
|
30
|
+
weekStartsOn: 0 | 1;
|
|
31
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
32
|
+
declare const _default: typeof __VLS_export;
|
|
33
|
+
export default _default;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
import {
|
|
5
|
+
addMonths,
|
|
6
|
+
formatISO,
|
|
7
|
+
isSameDay,
|
|
8
|
+
parseISO,
|
|
9
|
+
startOfMonth
|
|
10
|
+
} from "../utils/date";
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
selected: { type: [String, null], required: false, default: null },
|
|
13
|
+
markers: { type: Array, required: false, default: () => [] },
|
|
14
|
+
getMarker: { type: Function, required: false },
|
|
15
|
+
isDisabled: { type: Function, required: false },
|
|
16
|
+
weekStartsOn: { type: Number, required: false, default: 1 }
|
|
17
|
+
});
|
|
18
|
+
const anchor = defineModel("anchor", { type: [String, null], ...{ default: null } });
|
|
19
|
+
const emit = defineEmits(["select", "dayEnter"]);
|
|
20
|
+
const { locale, t } = useI18n();
|
|
21
|
+
const today = /* @__PURE__ */ new Date();
|
|
22
|
+
const visibleMonth = computed(
|
|
23
|
+
() => startOfMonth(parseISO(anchor.value) ?? /* @__PURE__ */ new Date())
|
|
24
|
+
);
|
|
25
|
+
function shift(delta) {
|
|
26
|
+
anchor.value = formatISO(addMonths(visibleMonth.value, delta));
|
|
27
|
+
}
|
|
28
|
+
const monthLabel = computed(
|
|
29
|
+
() => new Intl.DateTimeFormat(locale.value, {
|
|
30
|
+
month: "long",
|
|
31
|
+
year: "numeric"
|
|
32
|
+
}).format(visibleMonth.value)
|
|
33
|
+
);
|
|
34
|
+
const weekdayLabels = computed(() => {
|
|
35
|
+
const sunday = new Date(2024, 0, 7);
|
|
36
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
37
|
+
const d = new Date(sunday);
|
|
38
|
+
d.setDate(sunday.getDate() + (i + props.weekStartsOn) % 7);
|
|
39
|
+
return new Intl.DateTimeFormat(locale.value, { weekday: "short" }).format(
|
|
40
|
+
d
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
function resolveMarker(iso) {
|
|
45
|
+
if (props.getMarker) {
|
|
46
|
+
const m = props.getMarker(iso);
|
|
47
|
+
if (m) {
|
|
48
|
+
return {
|
|
49
|
+
variant: m.variant,
|
|
50
|
+
isStart: iso === m.start,
|
|
51
|
+
isEnd: iso === m.end
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (let i = props.markers.length - 1; i >= 0; i--) {
|
|
56
|
+
const m = props.markers[i];
|
|
57
|
+
if (iso >= m.start && iso <= m.end) {
|
|
58
|
+
return {
|
|
59
|
+
variant: m.variant,
|
|
60
|
+
isStart: iso === m.start,
|
|
61
|
+
isEnd: iso === m.end
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const days = computed(() => {
|
|
68
|
+
const first = visibleMonth.value;
|
|
69
|
+
const startWeekday = first.getDay();
|
|
70
|
+
const offset = (startWeekday - props.weekStartsOn + 7) % 7;
|
|
71
|
+
const gridStart = new Date(first);
|
|
72
|
+
gridStart.setDate(gridStart.getDate() - offset);
|
|
73
|
+
const sel = parseISO(props.selected);
|
|
74
|
+
const result = [];
|
|
75
|
+
for (let i = 0; i < 42; i++) {
|
|
76
|
+
const d = new Date(gridStart);
|
|
77
|
+
d.setDate(gridStart.getDate() + i);
|
|
78
|
+
const iso = formatISO(d);
|
|
79
|
+
result.push({
|
|
80
|
+
iso,
|
|
81
|
+
label: d.getDate(),
|
|
82
|
+
inMonth: d.getMonth() === first.getMonth(),
|
|
83
|
+
isToday: isSameDay(d, today),
|
|
84
|
+
isSelected: !!sel && isSameDay(d, sel),
|
|
85
|
+
isDisabled: props.isDisabled?.(iso) ?? false,
|
|
86
|
+
marker: resolveMarker(iso)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
});
|
|
91
|
+
function onSelect(day) {
|
|
92
|
+
if (day.isDisabled) return;
|
|
93
|
+
emit("select", day.iso);
|
|
94
|
+
}
|
|
95
|
+
function onEnter(day) {
|
|
96
|
+
emit("dayEnter", day.iso);
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<template>
|
|
101
|
+
<div class="calendar">
|
|
102
|
+
<div class="calendar-nav">
|
|
103
|
+
<orio-button
|
|
104
|
+
variant="subdued"
|
|
105
|
+
icon="chevron-left"
|
|
106
|
+
:aria-label="t('calendar.previousMonth')"
|
|
107
|
+
@click="shift(-1)"
|
|
108
|
+
/>
|
|
109
|
+
<span class="calendar-month-title">{{ monthLabel }}</span>
|
|
110
|
+
<orio-button
|
|
111
|
+
variant="subdued"
|
|
112
|
+
icon="chevron-right"
|
|
113
|
+
:aria-label="t('calendar.nextMonth')"
|
|
114
|
+
@click="shift(1)"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="calendar-weekdays">
|
|
118
|
+
<span v-for="w in weekdayLabels" :key="w" class="calendar-weekday">
|
|
119
|
+
{{ w }}
|
|
120
|
+
</span>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="calendar-grid">
|
|
123
|
+
<button
|
|
124
|
+
v-for="day in days"
|
|
125
|
+
:key="day.iso"
|
|
126
|
+
type="button"
|
|
127
|
+
class="calendar-day"
|
|
128
|
+
:class="{
|
|
129
|
+
'out-of-month': !day.inMonth,
|
|
130
|
+
today: day.isToday,
|
|
131
|
+
selected: day.isSelected,
|
|
132
|
+
'has-marker': !!day.marker,
|
|
133
|
+
[`marker-${day.marker?.variant}`]: !!day.marker,
|
|
134
|
+
'marker-start': day.marker?.isStart,
|
|
135
|
+
'marker-end': day.marker?.isEnd
|
|
136
|
+
}"
|
|
137
|
+
:disabled="day.isDisabled"
|
|
138
|
+
@click="onSelect(day)"
|
|
139
|
+
@mouseenter="onEnter(day)"
|
|
140
|
+
>
|
|
141
|
+
<orio-badge v-if="day.isSelected" pill variant="primary">
|
|
142
|
+
{{ day.label }}
|
|
143
|
+
</orio-badge>
|
|
144
|
+
<template v-else>{{ day.label }}</template>
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</template>
|
|
149
|
+
|
|
150
|
+
<style scoped>
|
|
151
|
+
.calendar {
|
|
152
|
+
display: inline-block;
|
|
153
|
+
background: var(--color-bg);
|
|
154
|
+
border: 1px solid var(--color-border);
|
|
155
|
+
border-radius: var(--border-radius-md);
|
|
156
|
+
padding: 0.75rem;
|
|
157
|
+
user-select: none;
|
|
158
|
+
font-size: var(--control-font-size, var(--font-md));
|
|
159
|
+
color: var(--color-text);
|
|
160
|
+
min-width: 17rem;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.calendar-nav {
|
|
164
|
+
display: flex;
|
|
165
|
+
align-items: center;
|
|
166
|
+
justify-content: space-between;
|
|
167
|
+
gap: 0.5rem;
|
|
168
|
+
margin-bottom: 0.5rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.calendar-month-title {
|
|
172
|
+
font-weight: 600;
|
|
173
|
+
text-transform: capitalize;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.calendar-weekdays,
|
|
177
|
+
.calendar-grid {
|
|
178
|
+
display: grid;
|
|
179
|
+
grid-template-columns: repeat(7, 1fr);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.calendar-weekday {
|
|
183
|
+
text-align: center;
|
|
184
|
+
font-size: var(--font-xs);
|
|
185
|
+
color: var(--color-muted);
|
|
186
|
+
padding: 0.25rem 0;
|
|
187
|
+
text-transform: uppercase;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.calendar-day {
|
|
191
|
+
aspect-ratio: 1;
|
|
192
|
+
display: flex;
|
|
193
|
+
align-items: center;
|
|
194
|
+
justify-content: center;
|
|
195
|
+
background: transparent;
|
|
196
|
+
border: 0;
|
|
197
|
+
color: inherit;
|
|
198
|
+
cursor: pointer;
|
|
199
|
+
border-radius: var(--border-radius-sm);
|
|
200
|
+
font-size: inherit;
|
|
201
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
202
|
+
}
|
|
203
|
+
.calendar-day:hover:not(:disabled):not(.has-marker):not(.selected) {
|
|
204
|
+
background-color: var(--color-surface);
|
|
205
|
+
}
|
|
206
|
+
.calendar-day.out-of-month {
|
|
207
|
+
color: var(--color-muted);
|
|
208
|
+
opacity: 0.45;
|
|
209
|
+
}
|
|
210
|
+
.calendar-day.today {
|
|
211
|
+
box-shadow: inset 0 0 0 1px var(--color-accent);
|
|
212
|
+
}
|
|
213
|
+
.calendar-day.has-marker {
|
|
214
|
+
background-color: var(--marker-bg);
|
|
215
|
+
color: var(--marker-color);
|
|
216
|
+
border-radius: 0;
|
|
217
|
+
}
|
|
218
|
+
.calendar-day.has-marker.marker-start {
|
|
219
|
+
border-top-left-radius: var(--border-radius-sm);
|
|
220
|
+
border-bottom-left-radius: var(--border-radius-sm);
|
|
221
|
+
}
|
|
222
|
+
.calendar-day.has-marker.marker-end {
|
|
223
|
+
border-top-right-radius: var(--border-radius-sm);
|
|
224
|
+
border-bottom-right-radius: var(--border-radius-sm);
|
|
225
|
+
}
|
|
226
|
+
.calendar-day.marker-accent {
|
|
227
|
+
--marker-bg: var(--color-accent-soft);
|
|
228
|
+
--marker-color: var(--color-accent);
|
|
229
|
+
}
|
|
230
|
+
.calendar-day.marker-success {
|
|
231
|
+
--marker-bg: var(--color-success-soft);
|
|
232
|
+
--marker-color: var(--color-success);
|
|
233
|
+
}
|
|
234
|
+
.calendar-day.marker-alert {
|
|
235
|
+
--marker-bg: var(--color-alert-soft);
|
|
236
|
+
--marker-color: var(--color-alert);
|
|
237
|
+
}
|
|
238
|
+
.calendar-day.marker-danger {
|
|
239
|
+
--marker-bg: var(--color-danger-soft);
|
|
240
|
+
--marker-color: var(--color-danger);
|
|
241
|
+
}
|
|
242
|
+
.calendar-day.marker-muted {
|
|
243
|
+
--marker-bg: var(--color-surface);
|
|
244
|
+
--marker-color: var(--color-muted);
|
|
245
|
+
}
|
|
246
|
+
.calendar-day:disabled {
|
|
247
|
+
color: var(--color-muted);
|
|
248
|
+
cursor: not-allowed;
|
|
249
|
+
opacity: 0.4;
|
|
250
|
+
}
|
|
251
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type MarkerVariant = "accent" | "success" | "alert" | "danger" | "muted";
|
|
2
|
+
export interface CalendarMarker {
|
|
3
|
+
variant: MarkerVariant;
|
|
4
|
+
start: string;
|
|
5
|
+
end: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CalendarProps {
|
|
8
|
+
selected?: string | null;
|
|
9
|
+
markers?: CalendarMarker[];
|
|
10
|
+
getMarker?: (iso: string) => CalendarMarker | null;
|
|
11
|
+
isDisabled?: (iso: string) => boolean;
|
|
12
|
+
weekStartsOn?: 0 | 1;
|
|
13
|
+
}
|
|
14
|
+
type __VLS_Props = CalendarProps;
|
|
15
|
+
type __VLS_ModelProps = {
|
|
16
|
+
"anchor"?: string | null;
|
|
17
|
+
};
|
|
18
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
19
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
20
|
+
select: (iso: string) => any;
|
|
21
|
+
dayEnter: (iso: string) => any;
|
|
22
|
+
"update:anchor": (value: string | null) => any;
|
|
23
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
24
|
+
onSelect?: ((iso: string) => any) | undefined;
|
|
25
|
+
onDayEnter?: ((iso: string) => any) | undefined;
|
|
26
|
+
"onUpdate:anchor"?: ((value: string | null) => any) | undefined;
|
|
27
|
+
}>, {
|
|
28
|
+
selected: string | null;
|
|
29
|
+
markers: CalendarMarker[];
|
|
30
|
+
weekStartsOn: 0 | 1;
|
|
31
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
32
|
+
declare const _default: typeof __VLS_export;
|
|
33
|
+
export default _default;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ComputedRef, type InjectionKey, type Ref } from "vue";
|
|
2
2
|
import type { CanvasNode, CanvasTool, CanvasToolApi } from "./types.js";
|
|
3
|
+
import type { ExportOptions, ExportResult } from "./tools/exportTool.js";
|
|
3
4
|
export interface CanvasContext {
|
|
4
5
|
tools: Ref<CanvasTool[]>;
|
|
5
6
|
activeToolId: Ref<string | null>;
|
|
@@ -33,6 +34,14 @@ export interface CanvasContext {
|
|
|
33
34
|
endAction: () => void;
|
|
34
35
|
/** Keyboard handler for undo/redo shortcuts. Attach to the stage element. */
|
|
35
36
|
onKeyDown: (e: KeyboardEvent) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Export the current canvas as an image. Overrides merge over the registered
|
|
39
|
+
* `exportTool` options (if any) and the built-in defaults. Without
|
|
40
|
+
* `onExport`/`download` overrides the call triggers a browser download for
|
|
41
|
+
* backwards compatibility; pass `{ download: false }` or supply `onExport`
|
|
42
|
+
* to consume the result instead (e.g. push to a cart, upload to a backend).
|
|
43
|
+
*/
|
|
44
|
+
exportCanvas: (overrides?: ExportOptions) => Promise<ExportResult>;
|
|
36
45
|
}
|
|
37
46
|
export declare const CANVAS_CONTEXT: InjectionKey<CanvasContext>;
|
|
38
47
|
export declare function useCanvasContext(): CanvasContext;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CanvasNode, CanvasTool, CanvasToolApi } from "./types.js";
|
|
2
|
+
import { type ExportOptions, type ExportResult } from "./tools/exportTool.js";
|
|
2
3
|
export interface CanvasProps {
|
|
3
4
|
/**
|
|
4
5
|
* Unique name for this canvas instance. Required so detached toolbars and
|
|
@@ -31,6 +32,7 @@ export interface CanvasProps {
|
|
|
31
32
|
type __VLS_Props = CanvasProps;
|
|
32
33
|
declare function getToolOptions<T extends Record<string, unknown>>(id: string): T;
|
|
33
34
|
declare function setActiveTool(id: string | null): void;
|
|
35
|
+
declare function exportCanvas(overrides?: ExportOptions): Promise<ExportResult>;
|
|
34
36
|
type __VLS_ModelProps = {
|
|
35
37
|
"nodes"?: CanvasNode[];
|
|
36
38
|
};
|
|
@@ -54,6 +56,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
54
56
|
redo: () => void;
|
|
55
57
|
canUndo: import("vue").ComputedRef<boolean>;
|
|
56
58
|
canRedo: import("vue").ComputedRef<boolean>;
|
|
59
|
+
exportCanvas: typeof exportCanvas;
|
|
57
60
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
58
61
|
"update:nodes": (value: CanvasNode<unknown>[]) => any;
|
|
59
62
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
@@ -15,6 +15,9 @@ import { useCanvasNodes } from "./composables/useCanvasNodes";
|
|
|
15
15
|
import { useCanvasSetup } from "./composables/useCanvasSetup";
|
|
16
16
|
import Stage from "./components/Stage.vue";
|
|
17
17
|
import Toolbar from "./components/Toolbar.vue";
|
|
18
|
+
import {
|
|
19
|
+
performExport
|
|
20
|
+
} from "./tools/exportTool";
|
|
18
21
|
const props = defineProps({
|
|
19
22
|
name: { type: String, required: true },
|
|
20
23
|
tools: { type: Array, required: false, default: () => [] },
|
|
@@ -122,6 +125,18 @@ function handleKeyDown(e) {
|
|
|
122
125
|
}
|
|
123
126
|
onKeyDown(e);
|
|
124
127
|
}
|
|
128
|
+
function exportCanvas(overrides = {}) {
|
|
129
|
+
const toolOpts = getToolOptions("export");
|
|
130
|
+
return performExport(
|
|
131
|
+
{
|
|
132
|
+
nodes: nodes.value,
|
|
133
|
+
tools: props.tools,
|
|
134
|
+
width: props.width,
|
|
135
|
+
height: props.height
|
|
136
|
+
},
|
|
137
|
+
{ ...toolOpts, ...overrides }
|
|
138
|
+
);
|
|
139
|
+
}
|
|
125
140
|
const context = {
|
|
126
141
|
tools: toolsRef,
|
|
127
142
|
activeToolId,
|
|
@@ -146,7 +161,8 @@ const context = {
|
|
|
146
161
|
endAction,
|
|
147
162
|
onKeyDown: handleKeyDown,
|
|
148
163
|
cursorOverride,
|
|
149
|
-
setCursor
|
|
164
|
+
setCursor,
|
|
165
|
+
exportCanvas
|
|
150
166
|
};
|
|
151
167
|
provide(CANVAS_CONTEXT, context);
|
|
152
168
|
let registeredName = null;
|
|
@@ -190,7 +206,8 @@ defineExpose({
|
|
|
190
206
|
undo,
|
|
191
207
|
redo,
|
|
192
208
|
canUndo,
|
|
193
|
-
canRedo
|
|
209
|
+
canRedo,
|
|
210
|
+
exportCanvas
|
|
194
211
|
});
|
|
195
212
|
</script>
|
|
196
213
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CanvasNode, CanvasTool, CanvasToolApi } from "./types.js";
|
|
2
|
+
import { type ExportOptions, type ExportResult } from "./tools/exportTool.js";
|
|
2
3
|
export interface CanvasProps {
|
|
3
4
|
/**
|
|
4
5
|
* Unique name for this canvas instance. Required so detached toolbars and
|
|
@@ -31,6 +32,7 @@ export interface CanvasProps {
|
|
|
31
32
|
type __VLS_Props = CanvasProps;
|
|
32
33
|
declare function getToolOptions<T extends Record<string, unknown>>(id: string): T;
|
|
33
34
|
declare function setActiveTool(id: string | null): void;
|
|
35
|
+
declare function exportCanvas(overrides?: ExportOptions): Promise<ExportResult>;
|
|
34
36
|
type __VLS_ModelProps = {
|
|
35
37
|
"nodes"?: CanvasNode[];
|
|
36
38
|
};
|
|
@@ -54,6 +56,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
54
56
|
redo: () => void;
|
|
55
57
|
canUndo: import("vue").ComputedRef<boolean>;
|
|
56
58
|
canRedo: import("vue").ComputedRef<boolean>;
|
|
59
|
+
exportCanvas: typeof exportCanvas;
|
|
57
60
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
58
61
|
"update:nodes": (value: CanvasNode<unknown>[]) => any;
|
|
59
62
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
@@ -1,18 +1,65 @@
|
|
|
1
|
-
import type { CanvasTool } from "../types.js";
|
|
1
|
+
import type { CanvasNode, CanvasTool } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Image formats Canvas2D's `toBlob`/`toDataURL` natively supports across
|
|
4
4
|
* modern browsers. Anything else (e.g. AVIF, TIFF) is encoder-dependent and
|
|
5
5
|
* not guaranteed to round-trip.
|
|
6
6
|
*/
|
|
7
7
|
export type ExportFormat = "png" | "jpeg" | "webp";
|
|
8
|
-
export interface
|
|
9
|
-
|
|
8
|
+
export interface ExportResult {
|
|
9
|
+
blob: Blob;
|
|
10
|
+
dataURL: string;
|
|
11
|
+
/** Same bytes as `blob`, wrapped as a `File` with the configured filename. */
|
|
12
|
+
file: File;
|
|
10
13
|
format: ExportFormat;
|
|
14
|
+
/** Output pixel width (already multiplied by `scale`). */
|
|
15
|
+
width: number;
|
|
16
|
+
/** Output pixel height (already multiplied by `scale`). */
|
|
17
|
+
height: number;
|
|
18
|
+
filename: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ExportOptions {
|
|
21
|
+
/** Output format. Defaults to PNG. */
|
|
22
|
+
format?: ExportFormat;
|
|
11
23
|
/** Quality for lossy formats (jpeg, webp), 0–1. Ignored for PNG. */
|
|
12
|
-
quality
|
|
24
|
+
quality?: number;
|
|
13
25
|
/** File name without extension. */
|
|
14
|
-
filename
|
|
26
|
+
filename?: string;
|
|
15
27
|
/** Pixel scale relative to the visible canvas (1 = same size). */
|
|
16
|
-
scale
|
|
28
|
+
scale?: number;
|
|
29
|
+
/**
|
|
30
|
+
* If provided, receives the export result instead of (or alongside) the
|
|
31
|
+
* download. Use to push the image into application state (e.g. a cart) or
|
|
32
|
+
* upload to a backend. Async — the canvas awaits the returned Promise.
|
|
33
|
+
*/
|
|
34
|
+
onExport?: (result: ExportResult) => void | Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Trigger a browser download. Defaults to `true` when `onExport` is not
|
|
37
|
+
* provided, `false` when it is.
|
|
38
|
+
*/
|
|
39
|
+
download?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ExportToolOptions extends Required<Pick<ExportOptions, "format" | "quality" | "filename" | "scale">>, Pick<ExportOptions, "onExport" | "download">, Record<string, unknown> {
|
|
17
42
|
}
|
|
43
|
+
export declare const DEFAULT_EXPORT_OPTIONS: ExportToolOptions;
|
|
44
|
+
export interface CanvasSnapshot {
|
|
45
|
+
nodes: CanvasNode[];
|
|
46
|
+
tools: CanvasTool[];
|
|
47
|
+
width: number;
|
|
48
|
+
height: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Render the snapshot to an offscreen canvas and produce a Blob/dataURL/File.
|
|
52
|
+
* Pure: does not download or invoke callbacks.
|
|
53
|
+
*
|
|
54
|
+
* The offscreen canvas is sized exactly to `snapshot.width × snapshot.height`
|
|
55
|
+
* (× `scale`), so any node geometry outside the canvas is naturally clipped —
|
|
56
|
+
* the result captures only what would be visible on the live canvas.
|
|
57
|
+
*/
|
|
58
|
+
export declare function renderCanvasSnapshot(snapshot: CanvasSnapshot, options?: ExportOptions): Promise<ExportResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Render the snapshot, then optionally hand the result to `onExport` and/or
|
|
61
|
+
* trigger a browser download. Resolves with the result so callers can chain
|
|
62
|
+
* additional work (uploads, state updates).
|
|
63
|
+
*/
|
|
64
|
+
export declare function performExport(snapshot: CanvasSnapshot, options?: ExportOptions): Promise<ExportResult>;
|
|
18
65
|
export declare function exportTool(options?: Partial<ExportToolOptions>): CanvasTool<never, ExportToolOptions>;
|