@cloud-ru/ds-ai-suggestion 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/LICENSE +201 -0
- package/README.md +226 -0
- package/dist/cjs/AiSuggestionParent/AiSuggestionParent.d.ts +7 -0
- package/dist/cjs/AiSuggestionParent/AiSuggestionParent.js +107 -0
- package/dist/cjs/AiSuggestionParent/AiSuggestionParentChip.d.ts +26 -0
- package/dist/cjs/AiSuggestionParent/AiSuggestionParentChip.js +24 -0
- package/dist/cjs/AiSuggestionParent/animation/AnimatedExpandableItem.d.ts +12 -0
- package/dist/cjs/AiSuggestionParent/animation/AnimatedExpandableItem.js +24 -0
- package/dist/cjs/AiSuggestionParent/animation/constants.d.ts +33 -0
- package/dist/cjs/AiSuggestionParent/animation/constants.js +69 -0
- package/dist/cjs/AiSuggestionParent/animation/styles.module.css +23 -0
- package/dist/cjs/AiSuggestionParent/constants.d.ts +9 -0
- package/dist/cjs/AiSuggestionParent/constants.js +12 -0
- package/dist/cjs/AiSuggestionParent/context/AiSuggestionParentGroupContext.d.ts +13 -0
- package/dist/cjs/AiSuggestionParent/context/AiSuggestionParentGroupContext.js +25 -0
- package/dist/cjs/AiSuggestionParent/index.d.ts +4 -0
- package/dist/cjs/AiSuggestionParent/index.js +24 -0
- package/dist/cjs/AiSuggestionParent/styles.module.css +156 -0
- package/dist/cjs/AiSuggestionParent/types.d.ts +44 -0
- package/dist/cjs/AiSuggestionParent/types.js +2 -0
- package/dist/cjs/AiSuggestionParent/utils/hasExclusiveParentGroup.d.ts +2 -0
- package/dist/cjs/AiSuggestionParent/utils/hasExclusiveParentGroup.js +7 -0
- package/dist/cjs/AiSuggestionParent/utils/itemGuards.d.ts +3 -0
- package/dist/cjs/AiSuggestionParent/utils/itemGuards.js +11 -0
- package/dist/cjs/AiSuggestionSimple/AiSuggestionSimple.d.ts +18 -0
- package/dist/cjs/AiSuggestionSimple/AiSuggestionSimple.js +17 -0
- package/dist/cjs/AiSuggestionSimple/constants.d.ts +18 -0
- package/dist/cjs/AiSuggestionSimple/constants.js +21 -0
- package/dist/cjs/AiSuggestionSimple/index.d.ts +3 -0
- package/dist/cjs/AiSuggestionSimple/index.js +24 -0
- package/dist/cjs/AiSuggestionSimple/styles.module.css +138 -0
- package/dist/cjs/AiSuggestionSimple/types.d.ts +5 -0
- package/dist/cjs/AiSuggestionSimple/types.js +2 -0
- package/dist/cjs/AiSuggestionSimple/utils.d.ts +2 -0
- package/dist/cjs/AiSuggestionSimple/utils.js +7 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +18 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/style.css +317 -0
- package/dist/esm/AiSuggestionParent/AiSuggestionParent.d.ts +7 -0
- package/dist/esm/AiSuggestionParent/AiSuggestionParent.js +101 -0
- package/dist/esm/AiSuggestionParent/AiSuggestionParentChip.d.ts +26 -0
- package/dist/esm/AiSuggestionParent/AiSuggestionParentChip.js +18 -0
- package/dist/esm/AiSuggestionParent/animation/AnimatedExpandableItem.d.ts +12 -0
- package/dist/esm/AiSuggestionParent/animation/AnimatedExpandableItem.js +17 -0
- package/dist/esm/AiSuggestionParent/animation/constants.d.ts +33 -0
- package/dist/esm/AiSuggestionParent/animation/constants.js +63 -0
- package/dist/esm/AiSuggestionParent/animation/styles.module.css +23 -0
- package/dist/esm/AiSuggestionParent/constants.d.ts +9 -0
- package/dist/esm/AiSuggestionParent/constants.js +9 -0
- package/dist/esm/AiSuggestionParent/context/AiSuggestionParentGroupContext.d.ts +13 -0
- package/dist/esm/AiSuggestionParent/context/AiSuggestionParentGroupContext.js +21 -0
- package/dist/esm/AiSuggestionParent/index.d.ts +4 -0
- package/dist/esm/AiSuggestionParent/index.js +4 -0
- package/dist/esm/AiSuggestionParent/styles.module.css +156 -0
- package/dist/esm/AiSuggestionParent/types.d.ts +44 -0
- package/dist/esm/AiSuggestionParent/types.js +1 -0
- package/dist/esm/AiSuggestionParent/utils/hasExclusiveParentGroup.d.ts +2 -0
- package/dist/esm/AiSuggestionParent/utils/hasExclusiveParentGroup.js +4 -0
- package/dist/esm/AiSuggestionParent/utils/itemGuards.d.ts +3 -0
- package/dist/esm/AiSuggestionParent/utils/itemGuards.js +7 -0
- package/dist/esm/AiSuggestionSimple/AiSuggestionSimple.d.ts +18 -0
- package/dist/esm/AiSuggestionSimple/AiSuggestionSimple.js +11 -0
- package/dist/esm/AiSuggestionSimple/constants.d.ts +18 -0
- package/dist/esm/AiSuggestionSimple/constants.js +18 -0
- package/dist/esm/AiSuggestionSimple/index.d.ts +3 -0
- package/dist/esm/AiSuggestionSimple/index.js +3 -0
- package/dist/esm/AiSuggestionSimple/styles.module.css +138 -0
- package/dist/esm/AiSuggestionSimple/types.d.ts +5 -0
- package/dist/esm/AiSuggestionSimple/types.js +1 -0
- package/dist/esm/AiSuggestionSimple/utils.d.ts +2 -0
- package/dist/esm/AiSuggestionSimple/utils.js +4 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/style.css +317 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +45 -0
- package/src/AiSuggestionParent/AiSuggestionParent.tsx +257 -0
- package/src/AiSuggestionParent/AiSuggestionParentChip.tsx +79 -0
- package/src/AiSuggestionParent/animation/AnimatedExpandableItem.tsx +53 -0
- package/src/AiSuggestionParent/animation/constants.ts +77 -0
- package/src/AiSuggestionParent/animation/styles.module.scss +25 -0
- package/src/AiSuggestionParent/constants.ts +10 -0
- package/src/AiSuggestionParent/context/AiSuggestionParentGroupContext.tsx +44 -0
- package/src/AiSuggestionParent/index.ts +4 -0
- package/src/AiSuggestionParent/styles.module.scss +197 -0
- package/src/AiSuggestionParent/types.ts +49 -0
- package/src/AiSuggestionParent/utils/hasExclusiveParentGroup.ts +6 -0
- package/src/AiSuggestionParent/utils/itemGuards.ts +10 -0
- package/src/AiSuggestionSimple/AiSuggestionSimple.tsx +72 -0
- package/src/AiSuggestionSimple/constants.ts +21 -0
- package/src/AiSuggestionSimple/index.ts +3 -0
- package/src/AiSuggestionSimple/styles.module.scss +166 -0
- package/src/AiSuggestionSimple/types.ts +7 -0
- package/src/AiSuggestionSimple/utils.ts +6 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export type AnimatedExpandableItemProps = {
|
|
3
|
+
index: number;
|
|
4
|
+
total: number;
|
|
5
|
+
shown: boolean;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
export declare function AnimatedExpandableItem({ index, total, shown, children }: AnimatedExpandableItemProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export type AnimatedTriggerWrapProps = {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
export declare function AnimatedTriggerWrap({ children }: AnimatedTriggerWrapProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { motion } from 'framer-motion';
|
|
4
|
+
import { GAP_PX, innerChipTransition, layoutSpring, outerChipTransition } from './constants.js';
|
|
5
|
+
import styles from './styles.module.css';
|
|
6
|
+
export function AnimatedExpandableItem({ index, total, shown, children }) {
|
|
7
|
+
return (_jsx(motion.div, { layout: 'position', initial: false, animate: {
|
|
8
|
+
width: shown ? 'auto' : 0,
|
|
9
|
+
marginRight: shown ? GAP_PX : 0,
|
|
10
|
+
}, transition: outerChipTransition(shown), className: styles.outerChip, "aria-hidden": !shown, children: _jsx(motion.div, { initial: false, animate: {
|
|
11
|
+
scale: shown ? 1 : 0,
|
|
12
|
+
opacity: shown ? 1 : 0,
|
|
13
|
+
}, transition: innerChipTransition(index, total, shown), className: cn(styles.innerChip, { [styles.innerChipHidden]: !shown }), children: children }) }));
|
|
14
|
+
}
|
|
15
|
+
export function AnimatedTriggerWrap({ children }) {
|
|
16
|
+
return (_jsx(motion.div, { layout: 'position', transition: { layout: layoutSpring }, className: styles.triggerWrap, children: children }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Transition } from 'framer-motion';
|
|
2
|
+
/**
|
|
3
|
+
* Animation contract — двухслойный chip + FLIP-spring layout (1:1 из ai-components).
|
|
4
|
+
*
|
|
5
|
+
* Per-property timing:
|
|
6
|
+
* width / marginRight / scale → sync, одна duration, один easing.
|
|
7
|
+
* opacity → со stagger (visual ripple).
|
|
8
|
+
* layout (FLIP) → spring (110, 22, 1).
|
|
9
|
+
*
|
|
10
|
+
* Длительности:
|
|
11
|
+
* enter width/scale → 650ms opacity → 450ms, stagger 60ms
|
|
12
|
+
* exit width/scale → 550ms opacity → 280ms, stagger 80ms
|
|
13
|
+
*/
|
|
14
|
+
/** easings:
|
|
15
|
+
* enter — easeOutQuart
|
|
16
|
+
* exit — easeInQuart
|
|
17
|
+
* bubble — easeOutBack overshoot ~5% для inner scale
|
|
18
|
+
*/
|
|
19
|
+
export declare const ENTER_EASE: Transition['ease'];
|
|
20
|
+
export declare const EXIT_EASE: Transition['ease'];
|
|
21
|
+
export declare const BUBBLE_ENTER_EASE: Transition['ease'];
|
|
22
|
+
export declare const ENTER_WIDTH_S = 0.65;
|
|
23
|
+
export declare const ENTER_OPACITY_S = 0.45;
|
|
24
|
+
export declare const EXIT_OPACITY_S = 0.28;
|
|
25
|
+
export declare const EXIT_WIDTH_S = 0.55;
|
|
26
|
+
export declare const STAGGER_FORWARD_OPACITY_S = 0.06;
|
|
27
|
+
export declare const STAGGER_BACKWARD_OPACITY_S = 0.08;
|
|
28
|
+
export declare const MAX_STAGGER_WINDOW_S = 0.5;
|
|
29
|
+
export declare const GAP_PX = 4;
|
|
30
|
+
export declare const layoutSpring: Transition;
|
|
31
|
+
export declare const cappedStagger: (idealPerChip: number, total: number, index: number) => number;
|
|
32
|
+
export declare const outerChipTransition: (activated: boolean) => Transition;
|
|
33
|
+
export declare const innerChipTransition: (index: number, total: number, activated: boolean) => Transition;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation contract — двухслойный chip + FLIP-spring layout (1:1 из ai-components).
|
|
3
|
+
*
|
|
4
|
+
* Per-property timing:
|
|
5
|
+
* width / marginRight / scale → sync, одна duration, один easing.
|
|
6
|
+
* opacity → со stagger (visual ripple).
|
|
7
|
+
* layout (FLIP) → spring (110, 22, 1).
|
|
8
|
+
*
|
|
9
|
+
* Длительности:
|
|
10
|
+
* enter width/scale → 650ms opacity → 450ms, stagger 60ms
|
|
11
|
+
* exit width/scale → 550ms opacity → 280ms, stagger 80ms
|
|
12
|
+
*/
|
|
13
|
+
/** easings:
|
|
14
|
+
* enter — easeOutQuart
|
|
15
|
+
* exit — easeInQuart
|
|
16
|
+
* bubble — easeOutBack overshoot ~5% для inner scale
|
|
17
|
+
*/
|
|
18
|
+
export const ENTER_EASE = [0.22, 1, 0.36, 1];
|
|
19
|
+
export const EXIT_EASE = [0.64, 0, 0.78, 0];
|
|
20
|
+
export const BUBBLE_ENTER_EASE = [0.34, 1.5, 0.64, 1];
|
|
21
|
+
export const ENTER_WIDTH_S = 0.65;
|
|
22
|
+
export const ENTER_OPACITY_S = 0.45;
|
|
23
|
+
export const EXIT_OPACITY_S = 0.28;
|
|
24
|
+
export const EXIT_WIDTH_S = 0.55;
|
|
25
|
+
export const STAGGER_FORWARD_OPACITY_S = 0.06;
|
|
26
|
+
export const STAGGER_BACKWARD_OPACITY_S = 0.08;
|
|
27
|
+
export const MAX_STAGGER_WINDOW_S = 0.5;
|
|
28
|
+
export const GAP_PX = 4;
|
|
29
|
+
export const layoutSpring = {
|
|
30
|
+
type: 'spring',
|
|
31
|
+
stiffness: 110,
|
|
32
|
+
damping: 22,
|
|
33
|
+
mass: 1,
|
|
34
|
+
};
|
|
35
|
+
export const cappedStagger = (idealPerChip, total, index) => {
|
|
36
|
+
if (total <= 1) {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
const perChip = Math.min(idealPerChip, MAX_STAGGER_WINDOW_S / (total - 1));
|
|
40
|
+
return index * perChip;
|
|
41
|
+
};
|
|
42
|
+
const sizeTransition = (activated) => ({
|
|
43
|
+
duration: activated ? ENTER_WIDTH_S : EXIT_WIDTH_S,
|
|
44
|
+
ease: activated ? ENTER_EASE : EXIT_EASE,
|
|
45
|
+
});
|
|
46
|
+
export const outerChipTransition = (activated) => ({
|
|
47
|
+
width: sizeTransition(activated),
|
|
48
|
+
marginRight: sizeTransition(activated),
|
|
49
|
+
layout: layoutSpring,
|
|
50
|
+
});
|
|
51
|
+
export const innerChipTransition = (index, total, activated) => ({
|
|
52
|
+
scale: {
|
|
53
|
+
duration: activated ? ENTER_WIDTH_S : EXIT_WIDTH_S,
|
|
54
|
+
ease: activated ? BUBBLE_ENTER_EASE : EXIT_EASE,
|
|
55
|
+
},
|
|
56
|
+
opacity: {
|
|
57
|
+
duration: activated ? ENTER_OPACITY_S : EXIT_OPACITY_S,
|
|
58
|
+
ease: activated ? ENTER_EASE : EXIT_EASE,
|
|
59
|
+
delay: activated
|
|
60
|
+
? cappedStagger(STAGGER_FORWARD_OPACITY_S, total, index)
|
|
61
|
+
: cappedStagger(STAGGER_BACKWARD_OPACITY_S, total, total - 1 - index),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.outerChip{
|
|
2
|
+
contain:layout;
|
|
3
|
+
display:inline-flex;
|
|
4
|
+
flex-shrink:0;
|
|
5
|
+
align-items:center;
|
|
6
|
+
justify-content:center;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.innerChip{
|
|
10
|
+
will-change:transform, opacity;
|
|
11
|
+
transform-origin:center;
|
|
12
|
+
display:inline-flex;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.innerChipHidden{
|
|
16
|
+
pointer-events:none;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.triggerWrap{
|
|
20
|
+
display:inline-flex;
|
|
21
|
+
flex-shrink:0;
|
|
22
|
+
margin-right:var(--sn-primitive-dimension-4, 4px);
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const CHILD_TYPE: {
|
|
2
|
+
readonly Suggestion: "suggestion";
|
|
3
|
+
readonly Parent: "parent";
|
|
4
|
+
};
|
|
5
|
+
export declare const TEST_IDS: {
|
|
6
|
+
readonly root: "ai-suggestion-parent";
|
|
7
|
+
readonly trigger: "ai-suggestion-parent__trigger";
|
|
8
|
+
readonly chevron: "ai-suggestion-parent__chevron";
|
|
9
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
type AiSuggestionParentGroupContextValue = {
|
|
3
|
+
expandedKey: string | null;
|
|
4
|
+
requestExpand(key: string): void;
|
|
5
|
+
collapseKey(key: string): void;
|
|
6
|
+
};
|
|
7
|
+
export type AiSuggestionParentGroupProviderProps = {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
initialExpandedKey?: string | null;
|
|
10
|
+
};
|
|
11
|
+
export declare function AiSuggestionParentGroupProvider({ children, initialExpandedKey, }: AiSuggestionParentGroupProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function useAiSuggestionParentGroup(): AiSuggestionParentGroupContextValue | null;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
|
3
|
+
const AiSuggestionParentGroupContext = createContext(null);
|
|
4
|
+
export function AiSuggestionParentGroupProvider({ children, initialExpandedKey = null, }) {
|
|
5
|
+
const [expandedKey, setExpandedKey] = useState(initialExpandedKey);
|
|
6
|
+
const requestExpand = useCallback((key) => {
|
|
7
|
+
setExpandedKey(prev => (prev === key ? null : key));
|
|
8
|
+
}, []);
|
|
9
|
+
const collapseKey = useCallback((key) => {
|
|
10
|
+
setExpandedKey(prev => (prev === key ? null : prev));
|
|
11
|
+
}, []);
|
|
12
|
+
const value = useMemo(() => ({
|
|
13
|
+
expandedKey,
|
|
14
|
+
requestExpand,
|
|
15
|
+
collapseKey,
|
|
16
|
+
}), [collapseKey, expandedKey, requestExpand]);
|
|
17
|
+
return _jsx(AiSuggestionParentGroupContext.Provider, { value: value, children: children });
|
|
18
|
+
}
|
|
19
|
+
export function useAiSuggestionParentGroup() {
|
|
20
|
+
return useContext(AiSuggestionParentGroupContext);
|
|
21
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
.root{
|
|
2
|
+
display:flex;
|
|
3
|
+
flex-wrap:wrap;
|
|
4
|
+
row-gap:var(--sn-primitive-dimension-4, 4px);
|
|
5
|
+
align-items:center;
|
|
6
|
+
min-width:0;
|
|
7
|
+
max-width:100%;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.root[data-flatten]{
|
|
11
|
+
display:contents;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.label{
|
|
15
|
+
overflow:hidden;
|
|
16
|
+
text-overflow:ellipsis;
|
|
17
|
+
white-space:nowrap;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.icon{
|
|
21
|
+
color:currentcolor;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.chevron{
|
|
25
|
+
display:flex;
|
|
26
|
+
flex-shrink:0;
|
|
27
|
+
align-items:center;
|
|
28
|
+
justify-content:center;
|
|
29
|
+
width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
|
|
30
|
+
height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
|
|
31
|
+
color:currentcolor;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.chip{
|
|
35
|
+
cursor:pointer;
|
|
36
|
+
position:relative;
|
|
37
|
+
display:inline-flex;
|
|
38
|
+
flex-shrink:0;
|
|
39
|
+
align-items:center;
|
|
40
|
+
justify-content:center;
|
|
41
|
+
box-sizing:border-box;
|
|
42
|
+
margin:0;
|
|
43
|
+
padding:0;
|
|
44
|
+
border:var(--sn-primitive-strokeWeight-strokeRegular, 1px) solid var(--sn-theme-color-material-stateLayer-neutral-border-default-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-default-borderColor, rgba(159, 166, 198, 0.3019607843)));
|
|
45
|
+
border-radius:var(--sn-primitive-dimension-full, 10000px);
|
|
46
|
+
font-family:inherit;
|
|
47
|
+
text-decoration:none;
|
|
48
|
+
background-color:var(--sn-theme-color-neutral-background1Level, var(--sn-brand-color-neutral-95, var(--sn-primitive-color-gray-95, #fdfdfd)));
|
|
49
|
+
outline:0;
|
|
50
|
+
outline-offset:0;
|
|
51
|
+
transition:background-color 450ms cubic-bezier(1, 0, 0.26, 0.7), border-color 450ms cubic-bezier(1, 0, 0.26, 0.7), color 450ms cubic-bezier(1, 0, 0.26, 0.7), outline-color 120ms ease;
|
|
52
|
+
}
|
|
53
|
+
.chip .icon,
|
|
54
|
+
.chip .label,
|
|
55
|
+
.chip .chevron{
|
|
56
|
+
color:var(--sn-theme-color-neutral-text, var(--sn-brand-color-neutral-45, var(--sn-primitive-color-gray-45, #81869c)));
|
|
57
|
+
}
|
|
58
|
+
.chip:disabled{
|
|
59
|
+
cursor:not-allowed;
|
|
60
|
+
opacity:var(--sn-theme-effect-opacity-disabled, 0.4);
|
|
61
|
+
}
|
|
62
|
+
.chip:not(:disabled):focus-visible{
|
|
63
|
+
outline:var(--sn-primitive-strokeWeight-strokeSemiBold, 2px) solid var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
|
|
64
|
+
outline-offset:0;
|
|
65
|
+
}
|
|
66
|
+
.chip[data-activated]{
|
|
67
|
+
border-color:var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
|
|
68
|
+
background-color:var(--sn-theme-color-primary-background1Level, var(--sn-brand-color-primary-95, var(--sn-brandRole-color-brandA-95, var(--sn-primitive-color-brandA-95, #f5fdf8))));
|
|
69
|
+
transition:background-color 450ms cubic-bezier(0.7, 0.26, 0, 1.58), border-color 450ms cubic-bezier(0.7, 0.26, 0, 1.58), color 450ms cubic-bezier(0.7, 0.26, 0, 1.58), outline-color 120ms ease;
|
|
70
|
+
}
|
|
71
|
+
.chip[data-activated] .icon,
|
|
72
|
+
.chip[data-activated] .label,
|
|
73
|
+
.chip[data-activated] .chevron{
|
|
74
|
+
color:var(--sn-theme-color-primary-text, var(--sn-brand-color-primary-45, var(--sn-brandRole-color-brandA-45, var(--sn-primitive-color-brandA-45, #22775b))));
|
|
75
|
+
}
|
|
76
|
+
@media (hover: hover){
|
|
77
|
+
.chip[data-activated]:hover:not(:disabled){
|
|
78
|
+
border-color:color-mix(in srgb, var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74)))) 80%, transparent);
|
|
79
|
+
background-color:var(--sn-theme-color-primary-background2Level, var(--sn-brand-color-primary-99, var(--sn-brandRole-color-brandA-99, var(--sn-primitive-color-brandA-99, #fbfffc))));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
@media (hover: hover){
|
|
83
|
+
.chip:not([data-activated], :disabled):hover{
|
|
84
|
+
border-color:var(--sn-theme-color-material-stateLayer-neutral-border-hovered-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-hovered-borderColor, rgba(159, 166, 198, 0.6509803922)));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
.chip[data-size=s]{
|
|
88
|
+
font-family:var(--sn-regular-label-m-fontFamily, var(--sn-brand-font-fontFamily-regular-label, var(--sn-primitive-fontFamily-brandA-sans, SB Sans Interface)));
|
|
89
|
+
font-weight:var(--sn-regular-label-m-fontWeight, var(--sn-theme-typography-fontWeight-regular-label-m, var(--sn-brand-font-fontWeight-light-regular-label-m, var(--sn-primitive-font-fontWeightValue-550, 550))));
|
|
90
|
+
line-height:var(--sn-regular-label-m-lineHeight, var(--sn-density-typography-roleLabel-lineHeightM, var(--sn-primitive-font-lineHeight-by16, 24px)));
|
|
91
|
+
font-size:var(--sn-regular-label-m-fontSize, var(--sn-density-typography-roleLabel-fontSizeM, var(--sn-primitive-font-fontSize-16, 16px)));
|
|
92
|
+
letter-spacing:var(--sn-regular-label-m-letterSpacing, var(--sn-primitive-font-letterSpacing-s, 0px));
|
|
93
|
+
paragraph-spacing:var(--sn-regular-label-m-paragraphSpacing, var(--sn-primitive-font-paragraphSpacing-byFontSize12, 6px));
|
|
94
|
+
height:var(--sn-primitive-dimension-32, 32px);
|
|
95
|
+
padding-inline:var(--sn-primitive-dimension-4, 4px);
|
|
96
|
+
}
|
|
97
|
+
.chip[data-size=s] .icon{
|
|
98
|
+
display:flex;
|
|
99
|
+
flex-shrink:0;
|
|
100
|
+
align-items:center;
|
|
101
|
+
justify-content:center;
|
|
102
|
+
width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
|
|
103
|
+
height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
|
|
104
|
+
}
|
|
105
|
+
.chip[data-size=s] .icon > svg{
|
|
106
|
+
display:block;
|
|
107
|
+
width:100%;
|
|
108
|
+
height:100%;
|
|
109
|
+
}
|
|
110
|
+
.chip[data-size=s] .labelContainer{
|
|
111
|
+
padding-inline:var(--sn-primitive-dimension-4, 4px) var(--sn-primitive-dimension-0, 0);
|
|
112
|
+
}
|
|
113
|
+
.chip[data-size=m]{
|
|
114
|
+
font-family:var(--sn-regular-label-l-fontFamily, var(--sn-brand-font-fontFamily-regular-label, var(--sn-primitive-fontFamily-brandA-sans, SB Sans Interface)));
|
|
115
|
+
font-weight:var(--sn-regular-label-l-fontWeight, var(--sn-theme-typography-fontWeight-regular-label-l, var(--sn-brand-font-fontWeight-light-regular-label-l, var(--sn-primitive-font-fontWeightValue-550, 550))));
|
|
116
|
+
line-height:var(--sn-regular-label-l-lineHeight, var(--sn-density-typography-roleLabel-lineHeightL, var(--sn-primitive-font-lineHeight-by20, 28px)));
|
|
117
|
+
font-size:var(--sn-regular-label-l-fontSize, var(--sn-density-typography-roleLabel-fontSizeL, var(--sn-primitive-font-fontSize-20, 20px)));
|
|
118
|
+
letter-spacing:var(--sn-regular-label-l-letterSpacing, var(--sn-primitive-font-letterSpacing-s, 0px));
|
|
119
|
+
paragraph-spacing:var(--sn-regular-label-l-paragraphSpacing, var(--sn-primitive-font-paragraphSpacing-byFontSize14, 7px));
|
|
120
|
+
height:var(--sn-primitive-dimension-40, 40px);
|
|
121
|
+
padding-inline:var(--sn-primitive-dimension-8, 8px);
|
|
122
|
+
}
|
|
123
|
+
.chip[data-size=m] .icon{
|
|
124
|
+
display:flex;
|
|
125
|
+
flex-shrink:0;
|
|
126
|
+
align-items:center;
|
|
127
|
+
justify-content:center;
|
|
128
|
+
width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
|
|
129
|
+
height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
|
|
130
|
+
}
|
|
131
|
+
.chip[data-size=m] .icon > svg{
|
|
132
|
+
display:block;
|
|
133
|
+
width:100%;
|
|
134
|
+
height:100%;
|
|
135
|
+
}
|
|
136
|
+
.chip[data-size=m] .labelContainer{
|
|
137
|
+
padding-inline:var(--sn-primitive-dimension-8, 8px) var(--sn-primitive-dimension-2, 2px);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.labelContainer{
|
|
141
|
+
display:inline-flex;
|
|
142
|
+
align-items:center;
|
|
143
|
+
justify-content:center;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.chevronIcon{
|
|
147
|
+
display:block;
|
|
148
|
+
max-width:100%;
|
|
149
|
+
max-height:100%;
|
|
150
|
+
transition:transform 450ms cubic-bezier(1, 0, 0.26, 0.7);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.chip[data-activated] .chevronIcon{
|
|
154
|
+
transform:rotate(90deg);
|
|
155
|
+
transition:transform 450ms cubic-bezier(0.7, 0.26, 0, 1.58);
|
|
156
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WithSupportProps } from '@cloud-ru/ds-utils';
|
|
2
|
+
import { MouseEvent, ReactNode } from 'react';
|
|
3
|
+
import { Size } from '../AiSuggestionSimple/types.js';
|
|
4
|
+
import { CHILD_TYPE } from './constants.js';
|
|
5
|
+
export type AiSuggestionParentProps = WithSupportProps<{
|
|
6
|
+
/** Текст на триггере */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Иконка слева от текста */
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
/** Размер (Figma: Mobile Off → `s`, Mobile On → `m`) */
|
|
11
|
+
size?: Size;
|
|
12
|
+
/** Блокирует взаимодействие */
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
/** Controlled — раскрыт (Figma: Activated=On) */
|
|
15
|
+
expanded?: boolean;
|
|
16
|
+
/** Uncontrolled — initial expanded */
|
|
17
|
+
defaultExpanded?: boolean;
|
|
18
|
+
/** Колбэк toggle (controlled и uncontrolled) */
|
|
19
|
+
onExpandedChange?(expanded: boolean): void;
|
|
20
|
+
/** Вложенные подсказки и группы */
|
|
21
|
+
items?: AiSuggestionParentItem[];
|
|
22
|
+
/** Колбэк клика по вложенной подсказке */
|
|
23
|
+
onItemClick?(index: number, event: MouseEvent<HTMLButtonElement>): void;
|
|
24
|
+
/** Дополнительный CSS-класс */
|
|
25
|
+
className?: string;
|
|
26
|
+
}>;
|
|
27
|
+
export type AiSuggestionParentSuggestionItem = {
|
|
28
|
+
type?: typeof CHILD_TYPE.Suggestion;
|
|
29
|
+
key?: string;
|
|
30
|
+
items?: never;
|
|
31
|
+
label?: string;
|
|
32
|
+
icon?: ReactNode;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
onClick?(event: MouseEvent<HTMLButtonElement>): void;
|
|
35
|
+
};
|
|
36
|
+
export type AiSuggestionParentNestedItem = {
|
|
37
|
+
type?: typeof CHILD_TYPE.Parent;
|
|
38
|
+
key?: string;
|
|
39
|
+
label?: string;
|
|
40
|
+
icon?: ReactNode;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
items?: AiSuggestionParentItem[];
|
|
43
|
+
};
|
|
44
|
+
export type AiSuggestionParentItem = AiSuggestionParentSuggestionItem | AiSuggestionParentNestedItem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { AiSuggestionParentItem, AiSuggestionParentNestedItem, AiSuggestionParentSuggestionItem } from '../types.js';
|
|
2
|
+
export declare function isNestedItem(item: AiSuggestionParentItem): item is AiSuggestionParentNestedItem;
|
|
3
|
+
export declare function isSuggestionItem(item: AiSuggestionParentItem): item is AiSuggestionParentSuggestionItem;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { WithSupportProps } from '@cloud-ru/ds-utils';
|
|
2
|
+
import { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { Appearance, Size } from './types.js';
|
|
4
|
+
export type AiSuggestionSimpleProps = WithSupportProps<{
|
|
5
|
+
/** Текст подсказки */
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Иконка слева от текста */
|
|
8
|
+
icon?: ReactNode;
|
|
9
|
+
/** Внешний вид (Figma: Primary On/Off) */
|
|
10
|
+
appearance?: Appearance;
|
|
11
|
+
/** Размер (Figma: Mobile Off → `s`, Mobile On → `m`) */
|
|
12
|
+
size?: Size;
|
|
13
|
+
/** Блокирует взаимодействие */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Дополнительный CSS-класс */
|
|
16
|
+
className?: string;
|
|
17
|
+
}> & Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'tabIndex' | 'onClick'>;
|
|
18
|
+
export declare function AiSuggestionSimple({ label, icon, appearance, size, disabled, tabIndex, onClick, className, ...rest }: AiSuggestionSimpleProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { extractSupportProps } from '@cloud-ru/ds-utils';
|
|
3
|
+
import cn from 'classnames';
|
|
4
|
+
import { APPEARANCE, SIZE, TEST_IDS } from './constants.js';
|
|
5
|
+
import styles from './styles.module.css';
|
|
6
|
+
import { getLayerState } from './utils.js';
|
|
7
|
+
export function AiSuggestionSimple({ label = 'Label text', icon, appearance = APPEARANCE.Neutral, size = SIZE.S, disabled = false, tabIndex, onClick, className, ...rest }) {
|
|
8
|
+
const layerState = getLayerState(appearance);
|
|
9
|
+
const rootTestId = rest['data-test-id'] ?? TEST_IDS.root;
|
|
10
|
+
return (_jsxs("button", { type: 'button', className: cn(styles.chip, className), ...extractSupportProps(rest), "data-size": size, "data-appearance": appearance, "data-disabled": disabled || undefined, "data-test-id": rootTestId, disabled: disabled, tabIndex: tabIndex, onClick: disabled ? undefined : onClick, children: [_jsx("span", { className: styles.stateLayer, "aria-hidden": true, "data-state": layerState }), _jsxs("span", { className: styles.content, children: [icon && (_jsx("span", { className: styles.icon, "data-test-id": TEST_IDS.icon, children: icon })), label && (_jsx("span", { className: styles.labelContainer, children: _jsx("span", { className: styles.label, "data-test-id": TEST_IDS.label, children: label }) }))] })] }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const SIZE: {
|
|
2
|
+
readonly S: "s";
|
|
3
|
+
readonly M: "m";
|
|
4
|
+
};
|
|
5
|
+
export declare const APPEARANCE: {
|
|
6
|
+
readonly Primary: "primary";
|
|
7
|
+
readonly Neutral: "neutral";
|
|
8
|
+
};
|
|
9
|
+
export declare const TEST_IDS: {
|
|
10
|
+
readonly root: "ai-suggestion-simple";
|
|
11
|
+
readonly chip: "ai-suggestion-simple__chip";
|
|
12
|
+
readonly icon: "ai-suggestion-simple__icon";
|
|
13
|
+
readonly label: "ai-suggestion-simple__label";
|
|
14
|
+
};
|
|
15
|
+
export declare const LAYER_STATE: {
|
|
16
|
+
readonly ActivatedOnBackground: "activatedOnBackground";
|
|
17
|
+
readonly EmptyNeutralOnBackground: "emptyNeutralOnBackground";
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const SIZE = {
|
|
2
|
+
S: 's',
|
|
3
|
+
M: 'm',
|
|
4
|
+
};
|
|
5
|
+
export const APPEARANCE = {
|
|
6
|
+
Primary: 'primary',
|
|
7
|
+
Neutral: 'neutral',
|
|
8
|
+
};
|
|
9
|
+
export const TEST_IDS = {
|
|
10
|
+
root: 'ai-suggestion-simple',
|
|
11
|
+
chip: 'ai-suggestion-simple__chip',
|
|
12
|
+
icon: 'ai-suggestion-simple__icon',
|
|
13
|
+
label: 'ai-suggestion-simple__label',
|
|
14
|
+
};
|
|
15
|
+
export const LAYER_STATE = {
|
|
16
|
+
ActivatedOnBackground: 'activatedOnBackground',
|
|
17
|
+
EmptyNeutralOnBackground: 'emptyNeutralOnBackground',
|
|
18
|
+
};
|