Skip to content

Commit 58d8e86

Browse files
feat: Minor 3.3.0 (#222)
* feat: add ability to hide collections #211 (#213) * feat: add ability to hide collections #211 * fix: hide/unhide collection label is swapped * fix: missing useCallback dependency * fix: add selected tabs to existing collection adds all tabs in current window #215 (#216) * fix: add selected tabs to existing collection adds all tabs in current window #215 * fix: force selected tabs only for adding tabs to groups * feat: compact collection view (#214) * feat: compact collection view #201 * loc: compact view localization * fix(loc): missing "color" translation in edit dialog * feat: add ability to edit saved tabs (#218) * feat: adds option to edit saved tabs #217 * loc: translations for #217 * build(deps): Bump the react group with 2 updates (#221) Bumps the react group with 2 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom). Updates `react` from 19.2.1 to 19.2.3 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.3/packages/react) Updates `react-dom` from 19.2.1 to 19.2.3 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.3/packages/react-dom) --- updated-dependencies: - dependency-name: react dependency-version: 19.2.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-dom dependency-version: 19.2.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): Bump the deps group with 4 updates (#220) Bumps the deps group with 4 updates: [@fluentui/react-components](https://github.com/microsoft/fluentui), [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `@fluentui/react-components` from 9.72.8 to 9.72.9 - [Release notes](https://github.com/microsoft/fluentui/releases) - [Commits](https://github.com/microsoft/fluentui/compare/@fluentui/react-components_v9.72.8...@fluentui/react-components_v9.72.9) Updates `@eslint/js` from 9.39.1 to 9.39.2 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/commits/v9.39.2/packages/js) Updates `eslint` from 9.39.1 to 9.39.2 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](eslint/eslint@v9.39.1...v9.39.2) Updates `typescript-eslint` from 8.49.0 to 8.51.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.51.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: "@fluentui/react-components" dependency-version: 9.72.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: deps - dependency-name: "@eslint/js" dependency-version: 9.39.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: deps - dependency-name: eslint dependency-version: 9.39.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: deps - dependency-name: typescript-eslint dependency-version: 8.51.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: deps ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Bump version from 3.2.3 to 3.3.0 --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent fdac0c0 commit 58d8e86

29 files changed

+963
-669
lines changed

entrypoints/sidepanel/components/CollectionView.styles.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export const useStyles_CollectionView = makeStyles({
1919
"&:hover":
2020
{
2121
boxShadow: tokens.shadow4
22+
},
23+
24+
"&:not(:focus-within) .compact":
25+
{
26+
display: "none"
2227
}
2328
},
2429
color:

entrypoints/sidepanel/components/CollectionView.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ import { useStyles_CollectionView } from "./CollectionView.styles";
1212
import GroupView from "./GroupView";
1313
import TabView from "./TabView";
1414

15-
export default function CollectionView({ collection, index: collectionIndex, dragOverlay }: CollectionViewProps): ReactElement
15+
export default function CollectionView({
16+
collection,
17+
index: collectionIndex,
18+
dragOverlay,
19+
compact
20+
}: CollectionViewProps): ReactElement
1621
{
1722
const { tilesView } = useCollections();
1823
const {
@@ -53,22 +58,25 @@ export default function CollectionView({ collection, index: collectionIndex, dra
5358
{ (!activeItem || activeItem.item.type !== "collection") && !dragOverlay &&
5459
<>
5560
{ collection.items.length < 1 ?
56-
<div className={ cls.empty }>
61+
<div className={ mergeClasses(cls.empty, compact === true && "compact") }>
5762
<CollectionsRegular fontSize={ 32 } />
5863
<Body1Strong>{ i18n.t("collections.empty") }</Body1Strong>
5964
</div>
6065
:
61-
<div className={ mergeClasses(cls.list, !tilesView && cls.verticalList) }>
66+
<div className={ mergeClasses(cls.list, !tilesView && cls.verticalList, compact === true && "compact") }>
6267
<SortableContext
6368
items={ collection.items.map((_, index) => [collectionIndex, index].join("/")) }
6469
strategy={ tilesView ? horizontalListSortingStrategy : verticalListSortingStrategy }
6570
>
6671
{ collection.items.map((i, index) =>
6772
i.type === "group" ?
6873
<GroupView
69-
key={ index } group={ i } indices={ [collectionIndex, index] } />
74+
key={ index } group={ i } indices={ [collectionIndex, index] }
75+
collectionId={ collection.timestamp } />
7076
:
71-
<TabView key={ index } tab={ i } indices={ [collectionIndex, index] } />
77+
<TabView
78+
key={ index } tab={ i } indices={ [collectionIndex, index] }
79+
collectionId={ collection.timestamp } />
7280
) }
7381
</SortableContext>
7482
</div>
@@ -85,4 +93,5 @@ export type CollectionViewProps =
8593
collection: CollectionItem;
8694
index: number;
8795
dragOverlay?: boolean;
96+
compact?: boolean | null;
8897
};

entrypoints/sidepanel/components/EditDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default function EditDialog(props: GroupEditDialogProps): ReactElement
8787
value={ color === "pinned" ? i18n.t("groups.pinned") : title }
8888
onChange={ (_, e) => setTitle(e.value) } />
8989
</fui.Field>
90-
<fui.Field label="Color">
90+
<fui.Field label={ i18n.t("dialogs.edit.color") }>
9191
<div className={ cls.colorPicker } { ...horizontalNavigationAttributes }>
9292
{ (props.type === "group" && (!props.hidePinned || props.group?.pinned)) &&
9393
<fui.ToggleButton

entrypoints/sidepanel/components/GroupView.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import GroupMoreMenu from "./collections/GroupMoreMenu";
1414
import { useStyles_GroupView } from "./GroupView.styles";
1515
import TabView from "./TabView";
1616

17-
export default function GroupView({ group, indices, dragOverlay }: GroupViewProps): ReactElement
17+
export default function GroupView({ group, indices, dragOverlay, collectionId }: GroupViewProps): ReactElement
1818
{
1919
const [alwaysShowToolbars] = useSettings("alwaysShowToolbars");
2020
const { tilesView } = useCollections();
@@ -101,7 +101,9 @@ export default function GroupView({ group, indices, dragOverlay }: GroupViewProp
101101
strategy={ !tilesView ? verticalListSortingStrategy : horizontalListSortingStrategy }
102102
>
103103
{ group.items.map((i, index) =>
104-
<TabView key={ index } tab={ i } indices={ [...indices, index] } />
104+
<TabView
105+
key={ index } tab={ i } indices={ [...indices, index] }
106+
collectionId={ collectionId } />
105107
) }
106108
</SortableContext>
107109
</div>
@@ -117,4 +119,5 @@ export type GroupViewProps =
117119
group: GroupItem;
118120
indices: number[];
119121
dragOverlay?: boolean;
122+
collectionId: number;
120123
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { track } from "@/features/analytics";
2+
import { TabItem } from "@/models/CollectionModels";
3+
import { Button, DialogActions, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger, Field, Input, makeStyles, tokens } from "@fluentui/react-components";
4+
5+
export default function TabEditDialog({ tab, onSave }: TabEditDialogProps): React.ReactElement
6+
{
7+
const cls = useStyles();
8+
9+
const [title, setTitle] = useState(tab.title ?? "");
10+
const [url, setUrl] = useState(tab.url);
11+
const isValid = useMemo(() => url.trim().length > 0, [url]);
12+
13+
const onSubmit = (e: React.FormEvent<HTMLFormElement>) =>
14+
{
15+
e.preventDefault();
16+
track("item_edited", { type: "tab" });
17+
onSave({
18+
...tab,
19+
title: title.trim().length > 0 ? title : undefined,
20+
url: url.trim()
21+
});
22+
};
23+
24+
return (
25+
<DialogSurface>
26+
<form onSubmit={ onSubmit }>
27+
<DialogBody>
28+
<DialogTitle>{ i18n.t("dialogs.edit.title.edit_tab") }</DialogTitle>
29+
<DialogContent className={ cls.content }>
30+
<Input
31+
value={ title } onChange={ (_, e) => setTitle(e.value) }
32+
placeholder={ i18n.t("dialogs.edit.collection_title") } />
33+
<Field validationMessage={ isValid ? undefined : i18n.t("dialogs.edit.url_error") }>
34+
<Input
35+
value={ url } onChange={ (_, e) => setUrl(e.value) }
36+
placeholder="URL" />
37+
</Field>
38+
</DialogContent>
39+
40+
<DialogActions>
41+
<DialogTrigger disableButtonEnhancement>
42+
<Button disabled={ !isValid } appearance="primary" as="button" type="submit">
43+
{ i18n.t("common.actions.save") }
44+
</Button>
45+
</DialogTrigger>
46+
<DialogTrigger disableButtonEnhancement>
47+
<Button appearance="subtle">{ i18n.t("common.actions.cancel") }</Button>
48+
</DialogTrigger>
49+
</DialogActions>
50+
</DialogBody>
51+
</form>
52+
</DialogSurface>
53+
);
54+
}
55+
56+
const useStyles = makeStyles({
57+
content:
58+
{
59+
display: "flex",
60+
flexFlow: "column",
61+
gap: tokens.spacingVerticalMNudge
62+
}
63+
});
64+
65+
export type TabEditDialogProps =
66+
{
67+
tab: TabItem;
68+
onSave: (updatedTab: TabItem) => void;
69+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useDangerStyles } from "@/hooks/useDangerStyles";
2+
import { Button, Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, Tooltip } from "@fluentui/react-components";
3+
import { bundleIcon, Delete20Filled, Delete20Regular, Edit20Filled, Edit20Regular, MoreHorizontal20Regular } from "@fluentui/react-icons";
4+
import { ButtonHTMLAttributes } from "react";
5+
6+
export default function TabMoreButton({ onEdit, onDelete, ...props }: TabMoreButtonProps): React.ReactElement
7+
{
8+
const EditIcon = bundleIcon(Edit20Filled, Edit20Regular);
9+
const DeleteIcon = bundleIcon(Delete20Filled, Delete20Regular);
10+
const dangerCls = useDangerStyles();
11+
12+
return (
13+
<Menu>
14+
<Tooltip relationship="label" content={ i18n.t("common.tooltips.more") }>
15+
<MenuTrigger>
16+
<Button
17+
appearance="subtle" icon={ <MoreHorizontal20Regular /> }
18+
onClick={ ev => ev.stopPropagation() }
19+
{ ...props } />
20+
</MenuTrigger>
21+
</Tooltip>
22+
23+
<MenuPopover onClick={ ev => ev.stopPropagation() }>
24+
<MenuList>
25+
<MenuItem icon={ <EditIcon /> } onClick={ onEdit }>
26+
{ i18n.t("dialogs.edit.title.edit_tab") }
27+
</MenuItem>
28+
<MenuItem icon={ <DeleteIcon /> } className={ dangerCls.menuItem } onClick={ onDelete }>
29+
{ i18n.t("tabs.delete") }
30+
</MenuItem>
31+
</MenuList>
32+
</MenuPopover>
33+
</Menu>
34+
);
35+
}
36+
37+
export type TabMoreButtonProps =
38+
ButtonHTMLAttributes<HTMLButtonElement> &
39+
{
40+
onDelete?: () => void;
41+
onEdit?: () => void;
42+
};

entrypoints/sidepanel/components/TabView.tsx

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@ import { useDialog } from "@/contexts/DialogProvider";
44
import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider";
55
import useDndItem from "@/entrypoints/sidepanel/hooks/useDndItem";
66
import useSettings from "@/hooks/useSettings";
7-
import { TabItem } from "@/models/CollectionModels";
8-
import { Button, Caption1, Link, mergeClasses, Tooltip } from "@fluentui/react-components";
9-
import { Dismiss20Regular } from "@fluentui/react-icons";
7+
import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels";
8+
import { Caption1, Link, mergeClasses, Tooltip } from "@fluentui/react-components";
109
import { MouseEventHandler, ReactElement } from "react";
1110
import { useStyles_TabView } from "./TabView.styles";
1211
import CollectionContext, { CollectionContextType } from "../contexts/CollectionContext";
12+
import TabMoreButton from "./TabMoreButton";
13+
import TabEditDialog from "./TabEditDialog";
1314

14-
export default function TabView({ tab, indices, dragOverlay }: TabViewProps): ReactElement
15+
export default function TabView({ tab, indices, dragOverlay, collectionId }: TabViewProps): ReactElement
1516
{
16-
const { removeItem, graphics, tilesView } = useCollections();
17+
const { removeItem, graphics, tilesView, collections, updateCollection } = useCollections();
1718
const { collection } = useContext<CollectionContextType>(CollectionContext);
1819
const {
1920
setNodeRef, setActivatorNodeRef,
@@ -26,11 +27,8 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
2627

2728
const cls = useStyles_TabView();
2829

29-
const handleDelete: MouseEventHandler<HTMLButtonElement> = (args) =>
30+
const handleDelete = (): void =>
3031
{
31-
args.preventDefault();
32-
args.stopPropagation();
33-
3432
const removeIndex: number[] = [collection.timestamp, ...indices.slice(1)];
3533

3634
if (deletePrompt)
@@ -45,6 +43,26 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
4543
removeItem(...removeIndex);
4644
};
4745

46+
const handleEdit = (): void =>
47+
{
48+
if (collectionId < 0)
49+
return;
50+
51+
const updateTab = async (updatedTab: TabItem): Promise<void> =>
52+
{
53+
const collection: CollectionItem = collections!.find(i => i.timestamp === collectionId)!;
54+
55+
if (indices.length > 2)
56+
(collection.items[indices[1]] as GroupItem).items[indices[2]] = updatedTab;
57+
else
58+
collection.items[indices[1]] = updatedTab;
59+
60+
await updateCollection(collection, collection.timestamp);
61+
};
62+
63+
dialog.pushCustom(<TabEditDialog tab={ tab } onSave={ updateTab } />);
64+
};
65+
4866
const handleClick: MouseEventHandler<HTMLAnchorElement> = (args) =>
4967
{
5068
args.preventDefault();
@@ -91,12 +109,10 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
91109
</Caption1>
92110
</Tooltip>
93111

94-
<Tooltip relationship="label" content={ i18n.t("tabs.delete") }>
95-
<Button
96-
className={ mergeClasses(cls.deleteButton, showToolbar === true && cls.showDeleteButton) }
97-
appearance="subtle" icon={ <Dismiss20Regular /> }
98-
onClick={ handleDelete } />
99-
</Tooltip>
112+
<TabMoreButton
113+
className={ mergeClasses(cls.deleteButton, showToolbar === true && cls.showDeleteButton) }
114+
onEdit={ handleEdit }
115+
onDelete={ handleDelete } />
100116
</div>
101117
</Link>
102118
);
@@ -107,4 +123,5 @@ export type TabViewProps =
107123
tab: TabItem;
108124
indices: number[];
109125
dragOverlay?: boolean;
126+
collectionId: number;
110127
};

entrypoints/sidepanel/components/collections/CollectionHeader.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionT
22
import useSettings from "@/hooks/useSettings";
33
import { TabItem } from "@/models/CollectionModels";
44
import { Button, Caption1, makeStyles, mergeClasses, Subtitle2, tokens, Tooltip } from "@fluentui/react-components";
5-
import { Add20Filled, Add20Regular, bundleIcon } from "@fluentui/react-icons";
5+
import { Add20Filled, Add20Regular, bundleIcon, EyeOff16Regular } from "@fluentui/react-icons";
66
import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext";
77
import { useCollections } from "../../contexts/CollectionsProvider";
88
import CollectionMoreButton from "./CollectionMoreButton";
@@ -23,7 +23,7 @@ export default function CollectionHeader({ dragHandleRef, dragHandleProps }: Col
2323

2424
const handleAddSelected = async () =>
2525
{
26-
const [newTabs, skipCount] = await getTabsToSaveAsync();
26+
const [newTabs, skipCount] = await getTabsToSaveAsync(true);
2727

2828
if (newTabs.length > 0)
2929
await updateCollection({
@@ -45,9 +45,12 @@ export default function CollectionHeader({ dragHandleRef, dragHandleProps }: Col
4545
content={ getCollectionTitle(collection) }
4646
positioning="above-start"
4747
>
48-
<Subtitle2 truncate wrap={ false } className={ cls.titleText }>
49-
{ getCollectionTitle(collection) }
50-
</Subtitle2>
48+
<div className={ cls.titleContainer }>
49+
{ collection.hidden && <EyeOff16Regular /> }
50+
<Subtitle2 truncate wrap={ false } className={ cls.titleText }>
51+
{ getCollectionTitle(collection) }
52+
</Subtitle2>
53+
</div>
5154
</Tooltip>
5255

5356
<Caption1>
@@ -112,5 +115,11 @@ const useStyles = makeStyles({
112115
showToolbar:
113116
{
114117
display: "flex"
118+
},
119+
titleContainer:
120+
{
121+
display: "flex",
122+
alignItems: "center",
123+
gap: tokens.spacingHorizontalS
115124
}
116125
});

entrypoints/sidepanel/components/collections/CollectionMoreButton.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export default function CollectionMoreButton({ onAddSelected, onOpenChange }: Co
2222
const EditIcon = ic.bundleIcon(ic.Edit20Filled, ic.Edit20Regular);
2323
const DeleteIcon = ic.bundleIcon(ic.Delete20Filled, ic.Delete20Regular);
2424
const BookmarkIcon = ic.bundleIcon(ic.BookmarkAdd20Filled, ic.BookmarkAdd20Regular);
25+
const ShowIcon = ic.bundleIcon(ic.Eye20Filled, ic.Eye20Regular);
26+
const HideIcon = ic.bundleIcon(ic.EyeOff20Filled, ic.EyeOff20Regular);
2527

2628
const dangerCls = useDangerStyles();
2729

@@ -39,6 +41,11 @@ export default function CollectionMoreButton({ onAddSelected, onOpenChange }: Co
3941
removeItem(collection.timestamp);
4042
};
4143

44+
const toggleHidden = () =>
45+
{
46+
updateCollection({ ...collection, hidden: !collection.hidden }, collection.timestamp);
47+
};
48+
4249
const handleEdit = () =>
4350
dialog.pushCustom(
4451
<EditDialog
@@ -82,6 +89,9 @@ export default function CollectionMoreButton({ onAddSelected, onOpenChange }: Co
8289
<MenuItem icon={ <EditIcon /> } onClick={ handleEdit }>
8390
{ i18n.t("collections.menu.edit") }
8491
</MenuItem>
92+
<MenuItem icon={ collection.hidden ? <ShowIcon /> : <HideIcon /> } onClick={ toggleHidden }>
93+
{ collection.hidden ? i18n.t("collections.menu.unhide") : i18n.t("collections.menu.hide") }
94+
</MenuItem>
8595
<MenuItem icon={ <DeleteIcon /> } className={ dangerCls.menuItem } onClick={ handleDelete }>
8696
{ i18n.t("collections.menu.delete") }
8797
</MenuItem>

entrypoints/sidepanel/components/collections/GroupMoreMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default function GroupMoreMenu(): ReactElement
6767

6868
const handleAddSelected = async () =>
6969
{
70-
const [newTabs, skipCount] = await getTabsToSaveAsync();
70+
const [newTabs, skipCount] = await getTabsToSaveAsync(true);
7171

7272
if (newTabs.length > 0)
7373
await updateGroup({

0 commit comments

Comments
 (0)