antd#Anchor TypeScript Examples
The following examples show how to use
antd#Anchor.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DendronTOC.tsx From dendron with GNU Affero General Public License v3.0 | 6 votes |
DendronTOC = ({
note,
...rest
}: {
note: NoteProps;
} & ComponentProps<typeof Anchor>) => {
return (
<>
<Anchor style={{ zIndex: 1 }} className="dendron-toc" {...rest}>
{Object.entries(note?.anchors).map(([key, entry]) =>
entry?.type === "header" ? (
<Link
key={key}
href={`#${key}`}
// `anchor.text` contains clean, user displayable text for
// headings. It should always exist for exported notes, but we
// have this fallback just in case.
title={entry?.text ?? unslug(entry?.value)}
/>
) : (
<></>
)
)}
</Anchor>
</>
);
}
Example #2
Source File: DendronTOC.tsx From dendron with GNU Affero General Public License v3.0 | 5 votes |
Link = Anchor.Link
Example #3
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
{ Link } = Anchor
Example #4
Source File: index.tsx From erda-ui with GNU Affero General Public License v3.0 | 5 votes |
DetailsPanel = (props: IProps) => {
const { baseInfoConf, linkList, anchorContainer, children } = props;
const _baseInfoConf = {
...baseInfoConf,
titleProps: {
title: baseInfoConf?.title,
level: 2,
...baseInfoConf?.titleProps,
},
};
const container = React.useRef(anchorContainer || (document.getElementById('main') as HTMLDivElement));
return (
<div className="details-panel-template">
<IF check={!isEmpty(baseInfoConf)}>
<div className="base-info mb-3">
<Content {...(_baseInfoConf as IContentProps)} />
</div>
</IF>
{children}
<IF check={!isEmpty(linkList)}>
<Anchor getContainer={() => container?.current || document.body}>
{map(linkList, (item) => {
const { linkProps, key } = item;
const { icon, title } = linkProps;
const href = `#${key}`;
return (
<Link
href={href}
title={
<div className="anchor-link-title-icon">
{icon}
<span className="flex items-center">{title}</span>
</div>
}
key={href}
/>
);
})}
</Anchor>
{map(linkList, (item) => {
const { linkProps, crossLine, titleProps, showTitle, panelProps, getComp, key } = item;
const { icon, title } = linkProps;
const _titleProps = {
title,
level: 2,
icon: <div className="title-icon">{icon}</div>,
...titleProps,
};
return (
<div key={key} id={key} className="pt-3">
<Content
crossLine={crossLine}
titleProps={_titleProps}
panelProps={panelProps}
getComp={getComp}
showTitle={showTitle}
/>
</div>
);
})}
</IF>
</div>
);
}
Example #5
Source File: header-renderer.tsx From electron-playground with MIT License | 5 votes |
{ Link } = Anchor
Example #6
Source File: header-renderer.tsx From electron-playground with MIT License | 5 votes |
AnchorRender = () => {
const [show, setShow] = useState<boolean>(false)
useEffect(() => {
return () => {
TOC = []
}
}, [])
const handleAnchor = (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => {
const dom = document.getElementById(link.href)
if (dom) {
dom.scrollIntoView({ behavior: 'smooth' })
dom.scrollTop -= 60
}
e.preventDefault()
}
return (
<>
<div className={style.contents} onMouseOver={() => setShow(true)}>
<LeftOutlined className={style.icon} />
目录
</div>
<Drawer title={null} placement='right' closable={false} mask={false} visible={show}>
<div onMouseLeave={() => setShow(false)} style={{height:'100%'}}>
<Anchor onClick={handleAnchor} bounds={0}>
{TOC.map(({ content, level }) => (
<div key={content} style={{ marginLeft: (level - 1) * 10 }}>
<Link href={content} title={content} key={content} />
</div>
))}
</Anchor>
</div>
</Drawer>
</>
)
}
Example #7
Source File: index.tsx From amiya with MIT License | 5 votes |
{ Link } = Anchor
Example #8
Source File: GeneralAnchor.spec.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
describe("GeneralAnchor", () => {
it("should work", () => {
const { Link } = Anchor;
const wrapper = mount(<GeneralAnchor anchorList={linkList} />);
expect(wrapper.find(Anchor).length).toBe(1);
expect(wrapper.find(Anchor).prop("offsetTop")).toBe(56);
wrapper.setProps({
type: "radio",
});
wrapper.update();
expect(wrapper.find("div.anchorWrapper").length).toBe(1);
expect(wrapper.find("div.anchorContainer").length).toBe(1);
expect(wrapper.find("div.anchorLinkContainer").length).toBe(1);
expect(wrapper.find(Link).length).toBe(3);
});
it("should work and type ", () => {
const linkListWidthChild = [
{
title: "应用资源",
href: "https://192.168.100.162/next/resource-monitor#saas-monitor",
children: [
{
title: "平台资源",
href: "https://192.168.100.162/next/resource-monitor#paas-monitor",
},
],
},
{
title: "基础设施",
href: "https://192.168.100.162/next/resource-monitor#iaas-monitor",
},
];
const { Link } = Anchor;
const wrapper = mount(
<GeneralAnchor anchorList={linkListWidthChild} type="default" />
);
expect(wrapper.find(Link).length).toBe(3);
});
it("should work and hasExtraSlot ", () => {
const extraBrick = {
useBrick: [
{
brick: "span",
properties: {
textContent: "测试",
},
},
],
};
const wrapper = shallow(
<GeneralAnchor anchorList={linkList} extraBrick={extraBrick} />
);
expect(wrapper.find(".extraContainer").length).toBe(1);
wrapper.setProps({
configProps: {
affix: true,
},
});
wrapper.update();
expect(wrapper.find(Anchor).prop("affix")).toBe(true);
});
it("handleClick and handleChange should work", () => {
const handleChange = jest.fn();
let result;
const handleClick = (
_e: React.MouseEvent<HTMLElement, MouseEvent>,
detail: Record<string, any>
) => {
result = detail;
};
const linkListWidthChild = [
{
title: "应用资源",
href: "https://192.168.100.162/next/resource-monitor#saas-monitor",
balabala: 123,
},
{
title: "基础设施",
href: "https://192.168.100.162/next/resource-monitor#iaas-monitor",
},
];
const { Link } = Anchor;
const wrapper = mount(
<GeneralAnchor
anchorList={linkListWidthChild}
type="default"
handleClick={handleClick}
handleChange={handleChange}
/>
);
wrapper.find(Link).at(0).simulate("click");
expect(handleChange).toBeCalled();
expect(result).toEqual({
balabala: 123,
href: "https://192.168.100.162/next/resource-monitor#saas-monitor",
title: "应用资源",
});
});
});
Example #9
Source File: GeneralAnchor.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function GeneralAnchor(props: GeneralAnchorProps): React.ReactElement {
const { configProps, type, extraBrick, handleClick, handleChange } = props;
const { Link } = Anchor;
const getHref = (hash: string) => {
const isHash = (hash || "").startsWith("#");
if (!isHash) {
return hash;
}
const history = getHistory();
return history.createHref({
...history.location,
hash,
});
};
const activeLink = useRef(getHref(location.hash));
const [anchorList, setAnchorList] = useState([]);
const renderAnchorList = (
anchorList: AnchorListType[],
type?: "default" | "radio"
) => {
return anchorList.map((item: AnchorListType, i: number) => {
return (
<span key={i} onClick={(e) => handleClick(e, item)}>
<Link
title={item.title}
href={item.href}
target={item.target}
key={i}
>
{type === "default" &&
item.children?.length &&
renderAnchorList(item.children, type)}
</Link>
</span>
);
});
};
useEffect(() => {
/* TODO(astrid): 初始锚点无法滚动到对应位置 */
const sharpMatcherRegx = /#([\S ]+)$/;
const initHash =
props?.anchorList.find((item) => item.href.includes(location.hash))
?.href || "";
if (initHash) {
const sharpLinkMatch = sharpMatcherRegx.exec(initHash.toString());
const target = document.getElementById(sharpLinkMatch[1]);
if (target) {
setTimeout(() => {
window.scrollTo({
top: target.offsetTop - (configProps?.offsetTop || 56),
});
});
handleChange(initHash);
}
}
}, []);
useEffect(() => {
if (props?.anchorList?.length) {
const anchorList = props.anchorList.map((anchor) => ({
...anchor,
href: getHref(anchor.href),
}));
setAnchorList([...anchorList]);
}
}, [props?.anchorList]);
return (
<Anchor
offsetTop={56}
{...configProps}
className={classnames([
{
[styles.anchorWrapper]: type !== "default",
},
])}
onChange={handleChange}
getCurrentAnchor={() => activeLink.current}
>
{type === "default" ? (
renderAnchorList(anchorList, type)
) : (
<div className={styles.anchorContainer}>
<div className={styles.anchorLinkContainer}>
{renderAnchorList(anchorList, type)}
</div>
{extraBrick?.useBrick && (
<div className={styles.extraContainer}>
<BrickAsComponent useBrick={extraBrick.useBrick} />
</div>
)}
</div>
)}
</Anchor>
);
}
Example #10
Source File: issue-drawer.tsx From erda-ui with GNU Affero General Public License v3.0 | 4 votes |
IssueDrawer = (props: IProps) => {
const {
className = '',
canCreate = false,
canDelete = false,
children,
editMode,
shareLink,
loading = false,
visible,
onClose,
onDelete,
confirmCloseTip,
handleCopy,
maskClosable,
data,
issueType,
projectId,
setData,
header = IssueDrawer.Empty,
footer = IssueDrawer.Empty,
extraHeaderOp = null,
detailTitle,
...rest
} = props;
const [
formField = IssueDrawer.Empty,
descField = IssueDrawer.Empty,
workflowField = IssueDrawer.Empty,
inclusionField = IssueDrawer.Empty,
relationField = IssueDrawer.Empty,
logField = IssueDrawer.Empty,
] = React.Children.toArray(children);
const customFieldDetail = issueStore.useStore((s) => s.customFieldDetail);
const escStack = layoutStore.useStore((s) => s.escStack);
const [copyTitle, setCopyTitle] = React.useState('');
const [isChanged, setIsChanged] = React.useState(false);
const [showCopy, setShowCopy] = React.useState(false);
const [showAnchor, setShowAnchor] = React.useState(false);
const preDataRef = React.useRef(data);
const preData = preDataRef.current;
const escClose = React.useCallback(
(e) => {
if (e.key === 'Escape') {
if (escStack.length) {
return;
}
if (isChanged && confirmCloseTip) {
Modal.confirm({
title: confirmCloseTip,
onOk() {
onClose(e);
},
});
} else {
onClose(e);
}
}
},
[confirmCloseTip, escStack, isChanged, onClose],
);
useEvent('keydown', escClose);
React.useEffect(() => {
const isIssueDrawerChanged = (initData: CreateDrawerData, currentData: CreateDrawerData) => {
setIsChanged(false);
Object.keys(currentData).forEach((key) => {
if (key in initData) {
if (!isEqual(initData[key], currentData[key])) {
setIsChanged(true);
}
} else {
const defaultValue = find(customFieldDetail?.property, { propertyName: key })?.values;
// Determine whether the field has changed. When the value is the following conditions, the field has not changed
const notChange =
isEqual(defaultValue, currentData[key]) ||
currentData[key] === undefined ||
currentData[key] === '' ||
isEqual(currentData[key], []) ||
isEqual(currentData[key], { estimateTime: 0, remainingTime: 0 });
if (!notChange) {
setIsChanged(true);
}
}
});
};
isIssueDrawerChanged(preData, data);
}, [customFieldDetail?.property, data, preData]);
const mainEle = React.useRef<HTMLDivElement>(null);
const { y } = useScroll(mainEle);
React.useLayoutEffect(() => {
let timer: NodeJS.Timeout;
if (mainEle.current) {
// wait for layout stable
timer = setTimeout(() => {
setShowAnchor(true);
}, 1000);
}
return () => timer && clearTimeout(timer);
}, []);
return (
<Modal
wrapClassName="issue-drawer-modal"
className={`task-drawer ${className}`}
width="calc(100% - 80px)"
closable={false}
visible={visible}
onCancel={onClose}
footer={null}
maskClosable={maskClosable || !isChanged}
keyboard={false}
forceRender // useScroll will not take effect if mainRel is lazy mounted
{...rest}
>
<Spin spinning={loading}>
<div className="relative flex flex-col" style={{ height: 'calc(100vh - 80px)' }}>
<If condition={header !== IssueDrawer.Empty}>
<div className={`task-drawer-header ${y > 2 ? 'shadow-card' : ''}`}>
<div className="flex justify-between items-center">
<div className="flex-1 nowrap">{typeof header === 'function' ? header(y) : header}</div>
<div className="task-drawer-op flex items-center">
{extraHeaderOp}
<SubscribersSelector
subscribers={data.subscribers}
issueID={customFieldDetail?.issueID}
issueType={issueType}
projectId={projectId}
setData={setData}
data={data}
/>
<If condition={editMode && shareLink}>
<Copy selector=".copy-share-link" tipName={i18n.t('dop:link-share')} />
<ErdaIcon
type="lianjie"
className="cursor-copy hover-active copy-share-link ml-4 text-default-6"
size="20"
data-clipboard-text={shareLink}
/>
</If>
<If condition={editMode}>
<WithAuth pass={canCreate}>
<Popover
title={i18n.t('dop:Copy issue')}
visible={showCopy}
onVisibleChange={(v) => setShowCopy(v)}
content={
<>
<Input
placeholder={i18n.t('dop:Please enter the issue title')}
style={{ width: 400 }}
value={copyTitle}
onChange={(e) => setCopyTitle(e.target.value)}
/>
<div className="flex items-center flex-wrap justify-end mt-2">
<Button
className="mr-2"
onClick={() => {
setCopyTitle('');
setShowCopy(false);
}}
>
{i18n.t('Cancel')}
</Button>
<Button
onClick={() => {
if (copyTitle === '') {
message.error(i18n.t('dop:The title can not be empty'));
return;
}
handleCopy && handleCopy(true, copyTitle);
setCopyTitle('');
setShowCopy(false);
}}
type="primary"
>
{i18n.t('Copy')}
</Button>
</div>
</>
}
placement="leftTop"
trigger="click"
>
<ErdaIcon type="fuzhi" className="hover-active ml-4 text-default-6" size="20" />
</Popover>
</WithAuth>
</If>
{onDelete ? (
<WithAuth pass={canDelete}>
<Popconfirm
title={`${i18n.t('common:confirm to delete')}?`}
placement="bottomRight"
onConfirm={onDelete}
>
<ErdaIcon type="shanchu-4d7l02mb" className="hover-active ml-4 text-default-6" size="20" />
</Popconfirm>
</WithAuth>
) : null}
{isChanged && confirmCloseTip ? (
<Popconfirm title={confirmCloseTip} placement="bottomRight" onConfirm={onClose}>
<ErdaIcon type="guanbi" className="ml-4 hover-active text-default-6" size="20" />
</Popconfirm>
) : (
<ErdaIcon type="guanbi" className="ml-4 hover-active text-default-6" size="20" onClick={onClose} />
)}
</div>
</div>
</div>
</If>
<div
ref={mainEle}
className="relative flex-1 overflow-x-hidden overflow-y-auto"
style={footer !== IssueDrawer.Empty ? { padding: '0 12% 60px' } : { padding: '0 12%' }}
>
<If condition={editMode && showAnchor}>
<div className="absolute">
<Anchor offsetTop={16} getContainer={() => mainEle.current || document.body}>
{formField !== IssueDrawer.Empty && <Anchor.Link href="#field" title={i18n.t('dop:Basic')} />}
{descField !== IssueDrawer.Empty && <Anchor.Link href="#desc" title={i18n.t('dop:Content')} />}
{workflowField !== IssueDrawer.Empty && (
<Anchor.Link href="#workflow" title={i18n.t('dop:workflow')} />
)}
{inclusionField !== IssueDrawer.Empty && (
<Anchor.Link href="#inclusion" title={i18n.t('dop:Contain')} />
)}
{relationField !== IssueDrawer.Empty && (
<Anchor.Link href="#relation" title={i18n.t('dop:Reference')} />
)}
{logField !== IssueDrawer.Empty && <Anchor.Link href="#log" title={i18n.t('dop:Log')} />}
</Anchor>
</div>
</If>
<If condition={formField !== IssueDrawer.Empty}>
<div id="field">{formField}</div>
</If>
<If condition={descField !== IssueDrawer.Empty}>
<div id="desc" className="h-px bg-default-08 my-4" />
{descField}
</If>
<If condition={workflowField !== IssueDrawer.Empty}>
<div id="workflow" className="mt-6">
{workflowField}
</div>
</If>
<If condition={inclusionField !== IssueDrawer.Empty}>
<div id="inclusion" className="mt-6">
{inclusionField}
</div>
</If>
<If condition={relationField !== IssueDrawer.Empty}>
<div id="relation" className="mt-6">
{relationField}
</div>
</If>
<If condition={logField !== IssueDrawer.Empty}>
<div id="log" className="mt-6">
{logField}
</div>
</If>
</div>
<If condition={footer !== IssueDrawer.Empty}>
<div className="task-drawer-footer">
{typeof footer === 'function' ? footer(isChanged, confirmCloseTip) : footer}
</div>
</If>
</div>
</Spin>
</Modal>
);
}
Example #11
Source File: index.tsx From gant-design with MIT License | 4 votes |
GantAnchor = (props: GantAnchorProps) => {
let {
list = [],
content,
fixedTop = 0,
layout = 'vertical',
onLayoutChange,
minHeight = 400,
className,
style,
getContainer = defaultContainer,
...nextProps
} = props;
const [stateMode, setStateMode] = useState(layout);
const [currentId, setId] = useState(list.length ? list[0].id : ''); //当前选中id,进入页面默认选中第一
const [leftArrowsShow, setLeftArrowsShow] = useState(false); //左侧箭头
const [rightArrowsShow, setRightArrowsShow] = useState(false); //右侧箭头
const [menuArrowsShow, setMenuArrowsShow] = useState(true);
const [silderIdWidth, setSilderIdWidth] = useState(0); //List外层宽度
const [contentWidth, setContentWidth] = useState(0); //List实际宽度
const [isClickScroll, setIsClickScroll] = useState(false); //页面滚动判断是点击后的滚动还是手动的滚动
let scrollHeight = 0; // 滚动的值
let m2 = 0; // 对比时间的值
let timer = null;
const Data = useCallback(
e => {
m2 = getContainerScrollHeight(getContainer);
if (m2 == scrollHeight) {
if (isClickScroll) {
setIsClickScroll(false);
}
}
},
[isClickScroll, setIsClickScroll, getContainer],
);
// //滚动时触发
const handleScroll = useCallback(
e => {
const fixedEle = document.getElementById('anchorBoxId'); //定位元素
const fixedEleParent = document.querySelector('.gant-anchor-horAnchorOut'); //定位元素的父元素
if (fixedEle && stateMode == 'horizontal') {
clearTimeout(timer);
timer = setTimeout(Data, 300);
const menuboxhor = document.querySelector('.gant-submenu-menuboxhor'); //anchor的外层card
const extraheight = menuboxhor ? menuboxhor['offsetHeight'] : 0;
const container = getContainer();
const isWindow = container == window;
// 容器距离浏览器顶部的距离
const containerScrollTop = isWindow
? 0
: (container as Element)?.getBoundingClientRect?.()?.top;
// 滚动条滚动的距离
scrollHeight = getContainerScrollHeight(getContainer);
if (fixedEle) {
if (scrollHeight >= FIXED_HEIGHT + fixedTop + extraheight) {
fixedEle.classList.add('gant-anchor-activeScroll');
const active = document.querySelector('.gant-anchor-activeScroll');
active['style'].top = `${fixedTop + containerScrollTop}px`;
active['style'].width = `${fixedEleParent['offsetWidth']}px`;
} else {
fixedEle.classList.remove('gant-anchor-activeScroll');
}
}
if (!isClickScroll) {
//水平方向锚点跟随页面滚动高亮
list.map(item => {
if (!item.isInvalid) {
const id = document.getElementById(item.id);
let common = fixedTop + extraheight + FIXED_HEIGHT + containerScrollTop;
if (id && id.getBoundingClientRect()) {
let { top, height } = id.getBoundingClientRect();
if (top <= common && top >= common - height) {
//这里的44是水平锚点条高度
setId(item.id);
}
}
}
});
}
}
},
[stateMode, setId, isClickScroll, list, getContainer],
);
// //点击左右箭头
const handleMobileTabs = useCallback(
e => {
const contentId = document.getElementById('contentId');
const left = contentId.offsetLeft;
const right = contentWidth - silderIdWidth + left;
if (e == 'left') {
if (left < 0 && left > -500) {
contentId.style.left = '0px';
} else if (left < -500) {
contentId.style.left = `${left + 500}` + 'px';
}
const newLeft = contentId.offsetLeft;
setLeftArrowsShow(newLeft == 0 ? false : true);
setRightArrowsShow(true);
} else {
if (right > 0 && right < 500) {
contentId.style.left = `${left - right}` + 'px';
} else if (right >= 0 && right > 500) {
contentId.style.left = `${left - 500}` + 'px';
}
const newRight = contentWidth - silderIdWidth + contentId.offsetLeft;
setLeftArrowsShow(true);
setRightArrowsShow(newRight == 0 ? false : true);
}
},
[contentWidth, silderIdWidth, setLeftArrowsShow, setRightArrowsShow],
);
useEffect(() => {
setStateMode(stateMode);
}, [setStateMode]);
useEffect(() => {
const container = getContainer();
container.addEventListener('scroll', handleScroll); //监听滚动
return function cleanup() {
container.removeEventListener('scroll', handleScroll);
};
}, [handleScroll, setIsClickScroll, getContainer]);
useEffect(() => {
const silderId = document.getElementById('silderId');
const contentId = document.getElementById('contentId');
const silderIdWidth = silderId ? silderId.offsetWidth : 0;
const contentWidth = contentId ? contentId.offsetWidth : 0;
setSilderIdWidth(silderIdWidth);
setContentWidth(contentWidth);
setRightArrowsShow(
stateMode == 'horizontal' ? (silderIdWidth < contentWidth ? true : false) : false,
);
setMenuArrowsShow(
stateMode == 'horizontal' ? (silderIdWidth < contentWidth ? true : false) : false,
);
}, [stateMode, setSilderIdWidth, setContentWidth, setRightArrowsShow, setMenuArrowsShow]);
const getOffsetTop = useCallback((element: HTMLElement, container: any): number => {
if (!element.getClientRects().length) {
return 0;
}
const rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
if (container === window) {
container = element.ownerDocument!.documentElement!;
return rect.top - container.clientTop;
}
return rect.top - (container as HTMLElement).getBoundingClientRect().top;
}
return rect.top;
}, []);
//水平方向锚点事件
const scrollToAnchor = useCallback(
anchorName => {
if (anchorName) {
const container = getContainer();
let anchorElement = document.getElementById(anchorName);
if (anchorElement) {
const scrollTop = getScroll(container, true);
const eleOffsetTop = getOffsetTop(anchorElement, container);
let y = scrollTop + eleOffsetTop - FIXED_HEIGHT - fixedTop;
scrollTo(y, {
getContainer,
});
}
setId(anchorName);
setIsClickScroll(true);
}
},
[setId, setIsClickScroll, fixedTop, getContainer],
);
//水平方向锚点menu内容
const menu = useMemo(() => {
return (
<Menu selectedKeys={[currentId]}>
{list.map(item => {
return (
<Menu.Item
key={item.id}
onClick={() => scrollToAnchor(item.id)}
disabled={item.isInvalid ? true : false}
>
{item.title}
</Menu.Item>
);
})}
</Menu>
);
}, [currentId, list]);
//锚点方向切换
const onSwitchClick = useCallback(
e => {
let newStateMode: layout = stateMode === 'vertical' ? 'horizontal' : 'vertical';
if (layout === 'vertical') {
// 去掉磁吸效果
const fixedEle = document.getElementById('anchorBoxId'); //定位元素
fixedEle && fixedEle.classList.remove(`${prefixCls}-activeScroll`);
}
setStateMode(newStateMode);
onLayoutChange && onLayoutChange(newStateMode);
},
[stateMode, setStateMode, onLayoutChange],
);
return (
<div className={classnames(className, `${prefixCls}-wrapper`)} style={{ ...style }}>
<div
style={{ width: stateMode === 'horizontal' ? '100%' : 'calc(100% - 150px)', float: 'left' }}
>
<div
className={classnames(`${prefixCls}-horAnchorOut`, {
[`${prefixCls}-horAnchorOut__hidden`]: stateMode === 'vertical',
})}
>
<div className={`gant-anchor`} id="anchorBoxId">
<Icon
type="left"
style={{ display: leftArrowsShow ? 'block' : 'none', float: 'left' }}
className={`${prefixCls}-iconCss`}
onClick={() => handleMobileTabs('left')}
/>
<div className={`${prefixCls}-silderCss`} id="silderId">
<div className={`${prefixCls}-contentCss`} id="contentId">
{list.map(item => {
let nowCss = item.id == currentId ? 'activeCss' : '';
if (item.isInvalid) {
return <div className={`${prefixCls}-isInvalid`}>{item.title}</div>;
}
return (
<a
className={`${prefixCls}-aCss`}
key={item.id}
onClick={() => scrollToAnchor(item.id)}
>
<span className={`${prefixCls}-${nowCss}`}>
{
<>
{item.title}
{item.complete ? (
<Icon
type="check-circle"
theme="twoTone"
twoToneColor="#52c41a"
style={{ paddingLeft: '5px' }}
/>
) : null}
</>
}
</span>
</a>
);
})}
</div>
</div>
<Icon
type="switcher"
onClick={onSwitchClick}
className={`${prefixCls}-iconCss`}
style={{ float: 'right' }}
/>
<Dropdown
overlay={menu}
// style={{ display: menuArrowsShow ? 'block' : 'none' }}
placement="bottomRight"
>
<Icon type="down" className={`${prefixCls}-iconCss`} style={{ float: 'right' }} />
</Dropdown>
<Icon
type="right"
style={{ display: rightArrowsShow ? 'block' : 'none', float: 'right' }}
className={`${prefixCls}-iconCss`}
onClick={() => handleMobileTabs('right')}
/>
</div>
</div>
<div className="gant-anchor-content" style={{ padding: '0px', minHeight }}>
{content}
</div>
</div>
<div
className={classnames(`${prefixCls}-verticalbox`, {
[`${prefixCls}-verticalbox__hidden`]: stateMode === 'horizontal',
})}
style={{
width: 150,
paddingLeft: '10px',
paddingTop: '10px',
float: stateMode === 'horizontal' ? 'none' : 'left',
}}
>
<Anchor
offsetTop={fixedTop}
onClick={e => {
e.preventDefault();
}}
{...nextProps}
getContainer={getContainer}
>
<Icon
type="switcher"
onClick={onSwitchClick}
style={{ width: '100%', paddingRight: '10px', textAlign: 'right' }}
/>
{list.map(item => {
const nullCss = {};
return (
<div
key={item?.id}
style={item.isInvalid ? { opacity: 0.5, cursor: 'not-allowed' } : nullCss}
>
<div style={item.isInvalid ? { pointerEvents: 'none' } : nullCss}>
<Anchor.Link
key={item.key || item.title}
href={`#${item.id || item.title}`}
title={
<>
<Tooltip title={item.title} placement="left">
{item.title}
</Tooltip>
{item.complete ? (
<Icon
type="check-circle"
theme="twoTone"
twoToneColor="#52c41a"
style={{ paddingLeft: '5px' }}
/>
) : null}
</>
}
/>
</div>
</div>
);
})}
</Anchor>
</div>
</div>
);
}
Example #12
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
FieldAccess: React.FC<Props> = props => {
const initState: State = {
currentItem: props.data,
tempFieldAccess: [],
};
const [currentItem, setCurrentItem] = useState(initState.currentItem);
const [tempFieldAccess, setTempFieldAccess] = useState(initState.tempFieldAccess);
useEffect(() => {
setCurrentItem(props.data);
const tempKey: string[] = [];
((props.data.current || {}).dataAccesses || []).forEach(e =>
e.config.fields.forEach((i: string) => tempKey.push(e.action + '-' + i)),
);
const temp = (props.data.optionalFields || []).map(field => {
const actions = (props.data.actions || []).map(a => {
let key = a.action + '-' + field.name;
return { checked: tempKey.find(i => i === key) ? false : true, ...a };
});
return { actions, ...field } as TempField;
});
setTempFieldAccess(temp);
}, [props.data]);
const buildAccess = () => {
let tempAccess = new Map();
tempFieldAccess.forEach(item => {
item.actions.forEach(ac => {
if (ac.checked) return;
//获取不到就创建
let fieldAccess =
tempAccess.get(ac.action) ||
tempAccess
.set(ac.action, {
action: ac.action,
type: 'DENY_FILEDS',
config: {
fields: [],
},
})
.get(ac.action);
fieldAccess.config.fields.push(item.name);
});
});
currentItem.dataAccesses = [...tempAccess.values()];
props.save(currentItem);
message.success('保存成功');
};
const checkItem = (field: string, action: string) => {
const temp = tempFieldAccess.map(item => {
if (item.name === field) {
item.actions.map(ac => {
if (ac.action === action) {
ac.checked = !ac.checked;
}
return ac;
});
}
return item;
});
setTempFieldAccess(temp);
};
return (
<Modal
title={`设置${currentItem.name}字段权限`}
visible
width={800}
onCancel={() => {
props.close();
setCurrentItem({});
}}
onOk={() => {
buildAccess();
}}
>
<Alert
message='描述: 如果角色对当前对象设置了 "新建" 或 "导入" 权限,带*号的必填字段默认设置为“读写”,不可设置为“只读”或“不可见”,否则会造成新建/导入不成功'
type="info"
/>
<Divider type="horizontal" />
<Card bordered={false} id="field-access-card" style={{ height: 300, overflow: 'auto' }}>
<Col span={20}>
{tempFieldAccess.map(data => {
return (
<Row style={{ width: '100%', marginTop: 20 }} key={data.name} id={data.name}>
<Col span={4} style={{ fontWeight: 700 }}>
<span style={{ fontSize: '14px', color: '#f5222d' }}>* </span>
{data.describe}
</Col>
<Col span={20}>
<Checkbox.Group
style={{ width: '100%' }}
value={data.actions.filter(e => e.checked === true).map(e => e.action)}
>
{data.actions.map(item => (
<Col key={item.action} span={5}>
<Checkbox
value={item.action}
onClick={() => {
checkItem(data.name, item.action);
}}
>
{item.name}
</Checkbox>
</Col>
))}
</Checkbox.Group>
</Col>
</Row>
);
})}
</Col>
<Col span={3} push={1} style={{ height: 600, overflow: 'auto' }}>
<Affix>
<Anchor getContainer={() => document.getElementById('field-access-card') || window}>
{(props.data.optionalFields || []).map(data => (
<Anchor.Link href={'#' + data.name} title={data.describe} key={data.name} />
))}
</Anchor>
</Affix>
</Col>
</Card>
</Modal>
);
}
Example #13
Source File: index.tsx From surveyo with Apache License 2.0 | 4 votes |
function FormCreator() {
const [questions, setQuestions] = useState<any>([]);
const [formSubmitted, setFormSubmitState] = useState(false);
const [formURL, setFormURL] = useState('');
const [surveyTitle, setSurveyTitle] = useState('');
const [formHook] = useForm();
const {user} = useAuth0();
const [sendToClient] = useMutation(ADD_FORM);
const getCard = (i: number) => {
const question = questions[i];
const params = {
question: question,
updateQuestion: (question: any) =>
setQuestions(update(questions, {$splice: [[i, 1, question]]})),
deleteQuestion: () =>
setQuestions(update(questions, {$splice: [[i, 1]]})),
};
return <QuestionCard {...params} />;
};
const menu = (
<Menu onClick={e => setQuestions(questions.concat({type: e.key}))}>
<Menu.Item key="Text">Short Answer</Menu.Item>
<Menu.Item key="SingleChoice">Multiple Choice</Menu.Item>
<Menu.Item key="Date">Date</Menu.Item>
<Menu.Item key="Rating">Rating</Menu.Item>
<Menu.Item key="NetPromoterScore">Net Promoter Score</Menu.Item>
</Menu>
);
if (formSubmitted) {
return (
<Card type="inner">
<Result
status="success"
title="Thank you!"
subTitle={
<Anchor>
<Anchor.Link href={formURL} title="Your form is live." />
</Anchor>
}
/>
</Card>
);
} else
return (
<PageHeader ghost={true} title="Create a survey">
<Form form={formHook}>
<Card
actions={[
<Dropdown overlay={menu}>
<Button>
Add Question <DownOutlined />
</Button>
</Dropdown>,
<Button
type="primary"
onClick={async () => {
const values = await formHook.validateFields();
console.log('validation ' + values.name);
for (let index = 0; index < questions.length; index++) {
if ('options' in questions[index]) {
let newOptions = questions[index].options.map(
(value: any, index: any) => {
return {order: index, title: value};
}
);
questions[index].options = newOptions;
}
questions[index].order = index;
if (!('required' in questions[index])) {
questions[index].required = false;
}
}
var form = {
title: surveyTitle,
fields: questions,
creator: {email: user.email},
};
console.log('Form: ', form);
try {
var result = await sendToClient({
variables: {
form: form,
},
});
console.log(result);
let id = result.data.addForm.form[0].id;
let url =
window.location.href.replace('/create', '') +
'/form/' +
id;
setFormURL(url);
setFormSubmitState(true);
} catch (error) {
console.log(error);
}
}}
>
Create
</Button>,
]}
>
<Form.Item
label="Survey Title"
name="survey title"
rules={[{required: true, message: 'Please input Survey title'}]}
>
<Input
placeholder="Enter your survey title"
onChange={e => {
setSurveyTitle(e.target.value);
}}
/>
</Form.Item>
{questions.map((question: any, index: number) => (
<div key={index}>
<Card>{getCard(index)}</Card>
</div>
))}
</Card>
</Form>
</PageHeader>
);
}
Example #14
Source File: index.tsx From amiya with MIT License | 4 votes |
export default function Demo() {
// 提交的数据
const [submitValues, setSubmitValues] = useState<AnyKeyProps>({})
// 当前 sku 表格数据
const [skuData, setSkuData] = useState<Array<Record>>([])
// sku 对应的图片对象
const [skuImageMap, setSkuImageMap] = useState<Record>({})
const formRef = useRef<any>()
/**
* 更新上传图片
* @param name 当前 sku 值
* @param value 新图片
*/
const handleChange = (name: string, value: string) => {
let newMap = { ...skuImageMap }
newMap[name] = value
setSkuImageMap(newMap)
}
// 改变 sku 表格数据后,删除多余的 key,保留已经选中的 key
useEffect(() => {
let newMap = { ...skuImageMap }
let names = skuData.map(item => item.name)
for (let key in newMap) {
if (!names.includes(key)) {
delete newMap[key]
}
}
setSkuImageMap(newMap)
}, [skuData])
const handleSubmit = (values: FormValues) => {
// TODO 必填校验
setSubmitValues({ ...values, skuMap: skuImageMap })
}
const fillData = () => {
formRef.current.setFieldsValue(data)
setSkuImageMap(data.skuMap)
}
return (
<Card>
<div style={{ position: 'relative' }}>
<AyForm
ref={formRef}
style={{ marginRight: 100 }}
formLayout="vertical"
onConfirm={values => handleSubmit(values)}
gutter={12}
>
<AyFields>
<AyField title="基础信息" key="__base" id="base" type="card" collapsible>
<AyField
title="店铺"
type="select"
key="shopId"
required
span={12}
options={[
{ label: '选项A', value: 1 },
{ label: '选项B', value: 2 }
]}
/>
<AyField title="商店名称" key="name" required span={12} maxLength={120} showCount />
<AyField
title="主营类目"
type="select"
key="category"
required
span={12}
options={[
{ label: '选项A', value: 1 },
{ label: '选项B', value: 2 }
]}
/>
<AyField
title="商品保存状态"
type="select"
key="saveStatus"
span={12}
options={[
{ label: '选项A', value: 1 },
{ label: '选项B', value: 2 }
]}
/>
<AyField title="商品描述" type="textarea" required key="remark" rows={5} maxLength={3000} showCount />
</AyField>
<AyField title="商品信息" key="__goods" id="goods" type="card" collapsible>
<AyField key="sku" type="custom" renderContent={() => <SkuEdit onDataChange={setSkuData} />} />
</AyField>
<AyField title="媒体管理" key="__images" type="card" id="images" collapsible>
<AyField
title="商品图片"
key="goodsImage"
type="custom"
required
help="*最多可以上传 9 张"
defaultValue={[]}
multiple
renderContent={() => <UploadImage multiple max={9} />}
/>
<AyField
title="SKU 图片"
key="__skuImage"
type="custom"
hidden={skuData.length === 0}
renderContent={() => (
<Space>
{skuData.map(sku => (
<div key={sku.name}>
<UploadImage
value={skuImageMap[sku.name]}
onChange={value => handleChange(sku.name, value as string)}
/>
<div style={{ textAlign: 'center', transform: 'translateX(-4px)' }}>{sku.name}</div>
</div>
))}
</Space>
)}
/>
</AyField>
<AyField title="运费信息" key="__freight" id="freight" type="card" collapsible>
<AyField key="__freightTitle" render={() => <h3>包裹尺寸</h3>} />
<AyField title="长" key="__long" type="input-group" span={8}>
<AyField key="long" type="number" />
<AyField key="longUnit" type="select" options={unitOptions} defaultValue={1} readonly />
</AyField>
<AyField title="宽" key="__wide" type="input-group" span={8}>
<AyField key="wide" type="number" />
<AyField key="wideUnit" type="select" options={unitOptions} defaultValue={1} readonly />
</AyField>
<AyField title="高" key="__height" type="input-group" span={8}>
<AyField key="height" type="number" />
<AyField key="heightUnit" type="select" options={unitOptions} defaultValue={1} readonly />
</AyField>
<AyField title="重量" key="__weight" type="input-group" span={8}>
<AyField key="weight" type="number" />
<AyField
key="weightUnit"
type="select"
options={[
{ label: 'kg', value: 1 },
{ label: 'g', value: 2 }
]}
readonly
defaultValue={1}
/>
</AyField>
</AyField>
</AyFields>
<Space>
<AyButton htmlType="submit" type="primary">
提交
</AyButton>
<AyButton onClick={fillData}>填充数据</AyButton>
</Space>
</AyForm>
<div style={{ position: 'absolute', right: 0, top: 0 }}>
<Anchor offsetTop={90}>
<Link href="#base" title="基础信息" />
<Link href="#goods" title="商品信息" />
<Link href="#images" title="媒体管理" />
<Link href="#freight" title="运费信息" />
</Anchor>
</div>
</div>
{submitValues.name && <pre>{JSON.stringify(submitValues, null, 2)}</pre>}
</Card>
)
}