@reltio/design 0.0.0-65

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/README.md ADDED
@@ -0,0 +1,58 @@
1
+
2
+ **@reltio/design** is a public JavaScript library that contains key components and utilities for developing Reltio UI applications.
3
+
4
+ - Built on ReactJS.
5
+ - Intended for Reltio internal teams developing applications.
6
+ - [Storybook with component examples is available here](https://reltio.design).
7
+
8
+ ## Installation
9
+
10
+ To install, use NPM:
11
+ ```bash
12
+ npm install @reltio/design
13
+ ```
14
+ ## Usage
15
+
16
+ Example usage of the **Button** component:
17
+
18
+ ```jsx
19
+ import React from 'react';
20
+ import { Button } from '@reltio/design';
21
+
22
+ function App() {
23
+ return <Button>Touch me</Button>;
24
+ }
25
+
26
+ export default App;
27
+ ```
28
+
29
+ **Important:** React is listed as peer dependencies. Make sure it's added to your project.
30
+
31
+ ## Target Audience
32
+
33
+ The library is designed for Reltio internal teams developing applications.
34
+
35
+ If you need additional components from the [Storybook](https://reltio.design), contact the **UI Center of Excellence** team at [ui.coe@reltio.com](mailto:ui.coe@reltio.com).
36
+
37
+ ## Contribution
38
+
39
+ Adding or modifying components is done via the monorepo: [https://bitbucket.org/reltio-ondemand/reltio-design](https://bitbucket.org/reltio-ondemand/reltio-design).
40
+
41
+ ### Development Process:
42
+
43
+ 1. Ensure you have write access to the monorepo.
44
+ 2. Create a feature branch for your changes.
45
+ 3. Run Storybook locally with `npm run dev`
46
+ 3. Follow the standard pull request (PR) creation and code review processes adopted across Reltio teams.
47
+ 4. After your PR is successfully merged into the main branch, run a **custom Bitbucket pipeline** to publish the component to NPM.
48
+
49
+ ### Publishing:
50
+
51
+ - If the component is published from the main branch, it will be released to NPM.
52
+ - If it is published from a feature branch, the component will be versioned as `0.0.0-{BUILD_ID}` and tagged with `{BRANCH_NAME}`.
53
+
54
+ For any questions about the process, contact the **UI Center of Excellence** team via [ui.coe@reltio.com](mailto:ui.coe@reltio.com).
55
+
56
+ ## License
57
+
58
+ This library is distributed under Reltio’s corporate license and is intended for internal use only.
@@ -0,0 +1,9 @@
1
+ import type { ButtonProps } from "./Button.types";
2
+ /**
3
+ * Universal Button Component
4
+ *
5
+ * A flexible, accessible button component that supports multiple variants
6
+ * (filled, outlined, text), color options (primary, secondary, inherited),
7
+ * sizes, states, and can render as either a button or anchor element.
8
+ */
9
+ export declare const Button: ({ variant, color, size, disabled, fullWidth, children, className, onClick, href, type, "aria-label": ariaLabel, ...rest }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import { classNames } from "../../utils/classNames";
4
+ import styles from "./Button.module.css";
5
+ /**
6
+ * Universal Button Component
7
+ *
8
+ * A flexible, accessible button component that supports multiple variants
9
+ * (filled, outlined, text), color options (primary, secondary, inherited),
10
+ * sizes, states, and can render as either a button or anchor element.
11
+ */
12
+ export const Button = ({ variant = "filled", color = "inherited", size = "medium", disabled = false, fullWidth = false, children, className, onClick, href, type = "button", "aria-label": ariaLabel, ...rest }) => {
13
+ const buttonRef = useRef(null);
14
+ // Remove focus when button becomes disabled
15
+ useEffect(() => {
16
+ if (disabled &&
17
+ buttonRef.current &&
18
+ document.activeElement === buttonRef.current) {
19
+ buttonRef.current.blur();
20
+ }
21
+ }, [disabled]);
22
+ // Determine if button is interactive
23
+ const isInteractive = !disabled;
24
+ // Handle click events
25
+ const handleClick = (event) => {
26
+ if (!isInteractive) {
27
+ event.preventDefault();
28
+ return;
29
+ }
30
+ // Prevent navigation for disabled anchors
31
+ if (href && disabled) {
32
+ event.preventDefault();
33
+ return;
34
+ }
35
+ onClick?.(event);
36
+ };
37
+ // Handle keyboard events (Enter and Space)
38
+ const handleKeyDown = (event) => {
39
+ if (!isInteractive) {
40
+ event.preventDefault();
41
+ return;
42
+ }
43
+ // Enter and Space activate button
44
+ if (event.key === "Enter" || event.key === " ") {
45
+ if (event.key === " ") {
46
+ event.preventDefault(); // Prevent page scroll
47
+ }
48
+ if (href) {
49
+ // For anchors, trigger click
50
+ buttonRef.current?.click();
51
+ }
52
+ else {
53
+ // For buttons, trigger onClick
54
+ onClick?.(event);
55
+ }
56
+ }
57
+ };
58
+ // Compose className using classNames utility
59
+ const composedClassName = classNames(styles.root, styles[variant], styles[color], styles[size], disabled && styles.disabled, fullWidth && styles.fullWidth, className);
60
+ // ARIA attributes
61
+ const ariaAttributes = {
62
+ "aria-label": ariaLabel,
63
+ "aria-disabled": disabled ? true : undefined,
64
+ };
65
+ // Render as anchor if href is provided
66
+ if (href) {
67
+ return (_jsx("a", { ref: buttonRef, href: disabled ? undefined : href, className: composedClassName, onClick: handleClick, onKeyDown: handleKeyDown, ...ariaAttributes, ...rest, children: children }));
68
+ }
69
+ // Render as button
70
+ return (_jsx("button", { ref: buttonRef, type: type, disabled: disabled, className: composedClassName, onClick: handleClick, onKeyDown: handleKeyDown, ...ariaAttributes, ...rest, children: children }));
71
+ };
@@ -0,0 +1,2 @@
1
+ import json from './Button.module.css.json';
2
+ export default json;
@@ -0,0 +1,194 @@
1
+ import styleInject from 'style-inject';
2
+ import json from './Button.module.css.json';
3
+ styleInject(`
4
+ /* CSS Custom Properties - All defined on .root (Principle IX) */
5
+ ._root_1lt78_1 {
6
+ /* Font tokens */
7
+ --reltio-button-font-family: var(
8
+ --reltio-font-family-body,
9
+ "Roboto",
10
+ sans-serif
11
+ );
12
+ --reltio-button-font-size: var(--reltio-font-size-body, 16px);
13
+ --reltio-button-font-weight: var(--reltio-font-weight-body, 500);
14
+ --reltio-button-line-height: var(--reltio-line-height-body, 1.5);
15
+ --reltio-button-letter-spacing: var(--reltio-letter-spacing-body, 0.15px);
16
+
17
+ /* Color tokens */
18
+ --reltio-button-color-background: #f5f5fa;
19
+ --reltio-button-color-primary: var(--reltio-color-primary, #0000cc);
20
+ --reltio-button-color-primary-text: var(--reltio-color-on-primary, #ffffff);
21
+ --reltio-button-color-text: var(--reltio-color-text, #0e0e25);
22
+
23
+ /* Spacing tokens */
24
+ --reltio-button-padding-x-small: var(--reltio-spacing-sm, 16px);
25
+ --reltio-button-padding-y-small: var(--reltio-spacing-xs, 8px);
26
+ --reltio-button-padding-x-medium: var(--reltio-spacing-md, 20px);
27
+ --reltio-button-padding-y-medium: var(--reltio-spacing-sm, 12px);
28
+ --reltio-button-padding-x-large: var(--reltio-spacing-lg, 32px);
29
+ --reltio-button-padding-y-large: var(--reltio-spacing-md, 16px);
30
+ --reltio-button-gap-x: var(--reltio-spacing-xs, 8px);
31
+
32
+ /* Size tokens */
33
+ --reltio-button-height-small: var(--reltio-spacing-xl, 32px);
34
+ --reltio-button-height-medium: var(--reltio-spacing-2xl, 40px);
35
+ --reltio-button-height-large: var(--reltio-spacing-3xl, 48px);
36
+
37
+ /* Border and shape */
38
+ --reltio-button-border-radius: 9999px; /* Fully rounded corners (pill shape) - Material Design 3 */
39
+ --reltio-button-border-width: 0;
40
+
41
+ /* Transitions */
42
+ --reltio-button-transition-duration: 200ms;
43
+ --reltio-button-transition-timing: ease;
44
+
45
+ /* Disabled state */
46
+ --reltio-button-disabled-opacity: 0.38;
47
+ --reltio-button-disabled-cursor: not-allowed;
48
+
49
+ /* Base styles */
50
+ display: inline-flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ gap: var(--reltio-button-gap-x);
54
+ border: var(--reltio-button-border-width) solid transparent;
55
+ border-radius: var(--reltio-button-border-radius);
56
+ cursor: pointer;
57
+ font-family: var(--reltio-button-font-family);
58
+ font-size: var(--reltio-button-font-size);
59
+ font-weight: var(--reltio-button-font-weight);
60
+ line-height: var(--reltio-button-line-height);
61
+ letter-spacing: var(--reltio-button-letter-spacing);
62
+ text-align: center;
63
+ text-decoration: none;
64
+ white-space: nowrap;
65
+ user-select: none;
66
+ transition: all var(--reltio-button-transition-duration)
67
+ var(--reltio-button-transition-timing);
68
+ box-sizing: border-box;
69
+ /* Remove default button styles */
70
+ background: none;
71
+ margin: 0;
72
+ padding: 0;
73
+
74
+ /* Focus visible state */
75
+ &:focus-visible {
76
+ outline: 2px solid var(--reltio-button-color-primary);
77
+ outline-offset: 2px;
78
+ }
79
+
80
+ /* Disabled state */
81
+ &:disabled,
82
+ &[aria-disabled="true"] {
83
+ opacity: var(--reltio-button-disabled-opacity);
84
+ cursor: var(--reltio-button-disabled-cursor);
85
+ pointer-events: none;
86
+ }
87
+ }
88
+
89
+ /* Style: Filled - base structure */
90
+ ._filled_1lt78_87 {
91
+ border: none;
92
+ }
93
+
94
+ /* Style: Outlined - base structure */
95
+ ._outlined_1lt78_92 {
96
+ background-color: transparent;
97
+ border: 1px solid;
98
+ }
99
+
100
+ /* Style: Text - base structure */
101
+ ._text_1lt78_98 {
102
+ background-color: transparent;
103
+ border: none;
104
+ }
105
+
106
+ /* Filled + Primary */
107
+ ._filled_1lt78_87._primary_1lt78_104 {
108
+ background-color: var(--reltio-button-color-primary);
109
+ color: #fff;
110
+
111
+ &:hover {
112
+ background-color: #006;
113
+ }
114
+ }
115
+
116
+ /* Filled + Inherited */
117
+ ._filled_1lt78_87._inherited_1lt78_114 {
118
+ background-color: var(--reltio-button-color-background);
119
+ color: var(--reltio-button-color-text);
120
+ }
121
+
122
+ /* Outlined + Primary */
123
+ ._outlined_1lt78_92._primary_1lt78_104 {
124
+ border-color: var(--reltio-button-color-primary);
125
+ color: var(--reltio-button-color-primary);
126
+
127
+ &:hover {
128
+ background-color: var(--reltio-button-color-background);
129
+ }
130
+ }
131
+
132
+ /* Outlined + Inherited */
133
+ ._outlined_1lt78_92._inherited_1lt78_114 {
134
+ /* Border and text color inherit from parent */
135
+ border-color: currentColor;
136
+ color: inherit;
137
+
138
+ &:hover {
139
+ background-color: var(--reltio-button-color-background);
140
+ }
141
+ }
142
+
143
+ /* Text + Primary */
144
+ ._text_1lt78_98._primary_1lt78_104 {
145
+ color: var(--reltio-button-color-primary);
146
+
147
+ &:hover {
148
+ background-color: var(--reltio-button-color-background);
149
+ }
150
+ }
151
+
152
+ /* Text + Inherited */
153
+ ._text_1lt78_98._inherited_1lt78_114 {
154
+ color: inherit;
155
+
156
+ &:hover {
157
+ background-color: var(--reltio-button-color-background);
158
+ }
159
+ }
160
+
161
+ /* Size: Small */
162
+ ._small_1lt78_159 {
163
+ min-height: var(--reltio-button-height-small);
164
+ padding: var(--reltio-button-padding-y-small)
165
+ var(--reltio-button-padding-x-small);
166
+ font-size: 0.875rem;
167
+ }
168
+
169
+ /* Size: Medium */
170
+ ._medium_1lt78_167 {
171
+ min-height: var(--reltio-button-height-medium);
172
+ padding: var(--reltio-button-padding-y-medium)
173
+ var(--reltio-button-padding-x-medium);
174
+ }
175
+
176
+ /* Size: Large */
177
+ ._large_1lt78_174 {
178
+ min-height: var(--reltio-button-height-large);
179
+ padding: var(--reltio-button-padding-y-large)
180
+ var(--reltio-button-padding-x-large);
181
+ }
182
+
183
+ /* State: Disabled */
184
+ ._disabled_1lt78_181 {
185
+ opacity: var(--reltio-button-disabled-opacity);
186
+ cursor: var(--reltio-button-disabled-cursor);
187
+ }
188
+
189
+ /* Full Width */
190
+ ._fullWidth_1lt78_187 {
191
+ width: 100%;
192
+ }
193
+ `);
194
+ export default json;
@@ -0,0 +1 @@
1
+ { "root": "_root_1lt78_1", "filled": "_filled_1lt78_87", "outlined": "_outlined_1lt78_92", "text": "_text_1lt78_98", "primary": "_primary_1lt78_104", "inherited": "_inherited_1lt78_114", "small": "_small_1lt78_159", "medium": "_medium_1lt78_167", "large": "_large_1lt78_174", "disabled": "_disabled_1lt78_181", "fullWidth": "_fullWidth_1lt78_187" }
@@ -0,0 +1,109 @@
1
+ import type React from "react";
2
+ /**
3
+ * Visual variant for the button
4
+ */
5
+ export type ButtonVariant = "filled" | "outlined" | "text";
6
+ /**
7
+ * Color variant for the button
8
+ */
9
+ export type ButtonColor = "primary" | "inherited";
10
+ /**
11
+ * Size variant for the button
12
+ */
13
+ export type ButtonSize = "small" | "medium" | "large";
14
+ /**
15
+ * Base button props (without element-specific attributes)
16
+ */
17
+ type BaseButtonProps = {
18
+ /**
19
+ * Visual variant
20
+ * @default "filled"
21
+ */
22
+ variant?: ButtonVariant;
23
+ /**
24
+ * Color variant
25
+ * @default "inherited"
26
+ */
27
+ color?: ButtonColor;
28
+ /**
29
+ * Size variant
30
+ * @default "medium"
31
+ */
32
+ size?: ButtonSize;
33
+ /**
34
+ * Whether the button is disabled
35
+ * @default false
36
+ */
37
+ disabled?: boolean;
38
+ /**
39
+ * Whether the button should take full width of container
40
+ * @default false
41
+ */
42
+ fullWidth?: boolean;
43
+ /**
44
+ * Button content (text, icons, etc.)
45
+ */
46
+ children: React.ReactNode;
47
+ /**
48
+ * Additional CSS class names
49
+ */
50
+ className?: string;
51
+ /**
52
+ * Additional CSS variables
53
+ */
54
+ style?: React.CSSProperties & {
55
+ "--reltio-button-color-background"?: string;
56
+ "--reltio-button-color-text"?: string;
57
+ };
58
+ /**
59
+ * Click event handler
60
+ */
61
+ onClick?: React.MouseEventHandler<HTMLElement>;
62
+ /**
63
+ * Accessible label for screen readers
64
+ * Required if button has no visible text
65
+ */
66
+ "aria-label"?: string;
67
+ };
68
+ /**
69
+ * Button props when rendered as button element (no href)
70
+ */
71
+ type ButtonElementProps = BaseButtonProps & {
72
+ /**
73
+ * If href is provided, component renders as anchor element
74
+ * If href is not provided, component renders as button element
75
+ */
76
+ href?: never;
77
+ /**
78
+ * Button type (only applicable when rendered as button)
79
+ * @default "button"
80
+ */
81
+ type?: "button" | "submit" | "reset";
82
+ };
83
+ /**
84
+ * Anchor props when rendered as anchor element (with href)
85
+ */
86
+ type AnchorElementProps = BaseButtonProps & {
87
+ /**
88
+ * URL to navigate to (causes component to render as anchor)
89
+ */
90
+ href: string;
91
+ /**
92
+ * Button type is not applicable for anchor elements
93
+ */
94
+ type?: never;
95
+ /**
96
+ * Target attribute for anchor (e.g., "_blank")
97
+ */
98
+ target?: string;
99
+ /**
100
+ * Rel attribute for anchor (e.g., "noopener noreferrer")
101
+ */
102
+ rel?: string;
103
+ };
104
+ /**
105
+ * Union type for Button component props
106
+ * Component can be either a button or an anchor based on href prop
107
+ */
108
+ export type ButtonProps = (ButtonElementProps | AnchorElementProps) & Omit<React.ComponentPropsWithoutRef<"button">, keyof ButtonElementProps> & Omit<React.ComponentPropsWithoutRef<"a">, keyof AnchorElementProps>;
109
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from "./Button";
2
+ export type * from "./Button.types";
@@ -0,0 +1 @@
1
+ export * from "./Button";
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@reltio/design",
3
+ "version": "0.0.0-65",
4
+ "main": "packages/design/index.js",
5
+ "description": "Reltio Design System",
6
+ "peerDependencies": {
7
+ "react": "^17.0.2",
8
+ "react-dom": "^17.0.2"
9
+ },
10
+ "dependencies": {
11
+ "style-inject": "^0.3.0"
12
+ },
13
+ "scripts": {
14
+ "build": "node build-css.mjs && tsc && tsc-alias",
15
+ "postbuild": "cp package.json dist/ && cp README.md dist/"
16
+ },
17
+ "devDependencies": {
18
+ "@types/react": "^17.0.83",
19
+ "@types/react-dom": "^17.0.26",
20
+ "glob": "^11.0.0",
21
+ "postcss": "^8.4.49",
22
+ "postcss-modules": "^6.0.1",
23
+ "tsc-alias": "^1.8.10",
24
+ "typescript": "^5.7.2"
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ export * from "../../components/Button";
@@ -0,0 +1 @@
1
+ export * from "../../components/Button";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Combines CSS class names into a single string, handling BEM-like naming conventions.
3
+ * Filters out falsy values and ensures unique class names.
4
+ * For classes with suffixes (containing '__'), automatically adds the base class.
5
+ *
6
+ * @example
7
+ * classNames('a__b', false, 'c__d', 'c__e') // returns 'a a__b c c__d c__e'
8
+ *
9
+ * @param cssClasses - Array of CSS class names (strings) or falsy values
10
+ * @returns space-separated string of unique CSS class names
11
+ */
12
+ export declare const classNames: (...cssClasses: (string | undefined | null | false)[]) => string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Combines CSS class names into a single string, handling BEM-like naming conventions.
3
+ * Filters out falsy values and ensures unique class names.
4
+ * For classes with suffixes (containing '__'), automatically adds the base class.
5
+ *
6
+ * @example
7
+ * classNames('a__b', false, 'c__d', 'c__e') // returns 'a a__b c c__d c__e'
8
+ *
9
+ * @param cssClasses - Array of CSS class names (strings) or falsy values
10
+ * @returns space-separated string of unique CSS class names
11
+ */
12
+ export const classNames = (...cssClasses) => {
13
+ const uniqueClasses = new Set();
14
+ cssClasses.forEach((cssClass) => {
15
+ if (!cssClass)
16
+ return;
17
+ // Add the class itself
18
+ uniqueClasses.add(cssClass);
19
+ // If class contains a suffix (__), add the base class
20
+ const baseClass = cssClass.split("__")[0];
21
+ if (baseClass && baseClass !== cssClass) {
22
+ uniqueClasses.add(baseClass);
23
+ }
24
+ });
25
+ return Array.from(uniqueClasses).join(" ");
26
+ };