@fpkit/acss 0.5.8 → 0.5.9

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.
@@ -1,38 +1,38 @@
1
1
  // Code: Breadcrumb component
2
- import React from 'react'
3
- import UI from '#components/ui'
4
- import { Truncate } from '#libs/content'
5
- import Link from '#components/link/link'
2
+ import React from "react";
3
+ import UI from "#components/ui";
4
+ import { Truncate } from "#libs/content";
5
+ import Link from "#components/link/link";
6
6
 
7
7
  // TYPES
8
8
 
9
9
  type customRoute = {
10
10
  /** The path or id for routing */
11
- path?: string
11
+ path?: string;
12
12
  /** The display name */
13
- name: string
13
+ name: string;
14
14
  /** The url if linking out */
15
- url?: string
16
- }
15
+ url?: string;
16
+ };
17
17
 
18
18
  type BreadcrumbProps = {
19
19
  /** Array of custom route objects */
20
- routes?: customRoute[]
20
+ routes?: customRoute[];
21
21
  /** Starting route node */
22
- startRoute?: React.ReactNode
22
+ startRoute?: React.ReactNode;
23
23
  /* Starting route url */
24
- startRouteUrl?: string
24
+ startRouteUrl?: string;
25
25
  /** Spacer node between routes */
26
- spacer?: React.ReactNode
26
+ spacer?: React.ReactNode;
27
27
  /** String representing current route */
28
- currentRoute?: string
28
+ currentRoute?: string;
29
29
  /** Prefix breadcrumb aria-label - "prefix breadcrumb" */
30
- ariaLabelPrefix?: string
30
+ ariaLabelPrefix?: string;
31
31
  /** Truncate breadcrumb text after this length */
32
- truncateLength?: number
32
+ truncateLength?: number;
33
33
  /** Link props for breadcrumb links */
34
- linkProps?: React.ComponentProps<typeof Link>
35
- } & React.ComponentProps<typeof UI>
34
+ linkProps?: React.ComponentProps<typeof Link>;
35
+ } & React.ComponentProps<typeof UI>;
36
36
 
37
37
  // Components
38
38
 
@@ -53,11 +53,17 @@ const Items = ({
53
53
  ...props
54
54
  }: React.ComponentProps<typeof UI>) => {
55
55
  return (
56
- <li data-list="unstyled inline" {...props}>
56
+ <li
57
+ id={id}
58
+ style={styles}
59
+ className={classes}
60
+ data-list="unstyled inline"
61
+ {...props}
62
+ >
57
63
  {children}
58
64
  </li>
59
- )
60
- }
65
+ );
66
+ };
61
67
 
62
68
  /**
63
69
  * List component.
@@ -70,8 +76,8 @@ const List = ({ children, ...props }: React.ComponentProps<typeof UI>) => {
70
76
  <UI as="ol" data-list="unstyled inline" {...props}>
71
77
  {children}
72
78
  </UI>
73
- )
74
- }
79
+ );
80
+ };
75
81
 
76
82
  /**
77
83
  * Nav component.
@@ -93,8 +99,8 @@ const Nav = ({
93
99
  <UI as="nav" id={id} styles={styles} className={classes} {...props}>
94
100
  <List>{children}</List>
95
101
  </UI>
96
- )
97
- }
102
+ );
103
+ };
98
104
 
99
105
  /**
100
106
  * Navigation component for breadcrumbs.
@@ -110,7 +116,7 @@ const Nav = ({
110
116
  * @param props.children - Child components.
111
117
  */
112
118
  export const Breadcrumb = ({
113
- startRoute = 'Home',
119
+ startRoute = "Home",
114
120
  startRouteUrl = "/",
115
121
  currentRoute,
116
122
  spacer = <>&#47;</>,
@@ -123,13 +129,13 @@ export const Breadcrumb = ({
123
129
  linkProps,
124
130
  ...props
125
131
  }: BreadcrumbProps): React.JSX.Element => {
126
- const [currentPath, setCurrentPath] = React.useState('')
132
+ const [currentPath, setCurrentPath] = React.useState("");
127
133
  React.useEffect(() => {
128
- const path = currentRoute || window.location.pathname
134
+ const path = currentRoute || window.location.pathname;
129
135
  if (path.length) {
130
- setCurrentPath(path)
136
+ setCurrentPath(path);
131
137
  }
132
- }, [currentRoute])
138
+ }, [currentRoute]);
133
139
 
134
140
  /**
135
141
  * Gets the path name for the given path segment.
@@ -138,23 +144,22 @@ export const Breadcrumb = ({
138
144
  * @returns The path name object for the given path segment.
139
145
  */
140
146
  const getPathName = (pathSegment: string): customRoute => {
141
- const route = routes?.find((route) => route.path === pathSegment)
147
+ const route = routes?.find((route) => route.path === pathSegment);
142
148
 
143
149
  return {
144
150
  path: route?.path || pathSegment,
145
151
  name: route?.name || pathSegment,
146
152
  url: route?.url || pathSegment,
147
- }
148
- }
153
+ };
154
+ };
149
155
 
150
156
  /** Array of path segments from current path */
151
- const segments = currentPath.split('/').filter((segment) => segment)
157
+ const segments = currentPath.split("/").filter((segment) => segment);
152
158
  /** Index of last item in segments array */
153
- const lastSegment = segments.length - 1
159
+ const lastSegment = segments.length - 1;
154
160
 
155
161
  /** Unique id for breadcrumb */
156
- const uuid = React.useId()
157
-
162
+ const uuid = React.useId();
158
163
 
159
164
  return currentPath.length ? (
160
165
  <Nav
@@ -165,62 +170,62 @@ export const Breadcrumb = ({
165
170
  aria-label={ariaLabelPrefix}
166
171
  >
167
172
  <Items key={`${startRoute}-${uuid}`}>
168
- <Link href={startRouteUrl} {...linkProps}>{startRoute}</Link>
173
+ <Link href={startRouteUrl} {...linkProps}>
174
+ {startRoute}
175
+ </Link>
169
176
  </Items>
170
177
  <>
171
- {segments.length ? (
172
- segments.map((segment: any, index: number) => {
173
- const currentSegment = getPathName(segment)
174
- const { name, url, path } = currentSegment
175
- return index === lastSegment ? (
176
- <>
177
- {typeof segments[lastSegment] === 'string' &&
178
- segments[lastSegment].length > 3 &&
179
- segments[lastSegment] !== segments[lastSegment - 1] && (
180
- <Items key={`${path || index}-${uuid}`}>
181
-
182
- <span aria-hidden="true">{spacer}</span>
183
- <a
184
- href="#"
185
- aria-current="page"
186
- aria-label={
187
- name.length > truncateLength ? name : undefined
188
- }
189
- >
190
- {Truncate(decodeURIComponent(name), truncateLength)}
191
- </a>
192
-
193
- </Items>
194
- )}
195
- </>
196
- ) : (
197
- <Items key={`${currentSegment?.name}-${uuid}`}>
198
- <span aria-hidden="true">{spacer}</span>
199
- <span>
200
- <Link
201
- href={url}
202
- aria-label={name.length > truncateLength ? name : undefined}
203
- {...linkProps}
204
- >
205
- {Truncate(decodeURIComponent(name), truncateLength)}
206
- </Link>
207
- </span>
208
- </Items>
209
- );
210
- })
211
- ) : (
212
- null
213
- )}
178
+ {segments.length
179
+ ? segments.map((segment: string, index: number) => {
180
+ const currentSegment = getPathName(segment);
181
+ const { name, url, path } = currentSegment;
182
+ return index === lastSegment ? (
183
+ <>
184
+ {typeof segments[lastSegment] === "string" &&
185
+ segments[lastSegment].length > 3 &&
186
+ segments[lastSegment] !== segments[lastSegment - 1] && (
187
+ <Items key={`${path || index}-${uuid}`}>
188
+ <span aria-hidden="true">{spacer}</span>
189
+ <a
190
+ href="#"
191
+ aria-current="page"
192
+ aria-label={
193
+ name.length > truncateLength ? name : undefined
194
+ }
195
+ >
196
+ {Truncate(decodeURIComponent(name), truncateLength)}
197
+ </a>
198
+ </Items>
199
+ )}
200
+ </>
201
+ ) : (
202
+ <Items key={`${currentSegment?.name}-${uuid}`}>
203
+ <span aria-hidden="true">{spacer}</span>
204
+ <span>
205
+ <Link
206
+ href={url}
207
+ aria-label={
208
+ name.length > truncateLength ? name : undefined
209
+ }
210
+ {...linkProps}
211
+ >
212
+ {Truncate(decodeURIComponent(name), truncateLength)}
213
+ </Link>
214
+ </span>
215
+ </Items>
216
+ );
217
+ })
218
+ : null}
214
219
  </>
215
220
  </Nav>
216
221
  ) : (
217
222
  <></>
218
- )
219
- }
223
+ );
224
+ };
220
225
 
221
- export default Breadcrumb
226
+ export default Breadcrumb;
222
227
 
223
- Breadcrumb.displayName = 'BreadCrumb'
224
- Breadcrumb.Nav = Nav
225
- Breadcrumb.List = List
226
- Breadcrumb.Items = Items
228
+ Breadcrumb.displayName = "BreadCrumb";
229
+ Breadcrumb.Nav = Nav;
230
+ Breadcrumb.List = List;
231
+ Breadcrumb.Items = Items;
@@ -0,0 +1,133 @@
1
+ import { Meta } from "@storybook/blocks";
2
+
3
+ <Meta title="FP.REACT Components/Card/Readme" />
4
+
5
+ # Card Component
6
+
7
+ The Card component is a versatile and reusable React component for creating
8
+ card-like UI elements. It's part of the FPKit React component library.
9
+
10
+ ## Usage
11
+
12
+ ```tsx
13
+ import Card from "@fpkit/cards";
14
+
15
+ <Card elm="div">
16
+ <Card.Title>Card Title</Card.Title>
17
+ <Card.Content
18
+ className="custom-card-content"
19
+ styles={{ color: "blue", padding: "1rem" }}
20
+ >
21
+ This is the content of the card.
22
+ </Card.Content>
23
+ </Card>;
24
+ ```
25
+
26
+ ## Components
27
+
28
+ ### Card
29
+
30
+ The main container component for the card.
31
+
32
+ #### Props
33
+
34
+ - `elm?: 'div' | 'aside' | 'section' | 'article'` - HTML element to render as
35
+ (default: 'div')
36
+ - `title?: React.ReactNode` - Card title
37
+ - `footer?: React.ReactNode` - Card footer
38
+ - `styles?: React.CSSProperties` - Inline styles
39
+ - `classes?: string` - Additional CSS classes
40
+ - `id?: string` - Unique ID for the card
41
+
42
+ All other props, such as `aria-*` attributes, `data-*` attributes, and event
43
+ handlers (e.g., `onClick`, `onMouseEnter`), are passed through to the underlying
44
+ UI component. This allows you to customize the behavior and accessibility of the
45
+ Card component as needed.
46
+
47
+ ### Card.Title
48
+
49
+ A sub-component for rendering the card's title.
50
+
51
+ #### Props
52
+
53
+ - `as?: React.ElementType` - HTML element to render as (default: 'h3')
54
+ - `className?: string` - Additional CSS classes
55
+ - `styles?: React.CSSProperties` - Inline styles
56
+
57
+ #### Example
58
+
59
+ ### Card.Content
60
+
61
+ A sub-component for rendering the card's main content.
62
+
63
+ #### Props
64
+
65
+ - `className?: string` - Additional CSS classes
66
+ - `styles?: React.CSSProperties` - Inline styles
67
+
68
+ ## Styling
69
+
70
+ The component uses CSS classes for styling:
71
+
72
+ - `.card-title` for the title
73
+ - `.card-content` for the content
74
+
75
+ To integrate these styles with CSS Modules or SASS/SCSS:
76
+
77
+ 1. Create a CSS Module file (e.g., `Card.module.scss`) or a SASS/SCSS file
78
+ (e.g., `Card.scss`).
79
+
80
+ ## Accessibility
81
+
82
+ The Card component is designed with accessibility in mind:
83
+
84
+ - It uses semantic HTML elements (`div`, `aside`, `section`, or `article`) for
85
+ the main container.
86
+ - The Title component defaults to using an `h3` element, which can be changed if
87
+ needed.
88
+ - The Content component uses an `article` element for semantic structure.
89
+
90
+ ### Example with ARIA Attributes
91
+
92
+ ```tsx
93
+ <Card classes={styles.card}>
94
+ <Card.Title className={styles.cardTitle}>Card Title</Card.Title>
95
+ <Card.Content className={styles.cardContent}>
96
+ This is the content of the card.
97
+ </Card.Content>
98
+ </Card>
99
+ ```
100
+
101
+ You can override these classes or provide additional styling through the
102
+ `className` and `styles` props.
103
+
104
+ ## Accessibility
105
+
106
+ The Card component is designed with accessibility in mind:
107
+
108
+ - It uses semantic HTML elements (`div`, `aside`, `section`, or `article`) for
109
+ the main container.
110
+ - The Title component defaults to using an `h3` element, which can be changed if
111
+ needed.
112
+ - The Content component uses an `article` element for semantic structure.
113
+
114
+ ## TypeScript
115
+
116
+ This component is written in TypeScript and provides type definitions for all
117
+ props and sub-components.
118
+
119
+ ## Contributing
120
+
121
+ When contributing to this component, please follow the established code style
122
+ and conventions. Ensure all changes are well-tested using **Vitest** and
123
+ documented. Additionally, use **ESLint** and **Prettier** for code formatting
124
+ and linting to maintain consistency.
125
+
126
+ This README provides an overview of the Card component, its usage, available
127
+ props, styling information, and accessibility considerations. It also mentions
128
+ that the component is written in TypeScript and provides guidance for
129
+ contributors.
130
+
131
+ ```
132
+
133
+ ```
@@ -15,18 +15,25 @@ type DetailsProps = {
15
15
  } & React.ComponentProps<"details"> &
16
16
  Partial<React.ComponentProps<typeof UI>>;
17
17
 
18
- /**3
19
- * Details component props interface.
18
+ /**
19
+ * A React component that renders a details element with a summary and content.
20
20
  *
21
- * @param {React.CSSProperties} [styles] - CSS styles object.
22
- * @param {string} [classes] - Classnames string.
23
- * @param {boolean} [open] - Whether the details is open.
24
- * @param {(e: React.PointerEvent<HTMLDetailsElement>) => void} [onToggle] - onToggle callback.
25
- * @param {(e: React.PointerEvent<HTMLDetailsElement>) => void} [onPointerDown] - onPointerDown callback.
26
- * @param {ReactNode} children - The content inside the details.
27
- * @param {string} [ariaLabel] - aria-label for accessibility.
28
- * @param {React.Ref<any>} [ref] - Ref object.
29
- * @param {Object} props - Other props.
21
+ * @param summary - The summary text shown for the details.
22
+ * @param ariaLabel - The aria-label element for accessibility.
23
+ * @param icon - An optional icon to display in the summary.
24
+ * @param styles - Optional styles to apply to the details element.
25
+ * @param classes - Optional CSS classes to apply to the details element.
26
+ * @param name - An optional name for the details element.
27
+ * @param open - Whether the details element should be initially open.
28
+ * @param onPointerDown - A callback function to be called when the summary is clicked.
29
+ * @param onToggle - A callback function to be called when the details element is toggled.
30
+ * @param children - The content to be displayed inside the details element.
31
+ * @param ref - A ref to the details element.
32
+ * @param props - Additional props to be passed to the details element.
33
+ * @example
34
+ * <Details summary="Details" ariaLabel="Details">
35
+ * <p>Details content</p>
36
+ * </Details>
30
37
  */
31
38
  export const Details = ({
32
39
  summary,
@@ -1,25 +1,20 @@
1
- import React from 'react'
2
- import FP from '../fp'
1
+ import React from "react";
2
+ import FP from "../fp";
3
3
 
4
4
  export type InputProps = {
5
5
  /**
6
6
  * The type of the input.
7
7
  */
8
- type?: 'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'search'
8
+ type?: "text" | "password" | "email" | "number" | "tel" | "url" | "search";
9
9
 
10
10
  /**
11
11
  * Set the element as disabled
12
12
  */
13
- isDisabled?: boolean
14
- } & React.ComponentProps<typeof FP>
13
+ isDisabled?: boolean;
14
+ } & React.ComponentProps<typeof FP>;
15
15
 
16
- /**
17
- * Input component that renders an HTML input element.
18
- * @param {InputProps} props - The input component props.
19
- * @returns {JSX.Element} - The input component.
20
- */
21
16
  export const Input = ({
22
- type = 'text',
17
+ type = "text",
23
18
  name,
24
19
  value,
25
20
  placeholder,
@@ -38,29 +33,29 @@ export const Input = ({
38
33
  }: InputProps): JSX.Element => {
39
34
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
40
35
  if (onChange && !disabled) {
41
- onChange?.(e)
36
+ onChange?.(e);
42
37
  }
43
- }
38
+ };
44
39
 
45
40
  const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
46
41
  if (onBlur && !disabled) {
47
- onBlur?.(e)
42
+ onBlur?.(e);
48
43
  }
49
- }
44
+ };
50
45
 
51
46
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
52
47
  if (onPointerDown && !disabled) {
53
- e.preventDefault()
54
- onPointerDown?.(e)
48
+ e.preventDefault();
49
+ onPointerDown?.(e);
55
50
  }
56
- }
51
+ };
57
52
 
58
53
  return (
59
54
  <FP
60
55
  as="input"
61
56
  id={id}
62
57
  type={type}
63
- placeholder={placeholder || `${required ? '*' : ''} ${type} input `}
58
+ placeholder={placeholder || `${required ? "*" : ""} ${type} input `}
64
59
  className={classes}
65
60
  styles={styles}
66
61
  onChange={handleChange}
@@ -77,8 +72,7 @@ export const Input = ({
77
72
  readOnly={readonly}
78
73
  {...props}
79
74
  />
80
- )
81
- }
82
-
83
- Input.displayName = 'Input'
84
- export default Input
75
+ );
76
+ };
77
+ Input.displayName = "Input";
78
+ export default Input;
@@ -1,15 +1,27 @@
1
- import React from 'react'
2
- import UI from '#components/ui'
3
- import { type } from 'os'
1
+ import React from "react";
2
+ import UI from "#components/ui";
4
3
 
5
4
  export type TitleProps = {
6
- children: React.ReactNode
7
- type: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
8
- ui?: string
9
- } & React.ComponentProps<typeof UI>
5
+ children: React.ReactNode;
6
+ type: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
7
+ ui?: string;
8
+ } & React.ComponentProps<typeof UI>;
10
9
 
10
+ /**
11
+ * A flexible heading component that renders different heading levels.
12
+ *
13
+ * @component
14
+ * @param {'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'} [props.type='h3'] - The heading level to render
15
+ * @param {string} [props.id] - Optional ID attribute for the heading
16
+ * @param {React.CSSProperties} [props.styles] - Custom styles to apply to the heading
17
+ * @param {string} [props.ui] - Custom UI modifier to be added as a data attribute
18
+ * @param {ReactNode} props.children - The content to be rendered within the heading
19
+ * @param {Object} [props] - Additional props to be spread onto the heading element
20
+ *
21
+ * @returns {JSX.Element} A heading element of the specified type
22
+ */
11
23
  const Heading = ({
12
- type = 'h3',
24
+ type = "h3",
13
25
  id,
14
26
  styles,
15
27
  ui,
@@ -20,8 +32,8 @@ const Heading = ({
20
32
  <UI as={type} id={id} styles={styles} data-ui={ui} {...props}>
21
33
  {children}
22
34
  </UI>
23
- )
24
- }
35
+ );
36
+ };
25
37
 
26
- export default Heading
27
- Heading.displayName = 'Heading'
38
+ export default Heading;
39
+ Heading.displayName = "Heading";
@@ -0,0 +1,98 @@
1
+ import { Meta } from "@storybook/blocks";
2
+
3
+ <Meta title="FP.REACT Components/Text/Readme" />
4
+
5
+ # Text Component
6
+
7
+ ## Summary
8
+
9
+ The `Text` component is a flexible wrapper for rendering various text elements
10
+ such as paragraphs, spans, and other inline or block-level elements. It supports
11
+ customization through props like `elm`, `text`, and additional styles or
12
+ classes.
13
+
14
+ The `Title` component is a specialized version of `Text` for rendering HTML
15
+ headings (`h1` to `h6`).
16
+
17
+ ## Features
18
+
19
+ - Render any valid HTML text element.
20
+ - Pass text content directly or use children for nested elements.
21
+ - Fully customizable with styles and classes.
22
+ - Supports accessibility with `id` and other attributes.
23
+
24
+ ## Props
25
+
26
+ ### Text Props
27
+
28
+ | Name | Type | Default | Description |
29
+ | ---------- | --------------------- | ------- | ----------------------------------- |
30
+ | `elm` | `TextElements` | `'p'` | The HTML element to render. |
31
+ | `text` | `string` | `''` | Text content to display. |
32
+ | `id` | `string` | `''` | Unique identifier for the element. |
33
+ | `styles` | `React.CSSProperties` | `null` | Inline styles for the element. |
34
+ | `classes` | `string` | `''` | Additional CSS classes for styling. |
35
+ | `children` | `React.ReactNode` | `null` | Nested content inside the element. |
36
+
37
+ ### Title Props
38
+
39
+ | Name | Type | Default | Description |
40
+ | ---------- | ---------------------------------------------- | ------- | ----------------------------------- |
41
+ | `elm` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6'` | `'h3'` | The HTML heading element to render. |
42
+ | `id` | `string` | `''` | Unique identifier for the element. |
43
+ | `styles` | `React.CSSProperties` | `null` | Inline styles for the element. |
44
+ | `classes` | `string` | `''` | Additional CSS classes for styling. |
45
+ | `children` | `React.ReactNode` | `null` | Nested content inside the element. |
46
+
47
+ ## Technical Details
48
+
49
+ - The `Text` component uses the `UI` component internally to render the
50
+ specified element.
51
+ - The `Title` component is a wrapper around `Text` with default settings for
52
+ headings.
53
+ - Both components support additional props inherited from the `UI` component.
54
+
55
+ ## Usage Examples
56
+
57
+ ### Basic Usage
58
+
59
+ ```tsx
60
+ import { Text, Title } from "./text";
61
+
62
+ const App = () => (
63
+ <>
64
+ <Text elm="p" text="This is a paragraph." />
65
+ <Text elm="span" text="This is a span." />
66
+ <Title elm="h1">This is a heading</Title>
67
+ </>
68
+ );
69
+ ```
70
+
71
+ ### Advanced Usage
72
+
73
+ ```tsx
74
+ import { Text, Title } from "./text";
75
+
76
+ const App = () => (
77
+ <>
78
+ <Text
79
+ elm="blockquote"
80
+ styles={{ fontStyle: "italic", color: "gray" }}
81
+ text="This is a blockquote."
82
+ />
83
+ <Title
84
+ elm="h2"
85
+ classes="custom-heading"
86
+ styles={{ fontWeight: "bold", fontSize: "2rem" }}
87
+ >
88
+ Custom Heading
89
+ </Title>
90
+ </>
91
+ );
92
+ ```
93
+
94
+ ### Additional Notes
95
+
96
+ - Use the `elm` prop to specify the desired HTML element.
97
+ - Combine `styles` and `classes` for advanced styling.
98
+ - Use `children` for nested content when `text` is not sufficient.