@arkitektbedriftene/fe-lib 0.3.12 → 0.3.14

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 (74) hide show
  1. package/dist/{Badge-3f33be41.js → Badge-5eab3787.js} +4 -3
  2. package/dist/colors.d.ts +1 -1
  3. package/dist/hooks.d.ts +1 -1
  4. package/dist/icons.d.ts +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/oidc.d.ts +1 -1
  7. package/dist/{lib/rich-text → rich-text}/Editor.d.ts +1 -1
  8. package/dist/rich-text.d.ts +1 -1
  9. package/dist/rich-text.es.js +4 -4
  10. package/dist/{lib/ui → ui}/stitches.config.d.ts +1 -0
  11. package/dist/ui.css.d.ts +1 -1
  12. package/dist/ui.d.ts +1 -1
  13. package/dist/ui.es.js +2 -2
  14. package/package.json +16 -9
  15. package/.github/workflows/auto-publish.yaml +0 -20
  16. package/dist/lib/index.d.ts +0 -1
  17. package/index.html +0 -13
  18. package/src/App.css +0 -42
  19. package/src/App.tsx +0 -44
  20. package/src/assets/react.svg +0 -1
  21. package/src/index.css +0 -69
  22. package/src/lib/colors/colors.ts +0 -19
  23. package/src/lib/hooks/hooks.ts +0 -3
  24. package/src/lib/icons/README.md +0 -1
  25. package/src/lib/icons/icons.tsx +0 -51
  26. package/src/lib/index.ts +0 -1
  27. package/src/lib/oidc/README.md +0 -28
  28. package/src/lib/oidc/firmAccess.ts +0 -36
  29. package/src/lib/oidc/impersonate.tsx +0 -104
  30. package/src/lib/oidc/oidc.tsx +0 -248
  31. package/src/lib/rich-text/Editor.tsx +0 -137
  32. package/src/lib/rich-text/config.ts +0 -16
  33. package/src/lib/rich-text/editorContext.ts +0 -34
  34. package/src/lib/rich-text/rich-text.ts +0 -4
  35. package/src/lib/rich-text/state.ts +0 -62
  36. package/src/lib/rich-text/theme.ts +0 -41
  37. package/src/lib/rich-text/trim.ts +0 -79
  38. package/src/lib/ui/components/Alert.tsx +0 -63
  39. package/src/lib/ui/components/Badge.tsx +0 -42
  40. package/src/lib/ui/components/Box.tsx +0 -3
  41. package/src/lib/ui/components/Button.tsx +0 -195
  42. package/src/lib/ui/components/OverlayCard.tsx +0 -50
  43. package/src/lib/ui/components/Popover.tsx +0 -164
  44. package/src/lib/ui/components/Spinner.tsx +0 -102
  45. package/src/lib/ui/components/Tooltip.tsx +0 -34
  46. package/src/lib/ui/stitches.config.ts +0 -144
  47. package/src/lib/ui/ui.css +0 -11
  48. package/src/lib/ui/ui.ts +0 -10
  49. package/src/main.tsx +0 -10
  50. package/src/vite-env.d.ts +0 -1
  51. package/tsconfig.json +0 -21
  52. package/tsconfig.node.json +0 -9
  53. package/vite.config.ts +0 -45
  54. /package/dist/{lib/colors → colors}/colors.d.ts +0 -0
  55. /package/dist/{lib/hooks → hooks}/hooks.d.ts +0 -0
  56. /package/dist/{lib/icons → icons}/icons.d.ts +0 -0
  57. /package/dist/{lib/oidc → oidc}/firmAccess.d.ts +0 -0
  58. /package/dist/{lib/oidc → oidc}/impersonate.d.ts +0 -0
  59. /package/dist/{lib/oidc → oidc}/oidc.d.ts +0 -0
  60. /package/dist/{lib/rich-text → rich-text}/config.d.ts +0 -0
  61. /package/dist/{lib/rich-text → rich-text}/editorContext.d.ts +0 -0
  62. /package/dist/{lib/rich-text → rich-text}/rich-text.d.ts +0 -0
  63. /package/dist/{lib/rich-text → rich-text}/state.d.ts +0 -0
  64. /package/dist/{lib/rich-text → rich-text}/theme.d.ts +0 -0
  65. /package/dist/{lib/rich-text → rich-text}/trim.d.ts +0 -0
  66. /package/dist/{lib/ui → ui}/components/Alert.d.ts +0 -0
  67. /package/dist/{lib/ui → ui}/components/Badge.d.ts +0 -0
  68. /package/dist/{lib/ui → ui}/components/Box.d.ts +0 -0
  69. /package/dist/{lib/ui → ui}/components/Button.d.ts +0 -0
  70. /package/dist/{lib/ui → ui}/components/OverlayCard.d.ts +0 -0
  71. /package/dist/{lib/ui → ui}/components/Popover.d.ts +0 -0
  72. /package/dist/{lib/ui → ui}/components/Spinner.d.ts +0 -0
  73. /package/dist/{lib/ui → ui}/components/Tooltip.d.ts +0 -0
  74. /package/dist/{lib/ui → ui}/ui.d.ts +0 -0
@@ -1,248 +0,0 @@
1
- import { SigninRedirectArgs, User, UserManager } from "oidc-client-ts";
2
- import {
3
- Context,
4
- createContext,
5
- ReactNode,
6
- useCallback,
7
- useContext,
8
- useEffect,
9
- useMemo,
10
- useRef,
11
- useState,
12
- } from "react";
13
-
14
- export * from "oidc-client-ts";
15
- export * from "./impersonate";
16
- export * from "./firmAccess"
17
-
18
- export type AuthState = {
19
- user: User | null;
20
- isLoading: boolean;
21
- isAuthenticated: boolean;
22
- isError: boolean;
23
- error: Error | null;
24
- };
25
-
26
- export type AuthContextData = {
27
- state: AuthState;
28
- handleSigninCallback: () => Promise<User | undefined>;
29
- redirectToSignin: UserManager["signinRedirect"];
30
- };
31
-
32
- export type AuthProviderConfig = {
33
- onSigninComplete: (user: User | null) => void;
34
- };
35
-
36
- const AuthProviderCore = ({
37
- userManager,
38
- context,
39
- children,
40
- }: {
41
- userManager: UserManager;
42
- context: Context<AuthContextData | null>;
43
- children: ReactNode;
44
- }) => {
45
- const [state, setState] = useState<AuthState>({
46
- user: null,
47
- isLoading: true,
48
- isAuthenticated: false,
49
- isError: false,
50
- error: null,
51
- });
52
-
53
- // Initialize on first render
54
- const isInitialized = useRef(false);
55
- useEffect(() => {
56
- if (isInitialized.current) {
57
- return;
58
- }
59
- isInitialized.current = true;
60
-
61
- void (async () => {
62
- try {
63
- const user = await userManager.getUser();
64
- setState({
65
- user,
66
- isLoading: false,
67
- isAuthenticated: user ? !user.expired : false,
68
- isError: false,
69
- error: null,
70
- });
71
- } catch (error) {
72
- setState({
73
- user: null,
74
- isLoading: false,
75
- isAuthenticated: false,
76
- isError: true,
77
- error:
78
- error instanceof Error
79
- ? error
80
- : new Error("Unknown error during auth"),
81
- });
82
- }
83
- })();
84
- }, [userManager]);
85
-
86
- // Set up UserManager events
87
- useEffect(() => {
88
- const onUserLoaded = (user: User) => {
89
- setState({
90
- user,
91
- isLoading: false,
92
- isAuthenticated: !user.expired,
93
- isError: false,
94
- error: null,
95
- });
96
- };
97
- userManager.events.addUserLoaded(onUserLoaded);
98
-
99
- const onUserUnloaded = () => {
100
- setState({
101
- ...state,
102
- user: null,
103
- isAuthenticated: false,
104
- });
105
- };
106
- userManager.events.addUserUnloaded(onUserUnloaded);
107
-
108
- const onSilentRenewError = (error: Error) => {
109
- setState({
110
- ...state,
111
- isLoading: false,
112
- isError: true,
113
- error,
114
- });
115
- };
116
- userManager.events.addSilentRenewError(onSilentRenewError);
117
-
118
- return () => {
119
- userManager.events.removeUserLoaded(onUserLoaded);
120
- userManager.events.removeUserUnloaded(onUserUnloaded);
121
- userManager.events.removeSilentRenewError(onSilentRenewError);
122
- };
123
- }, [userManager]);
124
-
125
- const handleSigninCallback = useCallback(async () => {
126
- const user = await userManager.signinCallback();
127
-
128
- setState({
129
- user: user ?? null,
130
- isLoading: false,
131
- isAuthenticated: user ? !user.expired : false,
132
- isError: false,
133
- error: null,
134
- });
135
-
136
- return user ?? undefined;
137
- }, [userManager]);
138
-
139
- const redirectToSignin = useCallback(
140
- async (args?: SigninRedirectArgs | undefined) => {
141
- try {
142
- await userManager.signinRedirect(args);
143
- } catch (error) {
144
- console.error(error);
145
- }
146
- },
147
- [userManager]
148
- );
149
-
150
- const contextValue = useMemo(
151
- () => ({
152
- state,
153
- handleSigninCallback,
154
- redirectToSignin,
155
- }),
156
- [state, handleSigninCallback, redirectToSignin]
157
- );
158
-
159
- return <context.Provider value={contextValue}>{children}</context.Provider>;
160
- };
161
-
162
- const useAuthContextCore = (context: Context<AuthContextData | null>) => {
163
- const contextData = useContext(context);
164
- if (!contextData) {
165
- throw new Error("useAuthContext must be used within an AuthProvider");
166
- }
167
- return contextData;
168
- };
169
-
170
- const useAuthStateCore = (context: Context<AuthContextData | null>) => {
171
- const { state } = useAuthContextCore(context);
172
- return state;
173
- };
174
-
175
- const useSigninCallbackCore = (
176
- context: Context<AuthContextData | null>,
177
- onDone?: (user?: User) => void
178
- ) => {
179
- const { state, handleSigninCallback } = useAuthContextCore(context);
180
-
181
- const isInitialized = useRef(false);
182
- useEffect(() => {
183
- // Only run once
184
- if (isInitialized.current) {
185
- return;
186
- }
187
- isInitialized.current = true;
188
- handleSigninCallback()
189
- // Wait for state to update
190
- // Otherwise any navigation that happens in onDone will
191
- // happen before the state update and the user will be
192
- // redirected to the signin page again.
193
- .then(
194
- (u) =>
195
- new Promise<User | undefined>((resolve) =>
196
- setTimeout(() => resolve(u), 0)
197
- )
198
- )
199
- .then((u) => onDone?.(u));
200
- }, [handleSigninCallback]);
201
-
202
- return state;
203
- };
204
-
205
- export const createAuthContext = (userManager: UserManager) => {
206
- const AuthContext = createContext<AuthContextData | null>(null);
207
-
208
- const AuthProvider = ({ children }: { children: ReactNode }) => {
209
- return (
210
- <AuthProviderCore userManager={userManager} context={AuthContext}>
211
- {children}
212
- </AuthProviderCore>
213
- );
214
- };
215
-
216
- const useAuthContext = () => useAuthContextCore(AuthContext);
217
- const useAuthState = () => useAuthStateCore(AuthContext);
218
-
219
- /**
220
- * Hook to handle the signin callback. Use this on the signin callback page.
221
- * @param onDone Optional callback to run after signin is complete. Useful to redirect to a different page.
222
- */
223
- const useSigninCallback = (onDone?: (user?: User) => void) =>
224
- useSigninCallbackCore(AuthContext, onDone);
225
-
226
- /**
227
- * Async function to get the access token for the current user
228
- * outside of a React component.
229
- *
230
- * Useful for prefetching data in React Router or similar.
231
- */
232
- const getAccessToken = async () => {
233
- const user = await userManager.getUser();
234
- if (!user) {
235
- return null;
236
- }
237
- return user.access_token;
238
- };
239
-
240
- return {
241
- AuthContext,
242
- AuthProvider,
243
- useAuthContext,
244
- useAuthState,
245
- useSigninCallback,
246
- getAccessToken,
247
- };
248
- };
@@ -1,137 +0,0 @@
1
- import { useRef, type ReactNode, useMemo, type ReactElement } from "react";
2
- import { Box, Spinner, styled } from "../ui/ui";
3
- import { type InitialConfigType, LexicalComposer } from "@lexical/react/LexicalComposer";
4
- import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
5
- import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
6
- import { lexicalTheme } from "./theme";
7
- import { useHasFocusWithin, richTextContext } from "./editorContext";
8
-
9
- const Container = styled("div", {
10
- border: "1px solid $borderDarker",
11
- borderRadius: "$md",
12
- position: "relative",
13
- backgroundColor: "white",
14
-
15
- "&:hover": {
16
- borderColor: "$gray200",
17
- },
18
-
19
- variants: {
20
- hasFocus: {
21
- true: {
22
- boxShadow: "$md",
23
- borderColor: "$gray200",
24
- },
25
- },
26
- hideBorder: {
27
- true: {
28
- border: "none",
29
- },
30
- },
31
- },
32
-
33
- compoundVariants: [
34
- {
35
- hasFocus: true,
36
- hideBorder: true,
37
- css: {
38
- boxShadow: "none",
39
- },
40
- },
41
- ],
42
- });
43
-
44
- const EditorSpinner = ({
45
- isLoading,
46
- }: {
47
- isLoading: boolean;
48
- }) => {
49
- return (
50
- <Box
51
- css={{
52
- visibility: isLoading ? "visible" : "hidden",
53
- position: "absolute",
54
- bottom: "1rem",
55
- right: "1rem",
56
- display: "flex",
57
- }}
58
- >
59
- <Spinner />
60
- </Box>
61
- );
62
- };
63
-
64
- export const RichTextEditor = ({
65
- isLoading,
66
- children,
67
- placeholderText,
68
- nodes,
69
- plugins,
70
- toolbar,
71
- content,
72
- hideBorder,
73
- onBlur,
74
- }: {
75
- isLoading: boolean;
76
- children: ReactNode;
77
- placeholderText?: string;
78
- nodes: InitialConfigType["nodes"];
79
- plugins?: ReactNode;
80
- toolbar?: ReactNode;
81
- content: ReactElement;
82
- hideBorder?: boolean;
83
- onBlur?: () => void;
84
- }) => {
85
- const { hasFocus, attributes } = useHasFocusWithin({ onBlur });
86
- const editorRef = useRef<HTMLDivElement>(null);
87
- const contextValue = useMemo(() => ({ hasFocus, editorRef }), [hasFocus]);
88
-
89
- return (
90
- <richTextContext.Provider value={contextValue}>
91
- <LexicalComposer
92
- initialConfig={{
93
- namespace: "CommentEditor",
94
- onError: (error) => {
95
- console.error(error);
96
- },
97
- theme: lexicalTheme,
98
- nodes,
99
- editable: true,
100
- }}
101
- >
102
- <Container
103
- ref={editorRef}
104
- hasFocus={hasFocus}
105
- hideBorder={hideBorder}
106
- {...attributes}
107
- >
108
- <RichTextPlugin
109
- contentEditable={content}
110
- placeholder={
111
- placeholderText ? (
112
- <Box
113
- css={{
114
- position: "absolute",
115
- top: "$4",
116
- left: "$4",
117
- color: "$gray500",
118
- pointerEvents: "none",
119
- fontSize: "$sm",
120
- }}
121
- >
122
- {placeholderText}
123
- </Box>
124
- ) : null
125
- }
126
- ErrorBoundary={LexicalErrorBoundary}
127
- />
128
- {plugins}
129
- <EditorSpinner isLoading={isLoading} />
130
- {toolbar}
131
- </Container>
132
- {/* biome-ignore lint/complexity/noUselessFragments: <explanation> */}
133
- <>{children}</>
134
- </LexicalComposer>
135
- </richTextContext.Provider>
136
- );
137
- };
@@ -1,16 +0,0 @@
1
- import { AutoLinkNode, LinkNode } from "@lexical/link";
2
- import { ListItemNode, ListNode } from "@lexical/list";
3
- import { HeadingNode, QuoteNode } from "@lexical/rich-text";
4
- import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
5
-
6
- export const defaultNodes = [
7
- HeadingNode,
8
- QuoteNode,
9
- ListNode,
10
- ListItemNode,
11
- AutoLinkNode,
12
- LinkNode,
13
- TableNode,
14
- TableRowNode,
15
- TableCellNode,
16
- ];
@@ -1,34 +0,0 @@
1
- import { type RefObject, createContext, useCallback, useContext, useRef, useState } from "react";
2
-
3
- export const richTextContext = createContext({ hasFocus: false, editorRef: { current: null } } as { hasFocus: boolean, editorRef: RefObject<HTMLElement | null> });
4
-
5
- export const useHasFocusWithin = ({ onBlur }: { onBlur?: () => void }) => {
6
- const [hasFocus, setHasFocus] = useState(false);
7
- const blurTimeout = useRef<number | null>(null);
8
-
9
- const handleFocus = useCallback(() => {
10
- setHasFocus(true);
11
- if (blurTimeout.current) {
12
- window.clearTimeout(blurTimeout.current);
13
- }
14
- }, []);
15
-
16
- const handleBlur = useCallback(() => {
17
- blurTimeout.current = window.setTimeout(() => {
18
- setHasFocus(false);
19
- onBlur?.();
20
- }, 0);
21
- }, [onBlur]);
22
-
23
- return {
24
- hasFocus,
25
- attributes: {
26
- onFocus: handleFocus,
27
- onBlur: handleBlur,
28
- },
29
- };
30
- };
31
-
32
- export const useRichTextContext = () => {
33
- return useContext(richTextContext);
34
- }
@@ -1,4 +0,0 @@
1
- export * from './state'
2
- export * from './config'
3
- export * from './Editor'
4
- export * from './editorContext'
@@ -1,62 +0,0 @@
1
- import { createHeadlessEditor } from "@lexical/headless";
2
- import { $generateHtmlFromNodes } from "@lexical/html";
3
- import { $createParagraphNode, $createTextNode, $getRoot, type CreateEditorArgs, type SerializedEditorState } from "lexical";
4
- import { trimState } from "./trim";
5
-
6
- export const isJSON = (str: string) => {
7
- return str[0] === "{";
8
- };
9
-
10
- export const stateToHTML = (
11
- state: SerializedEditorState | string | null | undefined,
12
- nodes: CreateEditorArgs["nodes"],
13
- options?: {
14
- maxLines?: number | null;
15
- onlyMainContent?: boolean;
16
- },
17
- ): {
18
- html: string;
19
- trimCount: number;
20
- } => {
21
- let trimCount = 0;
22
-
23
- const headlessEditor = createHeadlessEditor({
24
- nodes,
25
- editable: false,
26
- });
27
-
28
- if (state) {
29
- try {
30
- if (typeof state === "string" && !isJSON(state)) {
31
- // We have a string, but it's not JSON. We will assume it is plain text
32
- // and create a paragraph node with the text.
33
- headlessEditor.update(() => {
34
- const root = $getRoot();
35
- const p = $createParagraphNode();
36
- p.append($createTextNode(state.trim()));
37
- root.append(p);
38
- });
39
- } else {
40
- const parsed = headlessEditor.parseEditorState(state, () => {
41
- trimCount = trimState({
42
- editor: headlessEditor,
43
- maxLines: options?.maxLines,
44
- });
45
- });
46
- if (!parsed.isEmpty()) {
47
- headlessEditor.setEditorState(parsed);
48
- }
49
- }
50
- } catch (error) {
51
- console.error(error);
52
- }
53
- }
54
-
55
- let html = "";
56
-
57
- headlessEditor.update(() => {
58
- html = $generateHtmlFromNodes(headlessEditor);
59
- });
60
-
61
- return { html, trimCount };
62
- };
@@ -1,41 +0,0 @@
1
- import type { EditorThemeClasses } from "lexical";
2
- import { css } from "../ui/ui";
3
-
4
- const boldCss = css({
5
- fontWeight: "bold",
6
- });
7
-
8
- const italicCss = css({
9
- fontStyle: "italic",
10
- });
11
-
12
- const underlineCss = css({
13
- textDecoration: "underline",
14
- });
15
-
16
- const nestedListItem = css({
17
- listStyleType: "none",
18
- });
19
-
20
- // TODO: Improve
21
- const quoteCss = css({
22
- borderLeft: "4px solid #ccc",
23
- color: "#666",
24
- fontStyle: "italic",
25
- margin: "1.5em 10px",
26
- padding: "0.5em 10px",
27
- });
28
-
29
- export const lexicalTheme: EditorThemeClasses = {
30
- quote: quoteCss().className,
31
- text: {
32
- bold: boldCss().className,
33
- italic: italicCss().className,
34
- underline: underlineCss().className,
35
- },
36
- list: {
37
- nested: {
38
- listitem: nestedListItem().className,
39
- },
40
- },
41
- };
@@ -1,79 +0,0 @@
1
- import { $getRoot, type LexicalEditor, ParagraphNode } from "lexical";
2
- import { trimTextContentFromAnchor } from "@lexical/selection";
3
-
4
- const findTrimPoint = ({
5
- text,
6
- maxChars,
7
- maxLines,
8
- }: {
9
- text: string;
10
- maxChars?: number | null;
11
- maxLines?: number | null;
12
- }) => {
13
- if (text.length === 0) {
14
- return 0;
15
- }
16
-
17
- const hasMaxLines = typeof maxLines === "number";
18
- const hasMaxChars = typeof maxChars === "number";
19
-
20
- if (!hasMaxLines && !hasMaxChars) {
21
- return text.length;
22
- }
23
-
24
- let lineIndex = 0;
25
- let charIndex = 0;
26
- while (
27
- charIndex < text.length &&
28
- (!hasMaxLines || lineIndex < maxLines) &&
29
- (!hasMaxChars || charIndex < maxChars)
30
- ) {
31
- const char = text[charIndex];
32
-
33
- if (char === "\n") {
34
- lineIndex++;
35
- }
36
- charIndex++;
37
- }
38
-
39
- const newText = text.slice(0, charIndex);
40
-
41
- return newText.length;
42
- };
43
-
44
- export const trimState = ({
45
- editor,
46
- maxChars,
47
- maxLines,
48
- }: {
49
- editor: LexicalEditor;
50
- maxChars?: number | null;
51
- maxLines?: number | null;
52
- }) => {
53
- const rootNode = $getRoot();
54
- const text = rootNode.getTextContent();
55
-
56
- const i = findTrimPoint({ text, maxChars, maxLines });
57
- const trimCount = text.length - i;
58
-
59
- const selection = rootNode.select();
60
- const anchor = selection.anchor;
61
- trimTextContentFromAnchor(editor, anchor, trimCount);
62
-
63
- // Remove the last paragraph if it's empty.
64
- const lastChild = rootNode.getLastChild();
65
- if (lastChild instanceof ParagraphNode && lastChild.getChildrenSize() === 0) {
66
- lastChild.remove();
67
- }
68
-
69
- // if (trimCount > 0) {
70
- // // Append an ellipsis to the last text node is we can find it.
71
- // const lastNonRootNode = rootNode.getLastDescendant();
72
-
73
- // if (lastNonRootNode instanceof TextNode) {
74
- // lastNonRootNode.getParent()?.append($createTextNode("... Les mer"));
75
- // }
76
- // }
77
-
78
- return trimCount;
79
- };
@@ -1,63 +0,0 @@
1
- import { styled } from "../stitches.config";
2
- import { forwardRef } from "react";
3
-
4
- const AlertStyled = styled("div", {
5
- padding: "$3",
6
- borderRadius: "$md",
7
-
8
- variants: {
9
- color: {
10
- warning: {
11
- background: "$yellow200",
12
- border: "1px solid $yellow400",
13
- color: "$yellow900",
14
- },
15
- danger: {
16
- background: "$red200",
17
- border: "1px solid $red400",
18
- color: "$red900",
19
- },
20
- info: {
21
- background: "$blue200",
22
- border: "1px solid $blue400",
23
- color: "$blue900",
24
- },
25
- success: {
26
- background: "$green200",
27
- border: "1px solid $green400",
28
- color: "$green900",
29
- },
30
- },
31
- size: {
32
- sm: {
33
- fontSize: "$sm",
34
- },
35
- md: {
36
- fontSize: "$md",
37
- },
38
- },
39
- },
40
-
41
- defaultVariants: {
42
- color: "info",
43
- size: "md",
44
- },
45
- });
46
-
47
- type AlertProps = React.ComponentProps<typeof AlertStyled> & {
48
- as?: React.ElementType;
49
- }
50
-
51
- export const Alert = forwardRef<HTMLDivElement, AlertProps>(
52
- ({ children, ...props }, ref) => {
53
- return (
54
- <AlertStyled
55
- ref={ref}
56
- role="alert"
57
- {...props}
58
- >
59
- {children}
60
- </AlertStyled>
61
- );
62
- },
63
- );