From 565b59c4d02ac481bc912a444d7ff719c92301fb Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Tue, 22 Jul 2025 17:22:17 +0800 Subject: [PATCH] add FormattedMessage component --- next-ui/src/components/FormattedMessage.mdx | 11 +++ .../components/FormattedMessage.stories.ts | 73 +++++++++++++++++++ next-ui/src/components/FormattedMessage.ts | 26 +++++++ 3 files changed, 110 insertions(+) create mode 100644 next-ui/src/components/FormattedMessage.mdx create mode 100644 next-ui/src/components/FormattedMessage.stories.ts create mode 100644 next-ui/src/components/FormattedMessage.ts diff --git a/next-ui/src/components/FormattedMessage.mdx b/next-ui/src/components/FormattedMessage.mdx new file mode 100644 index 00000000..3c04cf8d --- /dev/null +++ b/next-ui/src/components/FormattedMessage.mdx @@ -0,0 +1,11 @@ +import {Canvas, Meta} from '@storybook/addon-docs/blocks'; + +import * as Stories from './FormattedMessage.stories'; + + + +# FormattedMessage + +Component to display formatted `formatjs` messages using dynamic slots. + + diff --git a/next-ui/src/components/FormattedMessage.stories.ts b/next-ui/src/components/FormattedMessage.stories.ts new file mode 100644 index 00000000..517a8870 --- /dev/null +++ b/next-ui/src/components/FormattedMessage.stories.ts @@ -0,0 +1,73 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' + +import FormattedMessage from './FormattedMessage.ts' + +const meta = { + component: FormattedMessage, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout + }, + args: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + render: (args: object) => ({ + components: { FormattedMessage }, + setup() { + return { args } + }, + template: ` + + + + + + `, + }), +} + +export const NoMarkup: Story = { + render: (args: object) => ({ + components: { FormattedMessage }, + setup() { + return { args } + }, + template: ` + + `, + }), +} + +export const NoMarkupTag: Story = { + render: (args: object) => ({ + components: { FormattedMessage }, + setup() { + return { args } + }, + template: ` + + `, + }), +} diff --git a/next-ui/src/components/FormattedMessage.ts b/next-ui/src/components/FormattedMessage.ts new file mode 100644 index 00000000..b3ad2e16 --- /dev/null +++ b/next-ui/src/components/FormattedMessage.ts @@ -0,0 +1,26 @@ +import { defineComponent, h } from 'vue' +import { type MessageDescriptor, useIntl } from 'vue-intl' + +// from: https://github.com/formatjs/formatjs/discussions/3961 +export default defineComponent({ + props: { + messageDescriptor: Object as PropType, + tag: String, + values: Object, + }, + setup(props, context) { + const { messageDescriptor, tag, values = {} } = props + const intl = useIntl() + const slotNames = Object.keys(context.slots) + + const message = intl.formatMessage(messageDescriptor, { + ...values, + ...slotNames.reduce((slots, name) => { + slots[name] = (content) => context.slots[name](() => content) + return slots + }, {}), + }) + + return () => (tag || Array.isArray(message) ? h(tag || 'div', message) : message) + }, +})