Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions web/apps/labelstudio/src/components/Breadcrumbs/Breadcrumbs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import { useEffect, useState } from "react";
import { NavLink } from "react-router-dom";
import { useConfig } from "../../providers/ConfigProvider";
import { useBreadcrumbs, useFindRouteComponent } from "../../providers/RoutesProvider";
import { BemWithSpecificContext } from "../../utils/bem";
import { cn } from "../../utils/bem";
import { absoluteURL } from "../../utils/helpers";
import { Dropdown } from "../Dropdown/Dropdown";
import { Menu } from "../Menu/Menu";
import "./Breadcrumbs.scss";

const { Block, Elem } = BemWithSpecificContext();

export const Breadcrumbs = () => {
const config = useConfig();
const reactBreadcrumbs = useBreadcrumbs();
Expand All @@ -25,8 +23,8 @@ export const Breadcrumbs = () => {
}, [reactBreadcrumbs, config]);

return (
<Block name="breadcrumbs">
<Elem tag="ul" name="list">
<div className={cn("breadcrumbs").toClassName()}>
<ul className={cn("breadcrumbs").elem("list").toClassName()}>
{breadcrumbs.map((item, index, list) => {
const isLastItem = index === list.length - 1;

Expand All @@ -37,9 +35,14 @@ export const Breadcrumbs = () => {
const isInternal = findComponent(href) !== null;

const title = (
<Elem tag="span" name="label" mod={{ faded: index === item.length - 1 }}>
<span
className={cn("breadcrumbs")
.elem("label")
.mod({ faded: index === item.length - 1 })
.toClassName()}
>
{item.title}
</Elem>
</span>
);

const dropdownSubmenu = item.submenu ? (
Expand All @@ -61,37 +64,35 @@ export const Breadcrumbs = () => {
) : null;

return item.onClick ? (
<Elem key={key} tag="li" name="item" mod={{ last: isLastItem }}>
<li key={key} className={cn("breadcrumbs").elem("item").mod({ last: isLastItem }).toClassName()}>
<span onClick={item.onClick}>{title}</span>
</Elem>
</li>
) : dropdownSubmenu ? (
<Elem
<Dropdown.Trigger
key={key}
tag="li"
component={Dropdown.Trigger}
name="item"
mod={{ last: isLastItem }}
component="li"
className={cn("breadcrumbs").elem("item").mod({ last: isLastItem }).toClassName()}
content={dropdownSubmenu}
>
<span>{title}</span>
</Elem>
</Dropdown.Trigger>
) : href && !isLastItem ? (
<Elem key={key} tag="li" name="item" mod={{ last: isLastItem }}>
<li key={key} className={cn("breadcrumbs").elem("item").mod({ last: isLastItem }).toClassName()}>
{isInternal ? (
<NavLink to={href} data-external={true}>
{title}
</NavLink>
) : (
<a href={absoluteURL(href)}>{title}</a>
)}
</Elem>
</li>
) : (
<Elem key={key} tag="li" name="item" mod={{ last: isLastItem }}>
<li key={key} className={cn("breadcrumbs").elem("item").mod({ last: isLastItem }).toClassName()}>
{title}
</Elem>
</li>
);
})}
</Elem>
</Block>
</ul>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { Label } from "..";
import { BemWithSpecificContext } from "../../../../utils/bem";
import { cn } from "../../../../utils/bem";
import { FormField } from "../../FormField";
import "./RadioGroup.scss";

const RadioContext = createContext();
const { Block, Elem } = BemWithSpecificContext();

export const RadioGroup = ({
label,
Expand Down Expand Up @@ -50,10 +49,10 @@ export const RadioGroup = ({
isSimple: simple === true,
}}
>
<Block name="radio-group-ls" mod={{ size, simple }} mix={className}>
<div className={cn("radio-group-ls").mod({ size, simple }).mix(className).toClassName()}>
<input ref={ref} name={props.name} type="hidden" defaultValue={currentValue} />
<Elem name="buttons">{children}</Elem>
</Block>
<div className={cn("radio-group-ls").elem("buttons").toClassName()}>{children}</div>
</div>
</RadioContext.Provider>
)}
</FormField>
Expand Down Expand Up @@ -90,7 +89,10 @@ const RadioButton = ({ value, disabled, children, label, description, ...props }
}, [props.checked]);

return (
<Elem name="button" mod={{ checked, disabled }} onClickCapture={clickHandler}>
<div
className={cn("radio-group-ls").elem("button").mod({ checked, disabled }).toClassName()}
onClickCapture={clickHandler}
>
{isSimple ? (
<Label placement="right" text={label} description={description}>
<input
Expand All @@ -105,7 +107,7 @@ const RadioButton = ({ value, disabled, children, label, description, ...props }
) : (
children
)}
</Elem>
</div>
);
};

Expand Down
25 changes: 10 additions & 15 deletions web/apps/labelstudio/src/components/Menu/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { forwardRef, useCallback, useMemo } from "react";
import { cn } from "../../utils/bem";
import { useDropdown } from "../Dropdown/Dropdown";
import "./Menu.scss";
import { Block, Elem, MenuContext } from "./MenuContext";
import { MenuContext } from "./MenuContext";
import { MenuItem } from "./MenuItem";

export const Menu = forwardRef(
Expand Down Expand Up @@ -30,25 +30,22 @@ export const Menu = forwardRef(

return (
<MenuContext.Provider value={{ selected }}>
<Block
<ul
ref={ref}
tag="ul"
name="main-menu"
mod={{ size, collapsed, contextual }}
mix={className}
className={cn("main-menu").mod({ size, collapsed, contextual }).mix(className).toClassName()}
style={style}
onClick={clickHandler}
>
{children}
</Block>
</ul>
</MenuContext.Provider>
);
},
);

Menu.Item = MenuItem;
Menu.Spacer = () => <Elem block="main-menu" tag="li" name="spacer" />;
Menu.Divider = () => <Elem block="main-menu" tag="li" name="divider" />;
Menu.Spacer = () => <li className={cn("main-menu").elem("spacer").toClassName()} />;
Menu.Divider = () => <li className={cn("main-menu").elem("divider").toClassName()} />;
Menu.Builder = (url, menuItems) => {
return (menuItems ?? []).map((item, index) => {
if (item === "SPACER") return <Menu.Spacer key={index} />;
Expand Down Expand Up @@ -85,11 +82,9 @@ Menu.Builder = (url, menuItems) => {

Menu.Group = ({ children, title, className, style }) => {
return (
<Block name="menu-group" mix={className} style={style}>
<Elem name="title">{title}</Elem>
<Elem tag="ul" name="list">
{children}
</Elem>
</Block>
<div className={cn("menu-group").mix(className).toClassName()} style={style}>
<div className={cn("menu-group").elem("title").toClassName()}>{title}</div>
<ul className={cn("menu-group").elem("list").toClassName()}>{children}</ul>
</div>
);
};
2 changes: 0 additions & 2 deletions web/apps/labelstudio/src/components/Menu/MenuContext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from "react";
import { BemWithSpecificContext } from "../../utils/bem";

export const MenuContext = React.createContext();
export const { Block, Elem } = BemWithSpecificContext();
11 changes: 6 additions & 5 deletions web/apps/labelstudio/src/components/Space/Space.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { BemWithSpecificContext } from "../../utils/bem";
import { cn } from "../../utils/bem";
import "./Space.scss";

const { Block } = BemWithSpecificContext();

export const Space = ({ direction = "horizontal", size, className, style, children, spread, stretch, align }) => {
return (
<Block name="space-ls" mod={{ direction, size, spread, stretch, align }} mix={className} style={style}>
<div
className={cn("space-ls").mod({ direction, size, spread, stretch, align }).mix(className).toClassName()}
style={style}
>
{children}
</Block>
</div>
);
};
62 changes: 33 additions & 29 deletions web/apps/labelstudio/src/pages/ExportPage/ExportPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Modal } from "../../components/Modal/Modal";
import { Space } from "../../components/Space/Space";
import { useAPI } from "../../providers/ApiProvider";
import { useFixedLocation, useParams } from "../../providers/RoutesProvider";
import { BemWithSpecificContext } from "../../utils/bem";
import { cn } from "../../utils/bem";
import { isDefined } from "../../utils/helpers";
import "./ExportPage.scss";

Expand All @@ -23,8 +23,6 @@ const downloadFile = (blob, filename) => {
link.click();
};

const { Block, Elem } = BemWithSpecificContext();

const wait = () => new Promise((resolve) => setTimeout(resolve, 5000));

export const ExportPage = () => {
Expand Down Expand Up @@ -115,7 +113,7 @@ export const ExportPage = () => {
// footer="Read more about supported export formats in the Documentation."
visible
>
<Block name="export-page">
<div className={cn("export-page").toClassName()}>
<FormatInfo
availableFormats={availableFormats}
selected={currentFormat}
Expand All @@ -126,56 +124,62 @@ export const ExportPage = () => {
<Input type="hidden" name="exportType" value={currentFormat} />
</Form>

<Elem name="footer">
<div className={cn("export-page").elem("footer").toClassName()}>
<Space style={{ width: "100%" }} spread>
<Elem name="recent">{/* {exportHistory} */}</Elem>
<Elem name="actions">
<div className={cn("export-page").elem("recent").toClassName()}>{/* {exportHistory} */}</div>
<div className={cn("export-page").elem("actions").toClassName()}>
<Space>
{downloadingMessage && "Files are being prepared. It might take some time."}
<Button className="w-[135px]" onClick={proceedExport} waiting={downloading} aria-label="Export data">
Export
</Button>
</Space>
</Elem>
</div>
</Space>
</Elem>
</Block>
</div>
</div>
</Modal>
);
};

const FormatInfo = ({ availableFormats, selected, onClick }) => {
return (
<Block name="formats">
<Elem name="info">You can export dataset in one of the following formats:</Elem>
<Elem name="list">
<div className={cn("formats").toClassName()}>
<div className={cn("formats").elem("info").toClassName()}>
You can export dataset in one of the following formats:
</div>
<div className={cn("formats").elem("list").toClassName()}>
{availableFormats.map((format) => (
<Elem
<div
key={format.name}
name="item"
mod={{
active: !format.disabled,
selected: format.name === selected,
}}
className={cn("formats")
.elem("item")
.mod({
active: !format.disabled,
selected: format.name === selected,
})
.toClassName()}
onClick={!format.disabled ? () => onClick(format) : null}
>
<Elem name="name">
<div className={cn("formats").elem("name").toClassName()}>
{format.title}

<Space size="small">
{format.tags?.map?.((tag, index) => (
<Elem key={index} name="tag">
<div key={index} className={cn("formats").elem("tag").toClassName()}>
{tag}
</Elem>
</div>
))}
</Space>
</Elem>
</div>

{format.description && <Elem name="description">{format.description}</Elem>}
</Elem>
{format.description && (
<div className={cn("formats").elem("description").toClassName()}>{format.description}</div>
)}
</div>
))}
</Elem>
<Elem name="feedback">
</div>
<div className={cn("formats").elem("feedback").toClassName()}>
Can't find an export format?
<br />
Please let us know in{" "}
Expand All @@ -191,8 +195,8 @@ const FormatInfo = ({ availableFormats, selected, onClick }) => {
>
Repository
</a>
</Elem>
</Block>
</div>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Typography } from "@humansignal/ui";
import { Space } from "@humansignal/ui/lib/space/space";
import { Block } from "apps/labelstudio/src/components/Menu/MenuContext";
import { cn } from "apps/labelstudio/src/utils/bem";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brandon probably can help here to decide If this is correct or should drive from @HumanSignal

import { Modal } from "apps/labelstudio/src/components/Modal/ModalPopup";
import { API } from "apps/labelstudio/src/providers/ApiProvider";
import { useAtomValue } from "jotai";
Expand Down Expand Up @@ -54,7 +54,7 @@ export function InviteLink({
const InvitationModal = () => {
const { data: link } = useAtomValue(linkAtom);
return (
<Block name="invite">
<div className={cn("invite").toClassName()}>
<Input value={link} style={{ width: "100%" }} readOnly />
<Typography size="small" className="text-neutral-content-subtler mt-base mb-wider">
Invite members to join your Label Studio instance. People that you invite have full access to all of your
Expand All @@ -74,7 +74,7 @@ const InvitationModal = () => {
</a>
.
</Typography>
</Block>
</div>
);
};

Expand Down
Loading