@eventuras/ratio-ui-native 0.0.0-stage → 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.
- package/LICENSE +382 -0
- package/README.md +96 -2
- package/dist/Button.d.ts +23 -0
- package/dist/Button.d.ts.map +1 -0
- package/dist/Button.js +98 -0
- package/dist/Button.js.map +1 -0
- package/dist/Card.d.ts +28 -0
- package/dist/Card.d.ts.map +1 -0
- package/dist/Card.js +51 -0
- package/dist/Card.js.map +1 -0
- package/dist/RatioProvider.d.ts +38 -0
- package/dist/RatioProvider.d.ts.map +1 -0
- package/dist/RatioProvider.js +55 -0
- package/dist/RatioProvider.js.map +1 -0
- package/dist/Text.d.ts +37 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/Text.js +66 -0
- package/dist/Text.js.map +1 -0
- package/dist/chat/ChatLinkPreviewCard.d.ts +13 -0
- package/dist/chat/ChatLinkPreviewCard.d.ts.map +1 -0
- package/dist/chat/ChatLinkPreviewCard.js +78 -0
- package/dist/chat/ChatLinkPreviewCard.js.map +1 -0
- package/dist/chat/ChatLog.d.ts +42 -0
- package/dist/chat/ChatLog.d.ts.map +1 -0
- package/dist/chat/ChatLog.js +198 -0
- package/dist/chat/ChatLog.js.map +1 -0
- package/dist/chat/ChatReactions.d.ts +21 -0
- package/dist/chat/ChatReactions.d.ts.map +1 -0
- package/dist/chat/ChatReactions.js +63 -0
- package/dist/chat/ChatReactions.js.map +1 -0
- package/dist/chat/ChatText.d.ts +15 -0
- package/dist/chat/ChatText.d.ts.map +1 -0
- package/dist/chat/ChatText.js +36 -0
- package/dist/chat/ChatText.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/package.json +61 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatLog.js","names":[],"sources":["../../src/chat/ChatLog.tsx"],"sourcesContent":["// ratio-ui · design system for knowledge sharing\n// SPDX-FileCopyrightText: 2026 Losol AS\n// SPDX-License-Identifier: MPL-2.0\n\nimport React, { useCallback, useMemo } from 'react';\nimport { FlatList, Linking, View, type FlatListProps } from 'react-native';\nimport {\n hasPreviewContent,\n mentionsNick,\n sameNick,\n segmentChatText,\n type ChatLogMessage,\n type ChatRole,\n} from '@eventuras/ratio-ui-core/chat';\nimport { useTheme } from '../RatioProvider';\nimport { Text } from '../Text';\nimport { ChatLinkPreviewCard } from './ChatLinkPreviewCard';\nimport { ChatReactions } from './ChatReactions';\nimport { ChatText } from './ChatText';\n\n/** Built-in text of `ChatLog`. Each entry falls back to English. @beta */\nexport interface ChatLogLabels {\n /** Name of each message's reaction row. @default 'Reactions' */\n reactions?: string;\n /** Read after the author and time of a message that mentions you. @default 'mentions you' */\n mentionsYou?: string;\n /** Spoken name of the `@` role. @default 'op' */\n op?: string;\n /** Spoken name of the `+` role. @default 'voice' */\n voice?: string;\n}\n\n/** @beta Prop shape may change before release. */\nexport interface ChatLogProps\n extends Omit<FlatListProps<ChatLogMessage>, 'data' | 'renderItem' | 'keyExtractor'> {\n /** Oldest first, as the web's `Chat.Log` takes them. */\n messages: ChatLogMessage[];\n /** Your own nick: tinted in the log, and messages that mention it get the band. */\n me?: string;\n /** Called with the message and the emoji when you tap a reaction. Without it reactions are read-only. */\n onToggleReaction?: (messageId: string, emoji: string) => void;\n /** Opens a link or a preview the reader taps. @default Linking.openURL */\n onOpenLink?: (url: string) => void;\n /** Built-in text. Each entry falls back to English. */\n labels?: ChatLogLabels;\n}\n\nconst ROLE_GLYPH: Record<ChatRole, string> = { op: '@', voice: '+' };\n\nconst openUrl = (url: string) => {\n void Linking.openURL(url);\n};\n\n/**\n * ChatLog — the channel log for React Native: one message per row, the nick\n * and time on a line above the text, as the web's `Chat.Log` lays out a\n * narrow log. Links and mentions are read by `@eventuras/ratio-ui-core/chat`,\n * so the app and the web agree on them. Joins and topic changes are `event`\n * rows, `/me` is an `action`, and a `divider` marks a day or \"new messages\".\n *\n * A `FlatList` underneath, and like the web's log it does not scroll\n * itself: the ref is the list, so the caller decides when to follow new\n * messages. Other `FlatList` props pass through.\n *\n * @beta This component is experimental — prop shape may change before release.\n */\nexport const ChatLog = React.forwardRef<FlatList<ChatLogMessage>, ChatLogProps>(function ChatLog(\n { messages, me, onToggleReaction, onOpenLink = openUrl, labels, contentContainerStyle, ...listProps },\n ref,\n) {\n const theme = useTheme();\n const { reactions, mentionsYou, op, voice } = labels ?? {};\n const rowLabels = useMemo<Required<ChatLogLabels>>(\n () => ({\n reactions: reactions ?? 'Reactions',\n mentionsYou: mentionsYou ?? 'mentions you',\n op: op ?? 'op',\n voice: voice ?? 'voice',\n }),\n [reactions, mentionsYou, op, voice],\n );\n\n const renderItem = useCallback(\n ({ item }: { item: ChatLogMessage }) => (\n <ChatLogRow\n message={item}\n me={me}\n onToggleReaction={onToggleReaction}\n onOpenLink={onOpenLink}\n labels={rowLabels}\n />\n ),\n [me, onToggleReaction, onOpenLink, rowLabels],\n );\n\n return (\n <FlatList\n ref={ref}\n data={messages}\n keyExtractor={message => message.id}\n renderItem={renderItem}\n contentContainerStyle={[{ paddingVertical: theme.space.xs, gap: 2 }, contentContainerStyle]}\n style={{ backgroundColor: theme.colors.surface }}\n {...listProps}\n />\n );\n});\nChatLog.displayName = 'ChatLog';\n\ninterface ChatLogRowProps {\n message: ChatLogMessage;\n me?: string;\n onToggleReaction?: (messageId: string, emoji: string) => void;\n onOpenLink: (url: string) => void;\n labels: Required<ChatLogLabels>;\n}\n\nconst PX = 16;\n\nconst ChatLogRow: React.FC<ChatLogRowProps> = ({ message, me, onToggleReaction, onOpenLink, labels }) => {\n const theme = useTheme();\n const { colors } = theme;\n const { id, type = 'msg', time, nick, role, text, preview, reactions } = message;\n\n if (type === 'divider') {\n const rule = { flex: 1, height: 1, backgroundColor: colors.border2, opacity: 0.5 };\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingHorizontal: PX, marginVertical: 8 }}>\n <View style={rule} />\n <Text size=\"xs\" tone=\"subtle\" weight=\"semibold\" style={{ textTransform: 'uppercase', letterSpacing: 1.2 }}>\n {text}\n </Text>\n <View style={rule} />\n </View>\n );\n }\n\n // Events and actions are one line of narration, not someone speaking.\n if (type === 'event' || type === 'action') {\n return (\n <View style={{ flexDirection: 'row', gap: 8, paddingHorizontal: PX, paddingVertical: 2 }}>\n <Text size=\"xs\" family=\"mono\" tone=\"subtle\" style={{ lineHeight: theme.fontSize.sm * 1.5 }}>\n {time}\n </Text>\n <Text size=\"sm\" tone={type === 'event' ? 'subtle' : 'muted'} style={{ flex: 1, fontStyle: 'italic' }}>\n {type === 'event' ? (\n `— ${text}`\n ) : (\n <>\n {['*', nick].filter(Boolean).join(' ')}{' '}\n <ChatText segments={segmentChatText(text, { mentions: false })} onOpenLink={onOpenLink} />\n </>\n )}\n </Text>\n </View>\n );\n }\n\n const segments = segmentChatText(text);\n const mentionsMe = mentionsNick(segments, me);\n const isMe = sameNick(nick, me);\n const nickColor = isMe\n ? colors.chatNickVoice\n : role === 'op'\n ? colors.chatNickOp\n : role === 'voice'\n ? colors.chatNickVoice\n : colors.chatNick;\n // The glyph would be read as \"at\" or \"plus\", so the header speaks the role's name.\n const spoken = [\n role ? `${nick} (${labels[role]})` : nick,\n time,\n mentionsMe ? labels.mentionsYou : undefined,\n ]\n .filter(Boolean)\n .join(', ');\n\n return (\n <View\n style={{\n gap: 4,\n paddingVertical: 6,\n paddingRight: PX,\n // The band's 3px edge comes out of the padding, so every row aligns.\n paddingLeft: PX - 3,\n borderLeftWidth: 3,\n borderLeftColor: mentionsMe ? colors.accent : 'transparent',\n backgroundColor: mentionsMe ? colors.chatMentionBg : 'transparent',\n }}\n >\n <View accessible aria-label={spoken} style={{ flexDirection: 'row', alignItems: 'baseline', gap: 8 }}>\n <Text weight=\"semibold\" numberOfLines={1} style={{ color: nickColor, flexShrink: 1 }}>\n {role ? ROLE_GLYPH[role] : ''}\n {nick}\n </Text>\n <Text size=\"xs\" family=\"mono\" tone=\"subtle\">\n {time}\n </Text>\n </View>\n <Text>\n <ChatText segments={segments} onOpenLink={onOpenLink} />\n </Text>\n {preview && hasPreviewContent(preview) && <ChatLinkPreviewCard preview={preview} onOpenLink={onOpenLink} />}\n {reactions && reactions.length > 0 && (\n <ChatReactions\n reactions={reactions}\n onToggle={onToggleReaction ? emoji => onToggleReaction(id, emoji) : undefined}\n aria-label={labels.reactions}\n />\n )}\n </View>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { ChatReaction } from '@eventuras/ratio-ui-core/chat';
|
|
3
|
+
/** @beta Prop shape may change before release. */
|
|
4
|
+
export interface ChatReactionsProps {
|
|
5
|
+
reactions: ChatReaction[];
|
|
6
|
+
/** Called with the emoji whose reaction you add or remove. Without it the reactions are read-only. */
|
|
7
|
+
onToggle?: (emoji: string) => void;
|
|
8
|
+
/** Name of the row. @default 'Reactions' */
|
|
9
|
+
'aria-label'?: string;
|
|
10
|
+
/** Name of one reaction, for screen readers. @default (emoji, count) => `${emoji} ${count}` */
|
|
11
|
+
reactionLabel?: (reaction: ChatReaction) => string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The emoji tally under a message. Your own reactions are tinted; with
|
|
15
|
+
* `onToggle` each one is a toggle that adds or takes back yours. Controlled,
|
|
16
|
+
* like the web's: it reports the emoji and waits for new `reactions`.
|
|
17
|
+
*
|
|
18
|
+
* @beta This component is experimental — prop shape may change before release.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ChatReactions: React.FC<ChatReactionsProps>;
|
|
21
|
+
//# sourceMappingURL=ChatReactions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatReactions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatReactions.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,sGAAsG;IACtG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,MAAM,CAAC;CACpD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA6CtD,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useTheme } from "../RatioProvider.js";
|
|
2
|
+
import { Text as Text$1 } from "../Text.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { Pressable, View } from "react-native";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/chat/ChatReactions.tsx
|
|
7
|
+
/**
|
|
8
|
+
* The emoji tally under a message. Your own reactions are tinted; with
|
|
9
|
+
* `onToggle` each one is a toggle that adds or takes back yours. Controlled,
|
|
10
|
+
* like the web's: it reports the emoji and waits for new `reactions`.
|
|
11
|
+
*
|
|
12
|
+
* @beta This component is experimental — prop shape may change before release.
|
|
13
|
+
*/
|
|
14
|
+
var ChatReactions = ({ reactions, onToggle, "aria-label": ariaLabel = "Reactions", reactionLabel = ({ emoji, count }) => `${emoji} ${count}` }) => {
|
|
15
|
+
const theme = useTheme();
|
|
16
|
+
return /* @__PURE__ */ jsx(View, {
|
|
17
|
+
role: "group",
|
|
18
|
+
"aria-label": ariaLabel,
|
|
19
|
+
style: {
|
|
20
|
+
flexDirection: "row",
|
|
21
|
+
flexWrap: "wrap",
|
|
22
|
+
gap: 6
|
|
23
|
+
},
|
|
24
|
+
children: reactions.map((reaction) => {
|
|
25
|
+
const chip = {
|
|
26
|
+
flexDirection: "row",
|
|
27
|
+
alignItems: "center",
|
|
28
|
+
gap: 4,
|
|
29
|
+
paddingHorizontal: 8,
|
|
30
|
+
paddingVertical: 2,
|
|
31
|
+
borderWidth: 1,
|
|
32
|
+
borderRadius: theme.radius.pill,
|
|
33
|
+
borderColor: reaction.me ? theme.colors.chatReactionMeBorder : theme.colors.border1,
|
|
34
|
+
backgroundColor: reaction.me ? theme.colors.chatReactionMeBg : theme.colors.card
|
|
35
|
+
};
|
|
36
|
+
const label = /* @__PURE__ */ jsxs(Text$1, {
|
|
37
|
+
size: "xs",
|
|
38
|
+
weight: "semibold",
|
|
39
|
+
children: [
|
|
40
|
+
reaction.emoji,
|
|
41
|
+
" ",
|
|
42
|
+
reaction.count
|
|
43
|
+
]
|
|
44
|
+
});
|
|
45
|
+
return onToggle ? /* @__PURE__ */ jsx(Pressable, {
|
|
46
|
+
role: "button",
|
|
47
|
+
"aria-label": reactionLabel(reaction),
|
|
48
|
+
"aria-pressed": !!reaction.me,
|
|
49
|
+
onPress: () => onToggle(reaction.emoji),
|
|
50
|
+
style: ({ pressed }) => [chip, pressed && { opacity: .7 }],
|
|
51
|
+
children: label
|
|
52
|
+
}, reaction.emoji) : /* @__PURE__ */ jsx(View, {
|
|
53
|
+
"aria-label": reactionLabel(reaction),
|
|
54
|
+
style: chip,
|
|
55
|
+
children: label
|
|
56
|
+
}, reaction.emoji);
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { ChatReactions };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=ChatReactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatReactions.js","names":[],"sources":["../../src/chat/ChatReactions.tsx"],"sourcesContent":["// ratio-ui · design system for knowledge sharing\n// SPDX-FileCopyrightText: 2026 Losol AS\n// SPDX-License-Identifier: MPL-2.0\n\nimport React from 'react';\nimport { Pressable, View } from 'react-native';\nimport type { ChatReaction } from '@eventuras/ratio-ui-core/chat';\nimport { useTheme } from '../RatioProvider';\nimport { Text } from '../Text';\n\n/** @beta Prop shape may change before release. */\nexport interface ChatReactionsProps {\n reactions: ChatReaction[];\n /** Called with the emoji whose reaction you add or remove. Without it the reactions are read-only. */\n onToggle?: (emoji: string) => void;\n /** Name of the row. @default 'Reactions' */\n 'aria-label'?: string;\n /** Name of one reaction, for screen readers. @default (emoji, count) => `${emoji} ${count}` */\n reactionLabel?: (reaction: ChatReaction) => string;\n}\n\n/**\n * The emoji tally under a message. Your own reactions are tinted; with\n * `onToggle` each one is a toggle that adds or takes back yours. Controlled,\n * like the web's: it reports the emoji and waits for new `reactions`.\n *\n * @beta This component is experimental — prop shape may change before release.\n */\nexport const ChatReactions: React.FC<ChatReactionsProps> = ({\n reactions,\n onToggle,\n 'aria-label': ariaLabel = 'Reactions',\n reactionLabel = ({ emoji, count }) => `${emoji} ${count}`,\n}) => {\n const theme = useTheme();\n return (\n <View role=\"group\" aria-label={ariaLabel} style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6 }}>\n {reactions.map(reaction => {\n const chip = {\n flexDirection: 'row' as const,\n alignItems: 'center' as const,\n gap: 4,\n paddingHorizontal: 8,\n paddingVertical: 2,\n borderWidth: 1,\n borderRadius: theme.radius.pill,\n borderColor: reaction.me ? theme.colors.chatReactionMeBorder : theme.colors.border1,\n backgroundColor: reaction.me ? theme.colors.chatReactionMeBg : theme.colors.card,\n };\n const label = (\n <Text size=\"xs\" weight=\"semibold\">\n {reaction.emoji} {reaction.count}\n </Text>\n );\n return onToggle ? (\n <Pressable\n key={reaction.emoji}\n role=\"button\"\n aria-label={reactionLabel(reaction)}\n aria-pressed={!!reaction.me}\n onPress={() => onToggle(reaction.emoji)}\n style={({ pressed }) => [chip, pressed && { opacity: 0.7 }]}\n >\n {label}\n </Pressable>\n ) : (\n <View key={reaction.emoji} aria-label={reactionLabel(reaction)} style={chip}>\n {label}\n </View>\n );\n })}\n </View>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AA4BA,IAAa,iBAA+C,EAC1D,WACA,UACA,cAAc,YAAY,aAC1B,iBAAiB,EAAE,OAAO,YAAY,GAAG,MAAM,GAAG,cAC9C;CACJ,MAAM,QAAQ,SAAS;CACvB,OACE,oBAAC,MAAD;EAAM,MAAK;EAAQ,cAAY;EAAW,OAAO;GAAE,eAAe;GAAO,UAAU;GAAQ,KAAK;EAAE;EAC/F,UAAA,UAAU,KAAI,aAAY;GACzB,MAAM,OAAO;IACX,eAAe;IACf,YAAY;IACZ,KAAK;IACL,mBAAmB;IACnB,iBAAiB;IACjB,aAAa;IACb,cAAc,MAAM,OAAO;IAC3B,aAAa,SAAS,KAAK,MAAM,OAAO,uBAAuB,MAAM,OAAO;IAC5E,iBAAiB,SAAS,KAAK,MAAM,OAAO,mBAAmB,MAAM,OAAO;GAC9E;GACA,MAAM,QACJ,qBAAC,QAAD;IAAM,MAAK;IAAK,QAAO;IAAvB,UAAA;KACG,SAAS;KAAM;KAAE,SAAS;IACvB;;GAER,OAAO,WACL,oBAAC,WAAD;IAEE,MAAK;IACL,cAAY,cAAc,QAAQ;IAClC,gBAAc,CAAC,CAAC,SAAS;IACzB,eAAe,SAAS,SAAS,KAAK;IACtC,QAAQ,EAAE,cAAc,CAAC,MAAM,WAAW,EAAE,SAAS,GAAI,CAAC;IAEzD,UAAA;GACQ,GARJ,SAAS,KAQL,IAEX,oBAAC,MAAD;IAA2B,cAAY,cAAc,QAAQ;IAAG,OAAO;IACpE,UAAA;GACG,GAFK,SAAS,KAEd;EAEV,CAAC;CACG,CAAA;AAEV"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { ChatTextSegment } from '@eventuras/ratio-ui-core/chat';
|
|
3
|
+
interface ChatTextProps {
|
|
4
|
+
segments: ChatTextSegment[];
|
|
5
|
+
/** Opens a link the reader taps. */
|
|
6
|
+
onOpenLink: (url: string) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Message text with its mentions highlighted and its links tappable — the
|
|
10
|
+
* runs `segmentChatText` finds, so the app and the web agree on them.
|
|
11
|
+
* Nested inside a `Text`, so the runs wrap as one paragraph.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ChatText: React.FC<ChatTextProps>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=ChatText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatText.d.ts","sourceRoot":"","sources":["../../src/chat/ChatText.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGrE,UAAU,aAAa;IACrB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,oCAAoC;IACpC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAwB5C,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useTheme } from "../RatioProvider.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { Text } from "react-native";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/chat/ChatText.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Message text with its mentions highlighted and its links tappable — the
|
|
8
|
+
* runs `segmentChatText` finds, so the app and the web agree on them.
|
|
9
|
+
* Nested inside a `Text`, so the runs wrap as one paragraph.
|
|
10
|
+
*/
|
|
11
|
+
var ChatText = ({ segments, onOpenLink }) => {
|
|
12
|
+
const { colors } = useTheme();
|
|
13
|
+
return segments.map((segment, i) => {
|
|
14
|
+
if (segment.kind === "mention") return /* @__PURE__ */ jsx(Text, {
|
|
15
|
+
style: {
|
|
16
|
+
fontWeight: "600",
|
|
17
|
+
color: colors.primary
|
|
18
|
+
},
|
|
19
|
+
children: segment.value
|
|
20
|
+
}, i);
|
|
21
|
+
if (segment.kind === "link") return /* @__PURE__ */ jsx(Text, {
|
|
22
|
+
role: "link",
|
|
23
|
+
onPress: () => onOpenLink(segment.value),
|
|
24
|
+
style: {
|
|
25
|
+
color: colors.primary,
|
|
26
|
+
textDecorationLine: "underline"
|
|
27
|
+
},
|
|
28
|
+
children: segment.value
|
|
29
|
+
}, i);
|
|
30
|
+
return segment.value;
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ChatText };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=ChatText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatText.js","names":[],"sources":["../../src/chat/ChatText.tsx"],"sourcesContent":["// ratio-ui · design system for knowledge sharing\n// SPDX-FileCopyrightText: 2026 Losol AS\n// SPDX-License-Identifier: MPL-2.0\n\nimport React from 'react';\nimport { Text as RNText } from 'react-native';\nimport type { ChatTextSegment } from '@eventuras/ratio-ui-core/chat';\nimport { useTheme } from '../RatioProvider';\n\ninterface ChatTextProps {\n segments: ChatTextSegment[];\n /** Opens a link the reader taps. */\n onOpenLink: (url: string) => void;\n}\n\n/**\n * Message text with its mentions highlighted and its links tappable — the\n * runs `segmentChatText` finds, so the app and the web agree on them.\n * Nested inside a `Text`, so the runs wrap as one paragraph.\n */\nexport const ChatText: React.FC<ChatTextProps> = ({ segments, onOpenLink }) => {\n const { colors } = useTheme();\n return segments.map((segment, i) => {\n if (segment.kind === 'mention') {\n return (\n <RNText key={i} style={{ fontWeight: '600', color: colors.primary }}>\n {segment.value}\n </RNText>\n );\n }\n if (segment.kind === 'link') {\n return (\n <RNText\n key={i}\n role=\"link\"\n onPress={() => onOpenLink(segment.value)}\n style={{ color: colors.primary, textDecorationLine: 'underline' }}\n >\n {segment.value}\n </RNText>\n );\n }\n return segment.value;\n });\n};\n"],"mappings":";;;;;;;;;;AAoBA,IAAa,YAAqC,EAAE,UAAU,iBAAiB;CAC7E,MAAM,EAAE,WAAW,SAAS;CAC5B,OAAO,SAAS,KAAK,SAAS,MAAM;EAClC,IAAI,QAAQ,SAAS,WACnB,OACE,oBAAC,MAAD;GAAgB,OAAO;IAAE,YAAY;IAAO,OAAO,OAAO;GAAQ;GAC/D,UAAA,QAAQ;EACH,GAFK,CAEL;EAGZ,IAAI,QAAQ,SAAS,QACnB,OACE,oBAAC,MAAD;GAEE,MAAK;GACL,eAAe,WAAW,QAAQ,KAAK;GACvC,OAAO;IAAE,OAAO,OAAO;IAAS,oBAAoB;GAAY;GAE/D,UAAA,QAAQ;EACH,GAND,CAMC;EAGZ,OAAO,QAAQ;CACjB,CAAC;AACH"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { RatioProvider, useTheme, type ColorScheme, type RatioProviderProps, type RatioTheme, } from './RatioProvider';
|
|
2
|
+
export { Text, Heading, type TextProps, type TextTone, type HeadingProps, type HeadingLevel, } from './Text';
|
|
3
|
+
export { Button, type ButtonProps, type ButtonSize, type ButtonVariant } from './Button';
|
|
4
|
+
export { Card, type CardProps, type CardElevation } from './Card';
|
|
5
|
+
export { ChatLog, type ChatLogLabels, type ChatLogProps, } from './chat/ChatLog';
|
|
6
|
+
export { ChatReactions, type ChatReactionsProps } from './chat/ChatReactions';
|
|
7
|
+
export type { ChatLinkPreview, ChatLogMessage, ChatReaction, ChatRole, } from '@eventuras/ratio-ui-core/chat';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,aAAa,EACb,QAAQ,EACR,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,IAAI,EACJ,OAAO,EACP,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AACzF,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAClE,OAAO,EACL,OAAO,EACP,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE9E,YAAY,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,MAAM,+BAA+B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RatioProvider, useTheme } from "./RatioProvider.js";
|
|
2
|
+
import { Heading, Text } from "./Text.js";
|
|
3
|
+
import { Button } from "./Button.js";
|
|
4
|
+
import { Card } from "./Card.js";
|
|
5
|
+
import { ChatReactions } from "./chat/ChatReactions.js";
|
|
6
|
+
import { ChatLog } from "./chat/ChatLog.js";
|
|
7
|
+
export { Button, Card, ChatLog, ChatReactions, Heading, RatioProvider, Text, useTheme };
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventuras/ratio-ui-native",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ratio UI for React Native: the design system's components for Expo and React Native apps, drawn from @eventuras/ratio-ui-core's theme data.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react-native",
|
|
7
|
+
"expo",
|
|
8
|
+
"design-system",
|
|
9
|
+
"ratio-ui"
|
|
10
|
+
],
|
|
11
|
+
"license": "MPL-2.0",
|
|
12
|
+
"homepage": "https://github.com/losol/ratio-ui/tree/main/packages/ratio-ui-native#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/losol/ratio-ui/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/losol/ratio-ui.git",
|
|
19
|
+
"directory": "packages/ratio-ui-native"
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"module": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@eventuras/ratio-ui-core": "^0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@eventuras/eslint-config": "^1.0.4",
|
|
42
|
+
"@eventuras/typescript-config": "^1.0.0",
|
|
43
|
+
"@eventuras/vite-config": "^0.3.1",
|
|
44
|
+
"@types/react": "^19.3.0",
|
|
45
|
+
"react": "^19.2.3",
|
|
46
|
+
"react-native": "0.86.3",
|
|
47
|
+
"typescript": "6.0.3",
|
|
48
|
+
"vite": "8.3.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"react": ">=19.2 <20",
|
|
52
|
+
"react-native": ">=0.86"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=22.0.0 <25.0.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "pnpm run clean && vite build && node scripts/check-imports.mjs",
|
|
59
|
+
"check:imports": "node scripts/check-imports.mjs",
|
|
60
|
+
"clean": "rm -rf dist",
|
|
61
|
+
"lint": "eslint .",
|
|
62
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
63
|
+
}
|
|
6
64
|
}
|