@a11ypros/a11y-ui-components 1.0.0 → 1.0.1
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/.storybook/custom.css +69 -0
- package/.storybook/main.ts +46 -0
- package/.storybook/manager.ts +26 -0
- package/.storybook/package.json +6 -0
- package/.storybook/preview.tsx +31 -0
- package/.storybook/public/logo.png +0 -0
- package/.storybook/vite.config.ts +24 -0
- package/.storybook/welcome.mdx +97 -0
- package/DEPLOYMENT.md +154 -0
- package/README.md +227 -0
- package/apps/web/app/(docs)/audit/audit.css +269 -0
- package/apps/web/app/(docs)/audit/page.tsx +271 -0
- package/apps/web/app/(docs)/components/button/page.tsx +49 -0
- package/apps/web/app/(docs)/components/form/page.tsx +92 -0
- package/apps/web/app/(docs)/components/link/page.tsx +31 -0
- package/apps/web/app/(docs)/components/modal/page.tsx +41 -0
- package/apps/web/app/(docs)/components/page.tsx +37 -0
- package/apps/web/app/(docs)/components/table/page.tsx +54 -0
- package/apps/web/app/(docs)/components/tabs/page.tsx +61 -0
- package/apps/web/app/(docs)/components/toast/page.tsx +51 -0
- package/apps/web/app/api/audit/route.ts +128 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +20 -0
- package/apps/web/app/page.tsx +17 -0
- package/apps/web/app/styles/globals.css +5 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +21 -0
- package/apps/web/package.json +28 -0
- package/apps/web/public/_headers +17 -0
- package/apps/web/public/_redirects +31 -0
- package/apps/web/public/logo.png +0 -0
- package/apps/web/tsconfig.json +29 -0
- package/netlify/functions/audit.ts +163 -0
- package/netlify.toml +37 -0
- package/package.json +30 -58
- package/packages/design-system/README.md +252 -0
- package/packages/design-system/package.json +68 -0
- package/packages/design-system/scripts/copy-css.js +63 -0
- package/packages/design-system/src/components/Button/Button.stories.tsx +228 -0
- package/packages/design-system/src/components/Button/Button.tsx +137 -0
- package/packages/design-system/src/components/Button/index.ts +3 -0
- package/packages/design-system/src/components/DataTable/DataTable.stories.tsx +211 -0
- package/packages/design-system/src/components/DataTable/DataTable.tsx +293 -0
- package/packages/design-system/src/components/DataTable/index.ts +3 -0
- package/packages/design-system/src/components/Form/Checkbox.stories.tsx +252 -0
- package/packages/design-system/src/components/Form/Checkbox.tsx +114 -0
- package/packages/design-system/src/components/Form/Fieldset.stories.tsx +210 -0
- package/packages/design-system/src/components/Form/Fieldset.tsx +71 -0
- package/packages/design-system/src/components/Form/Input.stories.tsx +164 -0
- package/packages/design-system/src/components/Form/Input.tsx +113 -0
- package/packages/design-system/src/components/Form/Label.tsx +56 -0
- package/packages/design-system/src/components/Form/Radio.stories.tsx +265 -0
- package/packages/design-system/src/components/Form/Radio.tsx +147 -0
- package/packages/design-system/src/components/Form/Select.stories.tsx +295 -0
- package/packages/design-system/src/components/Form/Select.tsx +160 -0
- package/packages/design-system/src/components/Form/Textarea.stories.tsx +253 -0
- package/packages/design-system/src/components/Form/Textarea.tsx +145 -0
- package/packages/design-system/src/components/Form/index.ts +8 -0
- package/packages/design-system/src/components/Link/Link.stories.tsx +128 -0
- package/packages/design-system/src/components/Link/Link.tsx +117 -0
- package/packages/design-system/src/components/Link/index.ts +3 -0
- package/packages/design-system/src/components/Modal/Modal.stories.tsx +165 -0
- package/packages/design-system/src/components/Modal/Modal.tsx +202 -0
- package/packages/design-system/src/components/Modal/index.ts +3 -0
- package/packages/design-system/src/components/Tabs/Tabs.stories.tsx +213 -0
- package/packages/design-system/src/components/Tabs/Tabs.tsx +248 -0
- package/packages/design-system/src/components/Tabs/index.ts +3 -0
- package/packages/design-system/src/components/Toast/Toast.stories.tsx +153 -0
- package/packages/design-system/src/components/Toast/Toast.tsx +175 -0
- package/packages/design-system/src/components/Toast/ToastProvider.tsx +73 -0
- package/packages/design-system/src/components/Toast/index.ts +5 -0
- package/packages/design-system/src/hooks/useAriaLive.ts +51 -0
- package/packages/design-system/src/hooks/useFocusReturn.ts +40 -0
- package/packages/design-system/src/hooks/useFocusTrap.ts +82 -0
- package/{dist/index.js → packages/design-system/src/index.ts} +4 -0
- package/packages/design-system/src/styles/index.ts +3 -0
- package/packages/design-system/src/tokens/breakpoints.ts +28 -0
- package/packages/design-system/src/tokens/colors.ts +98 -0
- package/packages/design-system/src/tokens/index.ts +6 -0
- package/packages/design-system/src/tokens/motion.ts +41 -0
- package/packages/design-system/src/tokens/spacing.ts +24 -0
- package/packages/design-system/src/tokens/theme.ts +19 -0
- package/packages/design-system/src/tokens/typography.ts +64 -0
- package/packages/design-system/src/utils/aria.ts +108 -0
- package/packages/design-system/src/utils/focus.ts +87 -0
- package/packages/design-system/src/utils/index.ts +4 -0
- package/packages/design-system/src/utils/keyboard.ts +77 -0
- package/packages/design-system/tsconfig.json +17 -0
- package/public/logo.png +0 -0
- package/scripts/fix-storybook-paths.js +53 -0
- package/tsconfig.json +20 -0
- package/dist/components/Button/Button.d.ts +0 -37
- package/dist/components/Button/Button.d.ts.map +0 -1
- package/dist/components/Button/Button.js +0 -52
- package/dist/components/Button/index.d.ts +0 -3
- package/dist/components/Button/index.d.ts.map +0 -1
- package/dist/components/Button/index.js +0 -1
- package/dist/components/DataTable/DataTable.d.ts +0 -71
- package/dist/components/DataTable/DataTable.d.ts.map +0 -1
- package/dist/components/DataTable/DataTable.js +0 -122
- package/dist/components/DataTable/index.d.ts +0 -3
- package/dist/components/DataTable/index.d.ts.map +0 -1
- package/dist/components/DataTable/index.js +0 -1
- package/dist/components/Form/Checkbox.d.ts +0 -36
- package/dist/components/Form/Checkbox.d.ts.map +0 -1
- package/dist/components/Form/Checkbox.js +0 -39
- package/dist/components/Form/Fieldset.d.ts +0 -33
- package/dist/components/Form/Fieldset.d.ts.map +0 -1
- package/dist/components/Form/Fieldset.js +0 -34
- package/dist/components/Form/Input.d.ts +0 -37
- package/dist/components/Form/Input.d.ts.map +0 -1
- package/dist/components/Form/Input.js +0 -41
- package/dist/components/Form/Label.d.ts +0 -30
- package/dist/components/Form/Label.d.ts.map +0 -1
- package/dist/components/Form/Label.js +0 -30
- package/dist/components/Form/Radio.d.ts +0 -53
- package/dist/components/Form/Radio.d.ts.map +0 -1
- package/dist/components/Form/Radio.js +0 -39
- package/dist/components/Form/Select.d.ts +0 -51
- package/dist/components/Form/Select.d.ts.map +0 -1
- package/dist/components/Form/Select.js +0 -49
- package/dist/components/Form/Textarea.d.ts +0 -44
- package/dist/components/Form/Textarea.d.ts.map +0 -1
- package/dist/components/Form/Textarea.js +0 -43
- package/dist/components/Form/index.d.ts +0 -8
- package/dist/components/Form/index.d.ts.map +0 -1
- package/dist/components/Form/index.js +0 -7
- package/dist/components/Link/Link.d.ts +0 -34
- package/dist/components/Link/Link.d.ts.map +0 -1
- package/dist/components/Link/Link.js +0 -48
- package/dist/components/Link/index.d.ts +0 -3
- package/dist/components/Link/index.d.ts.map +0 -1
- package/dist/components/Link/index.js +0 -1
- package/dist/components/Modal/Modal.d.ts +0 -64
- package/dist/components/Modal/Modal.d.ts.map +0 -1
- package/dist/components/Modal/Modal.js +0 -108
- package/dist/components/Modal/index.d.ts +0 -3
- package/dist/components/Modal/index.d.ts.map +0 -1
- package/dist/components/Modal/index.js +0 -1
- package/dist/components/Tabs/Tabs.d.ts +0 -63
- package/dist/components/Tabs/Tabs.d.ts.map +0 -1
- package/dist/components/Tabs/Tabs.js +0 -134
- package/dist/components/Tabs/index.d.ts +0 -3
- package/dist/components/Tabs/index.d.ts.map +0 -1
- package/dist/components/Tabs/index.js +0 -1
- package/dist/components/Toast/Toast.d.ts +0 -59
- package/dist/components/Toast/Toast.d.ts.map +0 -1
- package/dist/components/Toast/Toast.js +0 -91
- package/dist/components/Toast/ToastProvider.d.ts +0 -22
- package/dist/components/Toast/ToastProvider.d.ts.map +0 -1
- package/dist/components/Toast/ToastProvider.js +0 -33
- package/dist/components/Toast/index.d.ts +0 -5
- package/dist/components/Toast/index.d.ts.map +0 -1
- package/dist/components/Toast/index.js +0 -2
- package/dist/hooks/useAriaLive.d.ts +0 -9
- package/dist/hooks/useAriaLive.d.ts.map +0 -1
- package/dist/hooks/useAriaLive.js +0 -39
- package/dist/hooks/useFocusReturn.d.ts +0 -9
- package/dist/hooks/useFocusReturn.d.ts.map +0 -1
- package/dist/hooks/useFocusReturn.js +0 -33
- package/dist/hooks/useFocusTrap.d.ts +0 -9
- package/dist/hooks/useFocusTrap.d.ts.map +0 -1
- package/dist/hooks/useFocusTrap.js +0 -68
- package/dist/index.d.ts +0 -22
- package/dist/index.d.ts.map +0 -1
- package/dist/styles/index.d.ts +0 -3
- package/dist/styles/index.d.ts.map +0 -1
- package/dist/styles/index.js +0 -1
- package/dist/tokens/breakpoints.d.ts +0 -25
- package/dist/tokens/breakpoints.d.ts.map +0 -1
- package/dist/tokens/breakpoints.js +0 -23
- package/dist/tokens/colors.d.ts +0 -81
- package/dist/tokens/colors.d.ts.map +0 -1
- package/dist/tokens/colors.js +0 -86
- package/dist/tokens/index.d.ts +0 -6
- package/dist/tokens/index.d.ts.map +0 -1
- package/dist/tokens/index.js +0 -5
- package/dist/tokens/motion.d.ts +0 -30
- package/dist/tokens/motion.d.ts.map +0 -1
- package/dist/tokens/motion.js +0 -34
- package/dist/tokens/spacing.d.ts +0 -22
- package/dist/tokens/spacing.d.ts.map +0 -1
- package/dist/tokens/spacing.js +0 -20
- package/dist/tokens/theme.d.ts +0 -159
- package/dist/tokens/theme.d.ts.map +0 -1
- package/dist/tokens/theme.js +0 -15
- package/dist/tokens/typography.d.ts +0 -45
- package/dist/tokens/typography.d.ts.map +0 -1
- package/dist/tokens/typography.js +0 -56
- package/dist/utils/aria.d.ts +0 -60
- package/dist/utils/aria.d.ts.map +0 -1
- package/dist/utils/aria.js +0 -86
- package/dist/utils/focus.d.ts +0 -30
- package/dist/utils/focus.d.ts.map +0 -1
- package/dist/utils/focus.js +0 -80
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -3
- package/dist/utils/keyboard.d.ts +0 -38
- package/dist/utils/keyboard.d.ts.map +0 -1
- package/dist/utils/keyboard.js +0 -59
- /package/{dist → packages/design-system/src}/components/Button/Button.css +0 -0
- /package/{dist → packages/design-system/src}/components/DataTable/DataTable.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Checkbox.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Fieldset.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Input.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Label.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Radio.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Select.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Textarea.css +0 -0
- /package/{dist → packages/design-system/src}/components/Link/Link.css +0 -0
- /package/{dist → packages/design-system/src}/components/Modal/Modal.css +0 -0
- /package/{dist → packages/design-system/src}/components/Tabs/Tabs.css +0 -0
- /package/{dist → packages/design-system/src}/components/Toast/Toast.css +0 -0
- /package/{dist → packages/design-system/src}/components/Toast/ToastProvider.css +0 -0
- /package/{dist → packages/design-system/src}/styles/components.css +0 -0
- /package/{dist → packages/design-system/src}/styles/global.css +0 -0
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Input.css';
|
|
3
|
-
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
|
-
/**
|
|
5
|
-
* Error message to display
|
|
6
|
-
*/
|
|
7
|
-
error?: string;
|
|
8
|
-
/**
|
|
9
|
-
* Helper text to display below the input
|
|
10
|
-
*/
|
|
11
|
-
helperText?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Label for the input (creates associated label)
|
|
14
|
-
*/
|
|
15
|
-
label?: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Accessible Input component
|
|
19
|
-
*
|
|
20
|
-
* WCAG Compliance:
|
|
21
|
-
* - 1.3.1 Info and Relationships: Proper label-input association
|
|
22
|
-
* - 2.5.3 Label in Name: Label text matches accessible name
|
|
23
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
24
|
-
* - 4.1.3 Status Messages: Error messages announced
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```tsx
|
|
28
|
-
* <Input
|
|
29
|
-
* id="email"
|
|
30
|
-
* type="email"
|
|
31
|
-
* label="Email address"
|
|
32
|
-
* error="Please enter a valid email"
|
|
33
|
-
* />
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
37
|
-
//# sourceMappingURL=Input.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Input.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IAC7E;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,KAAK,qFAmEjB,CAAA"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { combineAriaDescribedBy } from '../../utils/aria';
|
|
5
|
-
import './Input.css';
|
|
6
|
-
/**
|
|
7
|
-
* Accessible Input component
|
|
8
|
-
*
|
|
9
|
-
* WCAG Compliance:
|
|
10
|
-
* - 1.3.1 Info and Relationships: Proper label-input association
|
|
11
|
-
* - 2.5.3 Label in Name: Label text matches accessible name
|
|
12
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
13
|
-
* - 4.1.3 Status Messages: Error messages announced
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* <Input
|
|
18
|
-
* id="email"
|
|
19
|
-
* type="email"
|
|
20
|
-
* label="Email address"
|
|
21
|
-
* error="Please enter a valid email"
|
|
22
|
-
* />
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export const Input = React.forwardRef(({ id, error, helperText, label, className = '', 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
|
|
26
|
-
const inputId = React.useId();
|
|
27
|
-
const finalId = id || `input-${inputId}`;
|
|
28
|
-
const errorId = error ? `${finalId}-error` : undefined;
|
|
29
|
-
const helperId = helperText ? `${finalId}-helper` : undefined;
|
|
30
|
-
const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
|
|
31
|
-
const classes = [
|
|
32
|
-
'form-input',
|
|
33
|
-
error && 'form-input--error',
|
|
34
|
-
props.disabled && 'form-input--disabled',
|
|
35
|
-
className,
|
|
36
|
-
]
|
|
37
|
-
.filter(Boolean)
|
|
38
|
-
.join(' ');
|
|
39
|
-
return (_jsxs("div", { className: "form-input-wrapper", children: [label && (_jsxs("label", { htmlFor: finalId, className: "form-label", children: [label, props.required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] })), _jsx("input", { ref: ref, id: finalId, className: classes, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy, required: props.required ? true : undefined, ...props }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
|
|
40
|
-
});
|
|
41
|
-
Input.displayName = 'Input';
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Label.css';
|
|
3
|
-
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
|
4
|
-
/**
|
|
5
|
-
* Whether this label is required (shows asterisk)
|
|
6
|
-
*/
|
|
7
|
-
required?: boolean;
|
|
8
|
-
/**
|
|
9
|
-
* ID of the input this label is associated with
|
|
10
|
-
*/
|
|
11
|
-
htmlFor?: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Accessible Label component
|
|
15
|
-
*
|
|
16
|
-
* WCAG Compliance:
|
|
17
|
-
* - 1.3.1 Info and Relationships: Proper label-input association
|
|
18
|
-
* - 2.5.3 Label in Name: Label text matches accessible name
|
|
19
|
-
* - 4.1.2 Name, Role, Value: Proper semantic HTML
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* <Label htmlFor="email" required>
|
|
24
|
-
* Email address
|
|
25
|
-
* </Label>
|
|
26
|
-
* <Input id="email" type="email" />
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export declare const Label: React.ForwardRefExoticComponent<LabelProps & React.RefAttributes<HTMLLabelElement>>;
|
|
30
|
-
//# sourceMappingURL=Label.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Label.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Label.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IAC7E;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,qFAqBjB,CAAA"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import './Label.css';
|
|
4
|
-
/**
|
|
5
|
-
* Accessible Label component
|
|
6
|
-
*
|
|
7
|
-
* WCAG Compliance:
|
|
8
|
-
* - 1.3.1 Info and Relationships: Proper label-input association
|
|
9
|
-
* - 2.5.3 Label in Name: Label text matches accessible name
|
|
10
|
-
* - 4.1.2 Name, Role, Value: Proper semantic HTML
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```tsx
|
|
14
|
-
* <Label htmlFor="email" required>
|
|
15
|
-
* Email address
|
|
16
|
-
* </Label>
|
|
17
|
-
* <Input id="email" type="email" />
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export const Label = React.forwardRef(({ required = false, className = '', children, ...props }, ref) => {
|
|
21
|
-
const classes = [
|
|
22
|
-
'form-label',
|
|
23
|
-
required && 'form-label--required',
|
|
24
|
-
className,
|
|
25
|
-
]
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.join(' ');
|
|
28
|
-
return (_jsxs("label", { ref: ref, className: classes, ...props, children: [children, required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] }));
|
|
29
|
-
});
|
|
30
|
-
Label.displayName = 'Label';
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Radio.css';
|
|
3
|
-
export interface RadioOption {
|
|
4
|
-
value: string;
|
|
5
|
-
label: string;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface RadioProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
9
|
-
/**
|
|
10
|
-
* Options for the radio group
|
|
11
|
-
*/
|
|
12
|
-
options: RadioOption[];
|
|
13
|
-
/**
|
|
14
|
-
* Name attribute for the radio group (required)
|
|
15
|
-
*/
|
|
16
|
-
name: string;
|
|
17
|
-
/**
|
|
18
|
-
* Label for the radio group
|
|
19
|
-
*/
|
|
20
|
-
label?: string;
|
|
21
|
-
/**
|
|
22
|
-
* Error message to display
|
|
23
|
-
*/
|
|
24
|
-
error?: string;
|
|
25
|
-
/**
|
|
26
|
-
* Helper text to display
|
|
27
|
-
*/
|
|
28
|
-
helperText?: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Accessible Radio component (radio group)
|
|
32
|
-
*
|
|
33
|
-
* WCAG Compliance:
|
|
34
|
-
* - 1.3.1 Info and Relationships: Proper fieldset/legend structure
|
|
35
|
-
* - 2.1.1 Keyboard: Full keyboard navigation
|
|
36
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```tsx
|
|
40
|
-
* <Radio
|
|
41
|
-
* name="size"
|
|
42
|
-
* label="Size"
|
|
43
|
-
* options={[
|
|
44
|
-
* { value: 's', label: 'Small' },
|
|
45
|
-
* { value: 'm', label: 'Medium' },
|
|
46
|
-
* ]}
|
|
47
|
-
* value={selected}
|
|
48
|
-
* onChange={handleChange}
|
|
49
|
-
* />
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLInputElement>>;
|
|
53
|
-
//# sourceMappingURL=Radio.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Radio.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Radio.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC3F;;OAEG;IACH,OAAO,EAAE,WAAW,EAAE,CAAA;IAEtB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,KAAK,qFAkFjB,CAAA"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { combineAriaDescribedBy } from '../../utils/aria';
|
|
5
|
-
import './Radio.css';
|
|
6
|
-
/**
|
|
7
|
-
* Accessible Radio component (radio group)
|
|
8
|
-
*
|
|
9
|
-
* WCAG Compliance:
|
|
10
|
-
* - 1.3.1 Info and Relationships: Proper fieldset/legend structure
|
|
11
|
-
* - 2.1.1 Keyboard: Full keyboard navigation
|
|
12
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* <Radio
|
|
17
|
-
* name="size"
|
|
18
|
-
* label="Size"
|
|
19
|
-
* options={[
|
|
20
|
-
* { value: 's', label: 'Small' },
|
|
21
|
-
* { value: 'm', label: 'Medium' },
|
|
22
|
-
* ]}
|
|
23
|
-
* value={selected}
|
|
24
|
-
* onChange={handleChange}
|
|
25
|
-
* />
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export const Radio = React.forwardRef(({ name, options, label, error, helperText, className = '', value, onChange, disabled, required, 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
|
|
29
|
-
const groupId = React.useId();
|
|
30
|
-
const errorId = error ? `radio-${groupId}-error` : undefined;
|
|
31
|
-
const helperId = helperText ? `radio-${groupId}-helper` : undefined;
|
|
32
|
-
const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
|
|
33
|
-
return (_jsxs("div", { className: "form-radio-wrapper", children: [label && (_jsx("div", { className: "form-radio-label", role: "group", "aria-labelledby": label ? `radio-label-${groupId}` : undefined, children: _jsxs("span", { id: `radio-label-${groupId}`, className: "form-label", children: [label, required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] }) })), _jsx("div", { className: "form-radio-group", role: "radiogroup", "aria-describedby": describedBy, "aria-invalid": error ? true : undefined, children: options.map((option, index) => {
|
|
34
|
-
const optionId = `radio-${groupId}-${index}`;
|
|
35
|
-
const isChecked = value === option.value;
|
|
36
|
-
return (_jsxs("div", { className: "form-radio-option", children: [_jsx("input", { ref: index === 0 ? ref : undefined, id: optionId, type: "radio", name: name, value: option.value, checked: isChecked, onChange: onChange, disabled: option.disabled || disabled, required: required, className: "form-radio", ...props }), _jsx("label", { htmlFor: optionId, className: "form-radio-label", children: option.label })] }, option.value));
|
|
37
|
-
}) }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
|
|
38
|
-
});
|
|
39
|
-
Radio.displayName = 'Radio';
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Select.css';
|
|
3
|
-
export interface SelectOption {
|
|
4
|
-
value: string;
|
|
5
|
-
label: string;
|
|
6
|
-
disabled?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'children'> {
|
|
9
|
-
/**
|
|
10
|
-
* Options for the select
|
|
11
|
-
*/
|
|
12
|
-
options: SelectOption[];
|
|
13
|
-
/**
|
|
14
|
-
* Error message to display
|
|
15
|
-
*/
|
|
16
|
-
error?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Helper text to display below the select
|
|
19
|
-
*/
|
|
20
|
-
helperText?: string;
|
|
21
|
-
/**
|
|
22
|
-
* Label for the select
|
|
23
|
-
*/
|
|
24
|
-
label?: string;
|
|
25
|
-
/**
|
|
26
|
-
* Placeholder option text
|
|
27
|
-
*/
|
|
28
|
-
placeholder?: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Accessible Select component
|
|
32
|
-
*
|
|
33
|
-
* WCAG Compliance:
|
|
34
|
-
* - 1.3.1 Info and Relationships: Proper label-select association
|
|
35
|
-
* - 2.1.1 Keyboard: Full keyboard navigation
|
|
36
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```tsx
|
|
40
|
-
* <Select
|
|
41
|
-
* id="country"
|
|
42
|
-
* label="Country"
|
|
43
|
-
* options={[
|
|
44
|
-
* { value: 'us', label: 'United States' },
|
|
45
|
-
* { value: 'ca', label: 'Canada' },
|
|
46
|
-
* ]}
|
|
47
|
-
* />
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
|
-
export declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLSelectElement>>;
|
|
51
|
-
//# sourceMappingURL=Select.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,cAAc,CAAA;AAErB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAClG;;OAEG;IACH,OAAO,EAAE,YAAY,EAAE,CAAA;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,MAAM,uFAgGlB,CAAA"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { combineAriaDescribedBy } from '../../utils/aria';
|
|
5
|
-
import './Select.css';
|
|
6
|
-
/**
|
|
7
|
-
* Accessible Select component
|
|
8
|
-
*
|
|
9
|
-
* WCAG Compliance:
|
|
10
|
-
* - 1.3.1 Info and Relationships: Proper label-select association
|
|
11
|
-
* - 2.1.1 Keyboard: Full keyboard navigation
|
|
12
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* <Select
|
|
17
|
-
* id="country"
|
|
18
|
-
* label="Country"
|
|
19
|
-
* options={[
|
|
20
|
-
* { value: 'us', label: 'United States' },
|
|
21
|
-
* { value: 'ca', label: 'Canada' },
|
|
22
|
-
* ]}
|
|
23
|
-
* />
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export const Select = React.forwardRef(({ id, options, error, helperText, label, placeholder, className = '', 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
|
|
27
|
-
const selectId = React.useId();
|
|
28
|
-
const finalId = id || `select-${selectId}`;
|
|
29
|
-
const errorId = error ? `${finalId}-error` : undefined;
|
|
30
|
-
const helperId = helperText ? `${finalId}-helper` : undefined;
|
|
31
|
-
const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId);
|
|
32
|
-
const handleKeyDown = React.useCallback((event) => {
|
|
33
|
-
// Arrow keys are handled natively by select
|
|
34
|
-
// But we can add custom handling if needed
|
|
35
|
-
if (props.onKeyDown) {
|
|
36
|
-
props.onKeyDown(event);
|
|
37
|
-
}
|
|
38
|
-
}, [props.onKeyDown]);
|
|
39
|
-
const classes = [
|
|
40
|
-
'form-select',
|
|
41
|
-
error && 'form-select--error',
|
|
42
|
-
props.disabled && 'form-select--disabled',
|
|
43
|
-
className,
|
|
44
|
-
]
|
|
45
|
-
.filter(Boolean)
|
|
46
|
-
.join(' ');
|
|
47
|
-
return (_jsxs("div", { className: "form-select-wrapper", children: [label && (_jsxs("label", { htmlFor: finalId, className: "form-label", children: [label, props.required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] })), _jsxs("select", { ref: ref, id: finalId, className: classes, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy, onKeyDown: handleKeyDown, required: props.required ? true : undefined, ...props, children: [placeholder && (_jsx("option", { value: "", disabled: true, children: placeholder })), options.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value)))] }), helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error }))] }));
|
|
48
|
-
});
|
|
49
|
-
Select.displayName = 'Select';
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Textarea.css';
|
|
3
|
-
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
4
|
-
/**
|
|
5
|
-
* Error message to display
|
|
6
|
-
*/
|
|
7
|
-
error?: string;
|
|
8
|
-
/**
|
|
9
|
-
* Helper text to display below the textarea
|
|
10
|
-
*/
|
|
11
|
-
helperText?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Label for the textarea
|
|
14
|
-
*/
|
|
15
|
-
label?: string;
|
|
16
|
-
/**
|
|
17
|
-
* Maximum character count (shows counter)
|
|
18
|
-
*/
|
|
19
|
-
maxLength?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Whether to show character count
|
|
22
|
-
*/
|
|
23
|
-
showCount?: boolean;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Accessible Textarea component
|
|
27
|
-
*
|
|
28
|
-
* WCAG Compliance:
|
|
29
|
-
* - 1.3.1 Info and Relationships: Proper label-textarea association
|
|
30
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
31
|
-
* - 4.1.3 Status Messages: Error messages announced
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```tsx
|
|
35
|
-
* <Textarea
|
|
36
|
-
* id="message"
|
|
37
|
-
* label="Message"
|
|
38
|
-
* maxLength={500}
|
|
39
|
-
* showCount
|
|
40
|
-
* />
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
44
|
-
//# sourceMappingURL=Textarea.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../../../src/components/Form/Textarea.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,gBAAgB,CAAA;AAEvB,MAAM,WAAW,aAAc,SAAQ,KAAK,CAAC,sBAAsB,CAAC,mBAAmB,CAAC;IACtF;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,QAAQ,2FA0FpB,CAAA"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { combineAriaDescribedBy } from '../../utils/aria';
|
|
5
|
-
import './Textarea.css';
|
|
6
|
-
/**
|
|
7
|
-
* Accessible Textarea component
|
|
8
|
-
*
|
|
9
|
-
* WCAG Compliance:
|
|
10
|
-
* - 1.3.1 Info and Relationships: Proper label-textarea association
|
|
11
|
-
* - 4.1.2 Name, Role, Value: Proper ARIA attributes
|
|
12
|
-
* - 4.1.3 Status Messages: Error messages announced
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* <Textarea
|
|
17
|
-
* id="message"
|
|
18
|
-
* label="Message"
|
|
19
|
-
* maxLength={500}
|
|
20
|
-
* showCount
|
|
21
|
-
* />
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export const Textarea = React.forwardRef(({ id, error, helperText, label, maxLength, showCount = false, className = '', value, 'aria-describedby': ariaDescribedBy, ...props }, ref) => {
|
|
25
|
-
const textareaId = React.useId();
|
|
26
|
-
const finalId = id || `textarea-${textareaId}`;
|
|
27
|
-
const errorId = error ? `${finalId}-error` : undefined;
|
|
28
|
-
const helperId = helperText ? `${finalId}-helper` : undefined;
|
|
29
|
-
const countId = showCount ? `${finalId}-count` : undefined;
|
|
30
|
-
const describedBy = combineAriaDescribedBy(ariaDescribedBy, errorId, helperId, countId);
|
|
31
|
-
const currentLength = typeof value === 'string' ? value.length : 0;
|
|
32
|
-
const remainingChars = maxLength ? maxLength - currentLength : undefined;
|
|
33
|
-
const classes = [
|
|
34
|
-
'form-textarea',
|
|
35
|
-
error && 'form-textarea--error',
|
|
36
|
-
props.disabled && 'form-textarea--disabled',
|
|
37
|
-
className,
|
|
38
|
-
]
|
|
39
|
-
.filter(Boolean)
|
|
40
|
-
.join(' ');
|
|
41
|
-
return (_jsxs("div", { className: "form-textarea-wrapper", children: [label && (_jsxs("label", { htmlFor: finalId, className: "form-label", children: [label, props.required && (_jsxs("span", { className: "form-label__required", "aria-hidden": "true", children: [' ', "*"] }))] })), _jsx("textarea", { ref: ref, id: finalId, className: classes, maxLength: maxLength, value: value, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy, required: props.required ? true : undefined, ...props }), (showCount || helperText) && (_jsxs("div", { className: "form-textarea-footer", children: [helperText && !error && (_jsx("span", { id: helperId, className: "form-helper-text", children: helperText })), error && (_jsx("span", { id: errorId, className: "form-error-text", role: "alert", children: error })), showCount && maxLength && (_jsxs("span", { id: countId, className: "form-character-count", "aria-live": "polite", children: [currentLength, " / ", maxLength] }))] }))] }));
|
|
42
|
-
});
|
|
43
|
-
Textarea.displayName = 'Textarea';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Form/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Link.css';
|
|
3
|
-
export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
4
|
-
/**
|
|
5
|
-
* Whether this is an external link
|
|
6
|
-
* Automatically adds rel="noopener noreferrer" for security
|
|
7
|
-
*/
|
|
8
|
-
external?: boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Whether this is a skip link (for keyboard navigation)
|
|
11
|
-
*/
|
|
12
|
-
skip?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* ARIA label for the link (required if no visible text)
|
|
15
|
-
*/
|
|
16
|
-
'aria-label'?: string;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Accessible Link component
|
|
20
|
-
*
|
|
21
|
-
* WCAG Compliance:
|
|
22
|
-
* - 2.4.4 Link Purpose: Clear link text or aria-label
|
|
23
|
-
* - 2.4.7 Focus Visible: Clear focus indicators
|
|
24
|
-
* - 4.1.2 Name, Role, Value: Proper semantic HTML
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```tsx
|
|
28
|
-
* <Link href="/about" external>
|
|
29
|
-
* Learn more
|
|
30
|
-
* </Link>
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
34
|
-
//# sourceMappingURL=Link.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../../src/components/Link/Link.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,YAAY,CAAA;AAEnB,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAC9E;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAElB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,qFA2EhB,CAAA"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import './Link.css';
|
|
5
|
-
/**
|
|
6
|
-
* Accessible Link component
|
|
7
|
-
*
|
|
8
|
-
* WCAG Compliance:
|
|
9
|
-
* - 2.4.4 Link Purpose: Clear link text or aria-label
|
|
10
|
-
* - 2.4.7 Focus Visible: Clear focus indicators
|
|
11
|
-
* - 4.1.2 Name, Role, Value: Proper semantic HTML
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* <Link href="/about" external>
|
|
16
|
-
* Learn more
|
|
17
|
-
* </Link>
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export const Link = React.forwardRef(({ external = false, skip = false, href, rel, target, className = '', children, 'aria-label': ariaLabel, ...props }, ref) => {
|
|
21
|
-
// Determine if link is external based on href or explicit prop
|
|
22
|
-
const isExternal = external ||
|
|
23
|
-
(href && (href.startsWith('http') || href.startsWith('//')));
|
|
24
|
-
// Build rel attribute
|
|
25
|
-
const relAttributes = React.useMemo(() => {
|
|
26
|
-
const attrs = new Set(rel?.split(' ') || []);
|
|
27
|
-
if (isExternal) {
|
|
28
|
-
attrs.add('noopener');
|
|
29
|
-
attrs.add('noreferrer');
|
|
30
|
-
}
|
|
31
|
-
return Array.from(attrs).join(' ');
|
|
32
|
-
}, [isExternal, rel]);
|
|
33
|
-
// Set target for external links
|
|
34
|
-
const linkTarget = isExternal && !target ? '_blank' : target;
|
|
35
|
-
const classes = [
|
|
36
|
-
'link',
|
|
37
|
-
skip && 'link--skip',
|
|
38
|
-
className,
|
|
39
|
-
]
|
|
40
|
-
.filter(Boolean)
|
|
41
|
-
.join(' ');
|
|
42
|
-
// Skip links should use button semantics if no href
|
|
43
|
-
if (skip && !href) {
|
|
44
|
-
return (_jsx("button", { ref: ref, className: classes, "aria-label": ariaLabel, ...props, children: children }));
|
|
45
|
-
}
|
|
46
|
-
return (_jsxs("a", { ref: ref, href: href, rel: relAttributes || undefined, target: linkTarget, className: classes, "aria-label": ariaLabel, ...props, children: [children, isExternal && (_jsxs("span", { className: "link__external-icon", "aria-hidden": "true", children: [' ', _jsx("span", { "aria-label": "(opens in new tab)", children: "\u2197" })] }))] }));
|
|
47
|
-
});
|
|
48
|
-
Link.displayName = 'Link';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Link/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC7B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { Link } from './Link';
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Modal.css';
|
|
3
|
-
export interface ModalProps {
|
|
4
|
-
/**
|
|
5
|
-
* Whether the modal is open
|
|
6
|
-
*/
|
|
7
|
-
isOpen: boolean;
|
|
8
|
-
/**
|
|
9
|
-
* Callback when modal should close
|
|
10
|
-
*/
|
|
11
|
-
onClose: () => void;
|
|
12
|
-
/**
|
|
13
|
-
* Title of the modal (required for accessibility)
|
|
14
|
-
*/
|
|
15
|
-
title: string;
|
|
16
|
-
/**
|
|
17
|
-
* Content of the modal
|
|
18
|
-
*/
|
|
19
|
-
children: React.ReactNode;
|
|
20
|
-
/**
|
|
21
|
-
* Whether to close on backdrop click
|
|
22
|
-
*/
|
|
23
|
-
closeOnBackdropClick?: boolean;
|
|
24
|
-
/**
|
|
25
|
-
* Whether to close on ESC key press
|
|
26
|
-
*/
|
|
27
|
-
closeOnEscape?: boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Size of the modal
|
|
30
|
-
*/
|
|
31
|
-
size?: 'sm' | 'md' | 'lg' | 'full';
|
|
32
|
-
/**
|
|
33
|
-
* Element to return focus to when modal closes
|
|
34
|
-
*/
|
|
35
|
-
returnFocusTo?: HTMLElement | null;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Accessible Modal component using HTML5 dialog element
|
|
39
|
-
*
|
|
40
|
-
* Uses the native `<dialog>` element which provides:
|
|
41
|
-
* - Built-in focus management and focus trapping
|
|
42
|
-
* - Automatic body scroll prevention
|
|
43
|
-
* - Native backdrop overlay
|
|
44
|
-
* - ESC key handling (configurable)
|
|
45
|
-
*
|
|
46
|
-
* WCAG Compliance:
|
|
47
|
-
* - 2.1.1 Keyboard: ESC key support, built-in focus trap
|
|
48
|
-
* - 2.1.2 No Keyboard Trap: Focus returns to trigger
|
|
49
|
-
* - 2.4.3 Focus Order: Focus trapped within modal (native behavior)
|
|
50
|
-
* - 4.1.2 Name, Role, Value: ARIA modal pattern
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```tsx
|
|
54
|
-
* <Modal
|
|
55
|
-
* isOpen={isOpen}
|
|
56
|
-
* onClose={() => setIsOpen(false)}
|
|
57
|
-
* title="Confirm Action"
|
|
58
|
-
* >
|
|
59
|
-
* <p>Are you sure?</p>
|
|
60
|
-
* </Modal>
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
export declare const Modal: React.FC<ModalProps>;
|
|
64
|
-
//# sourceMappingURL=Modal.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/Modal/Modal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAGhD,OAAO,aAAa,CAAA;AAEpB,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAA;IAEf;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAA;IAEnB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IAEzB;;OAEG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAE9B;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;;OAEG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAA;IAElC;;OAEG;IACH,aAAa,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CA2HtC,CAAA"}
|