@messagevisor/vue 0.0.1 → 0.1.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +9 -0
  4. package/jest.config.js +13 -0
  5. package/lib/MessagevisorContext.d.ts +21 -0
  6. package/lib/MessagevisorContext.js +5 -0
  7. package/lib/MessagevisorContext.js.map +1 -0
  8. package/lib/MessagevisorProvider.d.ts +44 -0
  9. package/lib/MessagevisorProvider.js +45 -0
  10. package/lib/MessagevisorProvider.js.map +1 -0
  11. package/lib/api.d.ts +20 -0
  12. package/lib/api.js +60 -0
  13. package/lib/api.js.map +1 -0
  14. package/lib/components.d.ts +86 -0
  15. package/lib/components.js +95 -0
  16. package/lib/components.js.map +1 -0
  17. package/lib/createMessagevisorProvider.d.ts +18 -0
  18. package/lib/createMessagevisorProvider.js +24 -0
  19. package/lib/createMessagevisorProvider.js.map +1 -0
  20. package/lib/index.d.ts +10 -0
  21. package/lib/index.js +41 -0
  22. package/lib/index.js.map +1 -0
  23. package/lib/richText.d.ts +16 -0
  24. package/lib/richText.js +87 -0
  25. package/lib/richText.js.map +1 -0
  26. package/lib/useMessagevisor.d.ts +1 -0
  27. package/lib/useMessagevisor.js +11 -0
  28. package/lib/useMessagevisor.js.map +1 -0
  29. package/lib/useMessagevisorSnapshot.d.ts +3 -0
  30. package/lib/useMessagevisorSnapshot.js +18 -0
  31. package/lib/useMessagevisorSnapshot.js.map +1 -0
  32. package/lib/useReactiveMessagevisor.d.ts +36 -0
  33. package/lib/useReactiveMessagevisor.js +169 -0
  34. package/lib/useReactiveMessagevisor.js.map +1 -0
  35. package/lib/useSdk.d.ts +2 -0
  36. package/lib/useSdk.js +17 -0
  37. package/lib/useSdk.js.map +1 -0
  38. package/package.json +49 -13
  39. package/src/MessagevisorContext.ts +28 -0
  40. package/src/MessagevisorProvider.spec.ts +99 -0
  41. package/src/MessagevisorProvider.ts +48 -0
  42. package/src/api.ts +119 -0
  43. package/src/components.spec.ts +134 -0
  44. package/src/components.ts +111 -0
  45. package/src/createMessagevisorProvider.ts +43 -0
  46. package/src/index.ts +41 -0
  47. package/src/richText.ts +130 -0
  48. package/src/testUtils.ts +82 -0
  49. package/src/useMessagevisor.spec.ts +70 -0
  50. package/src/useMessagevisor.ts +10 -0
  51. package/src/useMessagevisorSnapshot.ts +20 -0
  52. package/src/useReactiveMessagevisor.spec.ts +185 -0
  53. package/src/useReactiveMessagevisor.ts +353 -0
  54. package/src/useSdk.ts +19 -0
  55. package/tsconfig.cjs.json +12 -0
  56. package/tsconfig.typecheck.json +4 -0
@@ -0,0 +1,70 @@
1
+ import { mount } from "@vue/test-utils";
2
+ import { defineComponent, h } from "vue";
3
+
4
+ import { MessagevisorProvider } from "./MessagevisorProvider";
5
+ import { useMessagevisor } from "./useMessagevisor";
6
+ import { createTestInstance } from "./testUtils";
7
+
8
+ describe("useMessagevisor", function () {
9
+ it("returns imperative translation, formatting, and state APIs", function () {
10
+ const m = createTestInstance();
11
+ const calls: string[] = [];
12
+ const Child = defineComponent({
13
+ setup() {
14
+ const api = useMessagevisor();
15
+
16
+ calls.push(api.t("greeting", { name: "Ada" }));
17
+ calls.push(api.translate("greeting", { name: "Grace" }));
18
+ calls.push(api.formatMessage("Raw {name}", { name: "Lin" }));
19
+ calls.push(api.formatNumber(12, { style: "currency", currency: "EUR" }));
20
+ api.setContext({ plan: "pro" });
21
+ calls.push(api.t("greeting", { name: "Ada" }));
22
+
23
+ return () => h("p", calls.join("|"));
24
+ },
25
+ });
26
+
27
+ mount(MessagevisorProvider, {
28
+ props: { instance: m },
29
+ slots: { default: () => h(Child) },
30
+ });
31
+
32
+ expect(calls[0]).toEqual("Hello Ada");
33
+ expect(calls[1]).toEqual("Hello Grace");
34
+ expect(calls[2]).toEqual("Raw Lin");
35
+ expect(calls[3]).toContain("€");
36
+ expect(calls[4]).toEqual("Welcome back, pro Ada");
37
+ });
38
+
39
+ it("runs provider modules after SDK formatting", function () {
40
+ const m = createTestInstance();
41
+ const Child = defineComponent({
42
+ setup() {
43
+ const { t, formatMessage } = useMessagevisor();
44
+
45
+ return () =>
46
+ h("p", [
47
+ h("span", { class: "t" }, t("greeting", { name: "Ada" })),
48
+ h("span", { class: "format" }, formatMessage("Hi {name}", { name: "Ada" })),
49
+ ]);
50
+ },
51
+ });
52
+
53
+ const wrapper = mount(MessagevisorProvider, {
54
+ props: {
55
+ instance: m,
56
+ modules: [
57
+ {
58
+ transform({ translation, source }) {
59
+ return typeof translation === "string" ? `${source}:${translation}` : undefined;
60
+ },
61
+ },
62
+ ],
63
+ },
64
+ slots: { default: () => h(Child) },
65
+ });
66
+
67
+ expect(wrapper.find(".t").text()).toEqual("translation:Hello Ada");
68
+ expect(wrapper.find(".format").text()).toEqual("formatMessage:Hi Ada");
69
+ });
70
+ });
@@ -0,0 +1,10 @@
1
+ import { computed } from "vue";
2
+
3
+ import { createMessagevisorApi } from "./api";
4
+ import { useMessagevisorContextValue } from "./useSdk";
5
+
6
+ export function useMessagevisor() {
7
+ const context = useMessagevisorContextValue();
8
+
9
+ return computed(() => createMessagevisorApi(context)).value;
10
+ }
@@ -0,0 +1,20 @@
1
+ import type { MessagevisorSnapshot } from "@messagevisor/sdk";
2
+ import { computed, getCurrentScope, onScopeDispose, shallowRef, type ComputedRef } from "vue";
3
+
4
+ import { useSdk } from "./useSdk";
5
+
6
+ export function useMessagevisorSnapshot(): ComputedRef<MessagevisorSnapshot> {
7
+ const sdk = useSdk();
8
+ const snapshot = shallowRef(sdk.getSnapshot());
9
+ const unsubscribe = sdk.subscribe(() => {
10
+ snapshot.value = sdk.getSnapshot();
11
+ });
12
+
13
+ snapshot.value = sdk.getSnapshot();
14
+
15
+ if (getCurrentScope()) {
16
+ onScopeDispose(unsubscribe);
17
+ }
18
+
19
+ return computed(() => snapshot.value);
20
+ }
@@ -0,0 +1,185 @@
1
+ import { mount } from "@vue/test-utils";
2
+ import { computed, defineComponent, h, nextTick, ref } from "vue";
3
+
4
+ import { MessagevisorProvider } from "./MessagevisorProvider";
5
+ import {
6
+ useCurrency,
7
+ useDirection,
8
+ useFormatDate,
9
+ useFormatDateTimeRange,
10
+ useFormatMessage,
11
+ useFormatNumber,
12
+ useFormatNumberToParts,
13
+ useFormatPlural,
14
+ useFormatRelativeTime,
15
+ useFormatTime,
16
+ useLocale,
17
+ useLocaleInfo,
18
+ useMessagevisorContext,
19
+ useTimeZone,
20
+ useTranslation,
21
+ } from "./useReactiveMessagevisor";
22
+ import { useMessagevisorSnapshot } from "./useMessagevisorSnapshot";
23
+ import { useSdk } from "./useSdk";
24
+ import { createTestInstance, nlDatafile } from "./testUtils";
25
+
26
+ describe("reactive composables", function () {
27
+ it("recomputes focused state when the SDK snapshot changes", async function () {
28
+ const m = createTestInstance();
29
+ const Child = defineComponent({
30
+ setup() {
31
+ const sdk = useSdk(); // eslint-disable-line @typescript-eslint/no-unused-vars
32
+ const snapshot = useMessagevisorSnapshot();
33
+ const locale = useLocale();
34
+ const direction = useDirection();
35
+ const info = useLocaleInfo();
36
+ const context = useMessagevisorContext();
37
+ const currency = useCurrency();
38
+ const timeZone = useTimeZone();
39
+
40
+ return () =>
41
+ h("div", [
42
+ h("span", { class: "locale" }, locale.value || ""),
43
+ h("span", { class: "direction" }, direction.value || ""),
44
+ h("span", { class: "info" }, info.value.locale || ""),
45
+ h("span", { class: "context" }, String(context.value.plan || "")),
46
+ h("span", { class: "currency" }, currency.value || ""),
47
+ h("span", { class: "timeZone" }, timeZone.value || ""),
48
+ h(
49
+ "span",
50
+ { class: "revision" },
51
+ snapshot.value.datafileRevisionsByLocale[locale.value || ""],
52
+ ),
53
+ ]);
54
+ },
55
+ });
56
+
57
+ const wrapper = mount(MessagevisorProvider, {
58
+ props: { instance: m },
59
+ slots: { default: () => h(Child) },
60
+ });
61
+
62
+ m.setContext({ plan: "pro" });
63
+ m.setCurrency("EUR");
64
+ m.setTimeZone("Europe/Amsterdam");
65
+ m.setDatafile(nlDatafile);
66
+ m.setLocale("nl-NL");
67
+ await nextTick();
68
+
69
+ expect(wrapper.find(".locale").text()).toEqual("nl-NL");
70
+ expect(wrapper.find(".direction").text()).toEqual("ltr");
71
+ expect(wrapper.find(".info").text()).toEqual("nl-NL");
72
+ expect(wrapper.find(".context").text()).toEqual("pro");
73
+ expect(wrapper.find(".currency").text()).toEqual("EUR");
74
+ expect(wrapper.find(".timeZone").text()).toEqual("Europe/Amsterdam");
75
+ expect(wrapper.find(".revision").text()).toEqual("1-nl");
76
+ });
77
+
78
+ it("accepts plain values, refs, computed refs, and getters", async function () {
79
+ const m = createTestInstance();
80
+ const key = ref("greeting");
81
+ const name = ref("Ada");
82
+ const plan = ref("free");
83
+ const amount = ref(12);
84
+ const Child = defineComponent({
85
+ setup() {
86
+ const preset = computed(() => "money");
87
+ const translation = useTranslation(
88
+ () => key.value,
89
+ { name },
90
+ () => ({ context: { plan: plan.value } }),
91
+ );
92
+ const total = useFormatMessage("Total: {amount, number, money}", { amount });
93
+ const money = useFormatNumber(amount, preset);
94
+
95
+ return () =>
96
+ h("div", [
97
+ h("span", { class: "translation" }, translation.value),
98
+ h("span", { class: "total" }, total.value),
99
+ h("span", { class: "money" }, money.value),
100
+ ]);
101
+ },
102
+ });
103
+
104
+ const wrapper = mount(MessagevisorProvider, {
105
+ props: { instance: m },
106
+ slots: { default: () => h(Child) },
107
+ });
108
+
109
+ expect(wrapper.find(".translation").text()).toEqual("Hello Ada");
110
+ expect(wrapper.find(".total").text()).toContain("$12.00");
111
+ expect(wrapper.find(".money").text()).toContain("$12.00");
112
+
113
+ name.value = "Grace";
114
+ plan.value = "pro";
115
+ amount.value = 18;
116
+ await nextTick();
117
+
118
+ expect(wrapper.find(".translation").text()).toEqual("Welcome back, pro Grace");
119
+ expect(wrapper.find(".total").text()).toContain("$18.00");
120
+ expect(wrapper.find(".money").text()).toContain("$18.00");
121
+ });
122
+
123
+ it("covers formatter composables", function () {
124
+ const m = createTestInstance();
125
+ const Child = defineComponent({
126
+ setup() {
127
+ const date = "2026-05-12T12:30:00Z";
128
+
129
+ const currency = useFormatNumber(12, "money", { currency: "EUR" });
130
+ const formattedDate = useFormatDate(date, "short");
131
+ const range = useFormatDateTimeRange(date, "2026-05-13T12:30:00Z", "short");
132
+ const numberParts = useFormatNumberToParts(12, "money");
133
+ const plural = useFormatPlural(1);
134
+ const relative = useFormatRelativeTime(-1, "day", "short");
135
+ const time = useFormatTime(date, "short");
136
+
137
+ return () =>
138
+ h("div", [
139
+ h("span", { class: "currency" }, currency.value),
140
+ h("span", { class: "date" }, formattedDate.value),
141
+ h("span", { class: "range" }, range.value),
142
+ h("span", { class: "parts" }, String(numberParts.value.length > 0)),
143
+ h("span", { class: "plural" }, plural.value),
144
+ h("span", { class: "relative" }, relative.value),
145
+ h("span", { class: "time" }, time.value),
146
+ ]);
147
+ },
148
+ });
149
+
150
+ const wrapper = mount(MessagevisorProvider, {
151
+ props: { instance: m },
152
+ slots: { default: () => h(Child) },
153
+ });
154
+
155
+ expect(wrapper.find(".currency").text()).toContain("€");
156
+ expect(wrapper.find(".date").text()).toContain("5/12/26");
157
+ expect(wrapper.find(".range").text()).toContain("May");
158
+ expect(wrapper.find(".parts").text()).toEqual("true");
159
+ expect(wrapper.find(".plural").text()).toEqual("one");
160
+ expect(wrapper.find(".relative").text()).toEqual("yesterday");
161
+ expect(wrapper.find(".time").text()).toContain("12:30");
162
+ });
163
+
164
+ it("unsubscribes from SDK changes on unmount", function () {
165
+ const m = createTestInstance();
166
+ const unsubscribe = jest.fn();
167
+ const subscribe = jest.spyOn(m, "subscribe").mockReturnValue(unsubscribe);
168
+ const Child = defineComponent({
169
+ setup() {
170
+ useLocale();
171
+
172
+ return () => h("p", "ok");
173
+ },
174
+ });
175
+
176
+ const wrapper = mount(MessagevisorProvider, {
177
+ props: { instance: m },
178
+ slots: { default: () => h(Child) },
179
+ });
180
+
181
+ expect(subscribe).toHaveBeenCalledTimes(1);
182
+ wrapper.unmount();
183
+ expect(unsubscribe).toHaveBeenCalledTimes(1);
184
+ });
185
+ });
@@ -0,0 +1,353 @@
1
+ import type {
2
+ EvaluationOptions,
3
+ MessageFormatResult,
4
+ MessagePrimitiveValue,
5
+ MessageValues,
6
+ TranslateOptions,
7
+ } from "@messagevisor/sdk";
8
+ import type {
9
+ Context,
10
+ FormatDateTimePresetOptions,
11
+ FormatNumberPresetOptions,
12
+ FormatRelativeTimePresetOptions,
13
+ LocaleDirection,
14
+ LocaleKey,
15
+ MessageKey,
16
+ } from "@messagevisor/types";
17
+ import { computed, isRef, type ComputedRef, type MaybeRefOrGetter, toValue } from "vue";
18
+
19
+ import type { VueMessageChunk } from "./MessagevisorContext";
20
+ import { createRichTextTools, type VueMessageValues, type VueRichMessageValues } from "./richText";
21
+ import { useMessagevisorSnapshot } from "./useMessagevisorSnapshot";
22
+ import { useMessagevisorContextValue } from "./useSdk";
23
+
24
+ export interface LocaleInfo {
25
+ locale: LocaleKey | null;
26
+ direction: LocaleDirection | undefined;
27
+ }
28
+
29
+ type MaybeRecord<TRecord extends object> = MaybeRefOrGetter<TRecord | undefined>;
30
+ type MaybeRecordValue<TValue> = TValue | MaybeRefOrGetter<TValue>;
31
+ type MaybePrimitiveValues = Record<string, MaybeRecordValue<MessagePrimitiveValue>>;
32
+ type MaybeRichValues = Record<
33
+ string,
34
+ MaybeRecordValue<MessagePrimitiveValue> | VueRichMessageValues[string]
35
+ >;
36
+
37
+ function useReactiveContext() {
38
+ const context = useMessagevisorContextValue();
39
+ useMessagevisorSnapshot();
40
+
41
+ return context;
42
+ }
43
+
44
+ function resolveMaybeRecord<TRecord extends object>(record?: MaybeRecord<TRecord>) {
45
+ const resolved = typeof record === "undefined" ? undefined : toValue(record);
46
+
47
+ if (!resolved) {
48
+ return resolved;
49
+ }
50
+
51
+ return Object.fromEntries(
52
+ Object.entries(resolved as Record<string, unknown>).map(([key, value]) => {
53
+ if (isRef(value)) {
54
+ return [key, value.value];
55
+ }
56
+
57
+ if (typeof value === "function" && value.length === 0) {
58
+ return [key, (value as () => unknown)()];
59
+ }
60
+
61
+ return [key, value];
62
+ }),
63
+ ) as TRecord;
64
+ }
65
+
66
+ export function useLocale(): ComputedRef<LocaleKey | null> {
67
+ const snapshot = useMessagevisorSnapshot();
68
+
69
+ return computed(() => snapshot.value.locale);
70
+ }
71
+
72
+ export function useDirection(): ComputedRef<LocaleDirection | undefined> {
73
+ const snapshot = useMessagevisorSnapshot();
74
+
75
+ return computed(() => snapshot.value.direction);
76
+ }
77
+
78
+ export function useLocaleInfo(): ComputedRef<LocaleInfo> {
79
+ const snapshot = useMessagevisorSnapshot();
80
+
81
+ return computed(() => ({
82
+ locale: snapshot.value.locale,
83
+ direction: snapshot.value.direction,
84
+ }));
85
+ }
86
+
87
+ export function useMessagevisorContext(): ComputedRef<Context> {
88
+ const snapshot = useMessagevisorSnapshot();
89
+
90
+ return computed(() => snapshot.value.context);
91
+ }
92
+
93
+ export function useCurrency(): ComputedRef<string | undefined> {
94
+ const snapshot = useMessagevisorSnapshot();
95
+
96
+ return computed(() => snapshot.value.currency);
97
+ }
98
+
99
+ export function useTimeZone(): ComputedRef<string | undefined> {
100
+ const snapshot = useMessagevisorSnapshot();
101
+
102
+ return computed(() => snapshot.value.timeZone);
103
+ }
104
+
105
+ export function useTranslation(
106
+ messageKey: MaybeRefOrGetter<MessageKey>,
107
+ values?: MaybeRecord<MaybePrimitiveValues>,
108
+ options?: MaybeRecord<TranslateOptions>,
109
+ ): ComputedRef<string>;
110
+ export function useTranslation(
111
+ messageKey: MaybeRefOrGetter<MessageKey>,
112
+ values: MaybeRecord<MaybeRichValues>,
113
+ options?: MaybeRecord<TranslateOptions>,
114
+ ): ComputedRef<MessageFormatResult<VueMessageChunk>>;
115
+ export function useTranslation(
116
+ messageKey: MaybeRefOrGetter<MessageKey>,
117
+ values?: MaybeRecord<Record<string, unknown>>,
118
+ options?: MaybeRecord<TranslateOptions>,
119
+ ) {
120
+ const context = useReactiveContext();
121
+ const richText = createRichTextTools(context);
122
+
123
+ return computed(() => {
124
+ const key = toValue(messageKey);
125
+ const resolvedValues = resolveMaybeRecord(values);
126
+ const resolvedOptions = resolveMaybeRecord(options);
127
+ const message = context.instance.getRawTranslation(key, resolvedOptions);
128
+ const translation = context.instance.translate<VueMessageChunk>(
129
+ key,
130
+ richText.mergeValues(
131
+ resolvedValues as VueMessageValues | undefined,
132
+ message,
133
+ ) as MessageValues<VueMessageChunk>,
134
+ resolvedOptions,
135
+ );
136
+
137
+ return richText.wrapResult(
138
+ richText.runModules(translation, {
139
+ source: "translation",
140
+ messageKey: key,
141
+ }),
142
+ );
143
+ });
144
+ }
145
+
146
+ export function useFormatMessage(
147
+ message: MaybeRefOrGetter<string>,
148
+ values?: MaybeRecord<MaybePrimitiveValues>,
149
+ options?: MaybeRecord<EvaluationOptions>,
150
+ ): ComputedRef<string>;
151
+ export function useFormatMessage(
152
+ message: MaybeRefOrGetter<string>,
153
+ values: MaybeRecord<MaybeRichValues>,
154
+ options?: MaybeRecord<EvaluationOptions>,
155
+ ): ComputedRef<MessageFormatResult<VueMessageChunk>>;
156
+ export function useFormatMessage(
157
+ message: MaybeRefOrGetter<string>,
158
+ values?: MaybeRecord<Record<string, unknown>>,
159
+ options?: MaybeRecord<EvaluationOptions>,
160
+ ) {
161
+ const context = useReactiveContext();
162
+ const richText = createRichTextTools(context);
163
+
164
+ return computed(() => {
165
+ const resolvedMessage = toValue(message);
166
+ const translation = context.instance.formatMessage<VueMessageChunk>(
167
+ resolvedMessage,
168
+ richText.mergeValues(
169
+ resolveMaybeRecord(values) as VueMessageValues | undefined,
170
+ resolvedMessage,
171
+ ) as MessageValues<VueMessageChunk>,
172
+ resolveMaybeRecord(options),
173
+ );
174
+
175
+ return richText.wrapResult(
176
+ richText.runModules(translation, {
177
+ source: "formatMessage",
178
+ }),
179
+ );
180
+ });
181
+ }
182
+
183
+ export function useFormatNumber(
184
+ value: MaybeRefOrGetter<number>,
185
+ presetOrOptions?: MaybeRefOrGetter<string | FormatNumberPresetOptions | undefined>,
186
+ options?: MaybeRecord<EvaluationOptions>,
187
+ ) {
188
+ const context = useReactiveContext();
189
+
190
+ return computed(() =>
191
+ context.instance.formatNumber(
192
+ toValue(value),
193
+ toValue(presetOrOptions),
194
+ resolveMaybeRecord(options),
195
+ ),
196
+ );
197
+ }
198
+
199
+ export function useFormatNumberToParts(
200
+ value: MaybeRefOrGetter<number>,
201
+ presetOrOptions?: MaybeRefOrGetter<string | FormatNumberPresetOptions | undefined>,
202
+ options?: MaybeRecord<EvaluationOptions>,
203
+ ) {
204
+ const context = useReactiveContext();
205
+
206
+ return computed(() =>
207
+ context.instance.formatNumberToParts(
208
+ toValue(value),
209
+ toValue(presetOrOptions),
210
+ resolveMaybeRecord(options),
211
+ ),
212
+ );
213
+ }
214
+
215
+ export function useFormatDate(
216
+ value: MaybeRefOrGetter<Date | number | string>,
217
+ presetOrOptions?: MaybeRefOrGetter<string | FormatDateTimePresetOptions | undefined>,
218
+ options?: MaybeRecord<EvaluationOptions>,
219
+ ) {
220
+ const context = useReactiveContext();
221
+
222
+ return computed(() =>
223
+ context.instance.formatDate(
224
+ toValue(value),
225
+ toValue(presetOrOptions),
226
+ resolveMaybeRecord(options),
227
+ ),
228
+ );
229
+ }
230
+
231
+ export function useFormatDateToParts(
232
+ value: MaybeRefOrGetter<Date | number | string>,
233
+ presetOrOptions?: MaybeRefOrGetter<string | FormatDateTimePresetOptions | undefined>,
234
+ options?: MaybeRecord<EvaluationOptions>,
235
+ ) {
236
+ const context = useReactiveContext();
237
+
238
+ return computed(() =>
239
+ context.instance.formatDateToParts(
240
+ toValue(value),
241
+ toValue(presetOrOptions),
242
+ resolveMaybeRecord(options),
243
+ ),
244
+ );
245
+ }
246
+
247
+ export function useFormatTime(
248
+ value: MaybeRefOrGetter<Date | number | string>,
249
+ presetOrOptions?: MaybeRefOrGetter<string | FormatDateTimePresetOptions | undefined>,
250
+ options?: MaybeRecord<EvaluationOptions>,
251
+ ) {
252
+ const context = useReactiveContext();
253
+
254
+ return computed(() =>
255
+ context.instance.formatTime(
256
+ toValue(value),
257
+ toValue(presetOrOptions),
258
+ resolveMaybeRecord(options),
259
+ ),
260
+ );
261
+ }
262
+
263
+ export function useFormatTimeToParts(
264
+ value: MaybeRefOrGetter<Date | number | string>,
265
+ presetOrOptions?: MaybeRefOrGetter<string | FormatDateTimePresetOptions | undefined>,
266
+ options?: MaybeRecord<EvaluationOptions>,
267
+ ) {
268
+ const context = useReactiveContext();
269
+
270
+ return computed(() =>
271
+ context.instance.formatTimeToParts(
272
+ toValue(value),
273
+ toValue(presetOrOptions),
274
+ resolveMaybeRecord(options),
275
+ ),
276
+ );
277
+ }
278
+
279
+ export function useFormatDateTimeRange(
280
+ start: MaybeRefOrGetter<Date | number | string>,
281
+ end: MaybeRefOrGetter<Date | number | string>,
282
+ presetOrOptions?: MaybeRefOrGetter<string | FormatDateTimePresetOptions | undefined>,
283
+ options?: MaybeRecord<EvaluationOptions>,
284
+ ) {
285
+ const context = useReactiveContext();
286
+
287
+ return computed(() =>
288
+ context.instance.formatDateTimeRange(
289
+ toValue(start),
290
+ toValue(end),
291
+ toValue(presetOrOptions),
292
+ resolveMaybeRecord(options),
293
+ ),
294
+ );
295
+ }
296
+
297
+ export function useFormatRelativeTime(
298
+ value: MaybeRefOrGetter<number>,
299
+ unit: MaybeRefOrGetter<Intl.RelativeTimeFormatUnit>,
300
+ presetOrOptions?: MaybeRefOrGetter<string | FormatRelativeTimePresetOptions | undefined>,
301
+ options?: MaybeRecord<EvaluationOptions>,
302
+ ) {
303
+ const context = useReactiveContext();
304
+
305
+ return computed(() =>
306
+ context.instance.formatRelativeTime(
307
+ toValue(value),
308
+ toValue(unit),
309
+ toValue(presetOrOptions),
310
+ resolveMaybeRecord(options),
311
+ ),
312
+ );
313
+ }
314
+
315
+ export function useFormatPlural(
316
+ value: MaybeRefOrGetter<number>,
317
+ options?: MaybeRecord<Intl.PluralRulesOptions>,
318
+ ) {
319
+ const context = useReactiveContext();
320
+
321
+ return computed(() => context.instance.formatPlural(toValue(value), resolveMaybeRecord(options)));
322
+ }
323
+
324
+ export function useFormatList(
325
+ values: MaybeRefOrGetter<string[]>,
326
+ options?: MaybeRecord<Record<string, unknown>>,
327
+ ) {
328
+ const context = useReactiveContext();
329
+
330
+ return computed(() => context.instance.formatList(toValue(values), resolveMaybeRecord(options)));
331
+ }
332
+
333
+ export function useFormatListToParts(
334
+ values: MaybeRefOrGetter<string[]>,
335
+ options?: MaybeRecord<Record<string, unknown>>,
336
+ ) {
337
+ const context = useReactiveContext();
338
+
339
+ return computed(() =>
340
+ context.instance.formatListToParts(toValue(values), resolveMaybeRecord(options)),
341
+ );
342
+ }
343
+
344
+ export function useFormatDisplayName(
345
+ value: MaybeRefOrGetter<string>,
346
+ options?: MaybeRecord<Record<string, unknown>>,
347
+ ) {
348
+ const context = useReactiveContext();
349
+
350
+ return computed(() =>
351
+ context.instance.formatDisplayName(toValue(value), resolveMaybeRecord(options)),
352
+ );
353
+ }
package/src/useSdk.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { inject } from "vue";
2
+
3
+ import { MessagevisorInjectionKey } from "./MessagevisorContext";
4
+
5
+ export function useMessagevisorContextValue() {
6
+ const context = inject(MessagevisorInjectionKey);
7
+
8
+ if (!context) {
9
+ throw new Error(
10
+ "useSdk must be used within MessagevisorProvider or createMessagevisorProvider().",
11
+ );
12
+ }
13
+
14
+ return context;
15
+ }
16
+
17
+ export function useSdk() {
18
+ return useMessagevisorContextValue().instance;
19
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.cjs.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "rootDir": "./src",
6
+ "moduleResolution": "node",
7
+ "esModuleInterop": true,
8
+ "jsx": "preserve"
9
+ },
10
+ "include": ["./src/**/*.ts"],
11
+ "exclude": ["./src/**/*.spec.ts", "./src/testUtils.ts"]
12
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.cjs.json",
3
+ "exclude": []
4
+ }