1
0
mirror of https://github.com/XFox111/TabsAsideExtension.git synced 2026-04-22 07:58:01 +03:00

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] <support@github.com>
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](https://github.com/eslint/eslint/compare/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] <support@github.com>
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] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
2026-01-03 21:13:08 +03:00
committed by GitHub
parent fdac0c0766
commit 58d8e864e0
29 changed files with 961 additions and 667 deletions
@@ -19,6 +19,11 @@ export const useStyles_CollectionView = makeStyles({
"&:hover": "&:hover":
{ {
boxShadow: tokens.shadow4 boxShadow: tokens.shadow4
},
"&:not(:focus-within) .compact":
{
display: "none"
} }
}, },
color: color:
@@ -12,7 +12,12 @@ import { useStyles_CollectionView } from "./CollectionView.styles";
import GroupView from "./GroupView"; import GroupView from "./GroupView";
import TabView from "./TabView"; import TabView from "./TabView";
export default function CollectionView({ collection, index: collectionIndex, dragOverlay }: CollectionViewProps): ReactElement export default function CollectionView({
collection,
index: collectionIndex,
dragOverlay,
compact
}: CollectionViewProps): ReactElement
{ {
const { tilesView } = useCollections(); const { tilesView } = useCollections();
const { const {
@@ -53,12 +58,12 @@ export default function CollectionView({ collection, index: collectionIndex, dra
{ (!activeItem || activeItem.item.type !== "collection") && !dragOverlay && { (!activeItem || activeItem.item.type !== "collection") && !dragOverlay &&
<> <>
{ collection.items.length < 1 ? { collection.items.length < 1 ?
<div className={ cls.empty }> <div className={ mergeClasses(cls.empty, compact === true && "compact") }>
<CollectionsRegular fontSize={ 32 } /> <CollectionsRegular fontSize={ 32 } />
<Body1Strong>{ i18n.t("collections.empty") }</Body1Strong> <Body1Strong>{ i18n.t("collections.empty") }</Body1Strong>
</div> </div>
: :
<div className={ mergeClasses(cls.list, !tilesView && cls.verticalList) }> <div className={ mergeClasses(cls.list, !tilesView && cls.verticalList, compact === true && "compact") }>
<SortableContext <SortableContext
items={ collection.items.map((_, index) => [collectionIndex, index].join("/")) } items={ collection.items.map((_, index) => [collectionIndex, index].join("/")) }
strategy={ tilesView ? horizontalListSortingStrategy : verticalListSortingStrategy } strategy={ tilesView ? horizontalListSortingStrategy : verticalListSortingStrategy }
@@ -66,9 +71,12 @@ export default function CollectionView({ collection, index: collectionIndex, dra
{ collection.items.map((i, index) => { collection.items.map((i, index) =>
i.type === "group" ? i.type === "group" ?
<GroupView <GroupView
key={ index } group={ i } indices={ [collectionIndex, index] } /> key={ index } group={ i } indices={ [collectionIndex, index] }
collectionId={ collection.timestamp } />
: :
<TabView key={ index } tab={ i } indices={ [collectionIndex, index] } /> <TabView
key={ index } tab={ i } indices={ [collectionIndex, index] }
collectionId={ collection.timestamp } />
) } ) }
</SortableContext> </SortableContext>
</div> </div>
@@ -85,4 +93,5 @@ export type CollectionViewProps =
collection: CollectionItem; collection: CollectionItem;
index: number; index: number;
dragOverlay?: boolean; dragOverlay?: boolean;
compact?: boolean | null;
}; };
@@ -87,7 +87,7 @@ export default function EditDialog(props: GroupEditDialogProps): ReactElement
value={ color === "pinned" ? i18n.t("groups.pinned") : title } value={ color === "pinned" ? i18n.t("groups.pinned") : title }
onChange={ (_, e) => setTitle(e.value) } /> onChange={ (_, e) => setTitle(e.value) } />
</fui.Field> </fui.Field>
<fui.Field label="Color"> <fui.Field label={ i18n.t("dialogs.edit.color") }>
<div className={ cls.colorPicker } { ...horizontalNavigationAttributes }> <div className={ cls.colorPicker } { ...horizontalNavigationAttributes }>
{ (props.type === "group" && (!props.hidePinned || props.group?.pinned)) && { (props.type === "group" && (!props.hidePinned || props.group?.pinned)) &&
<fui.ToggleButton <fui.ToggleButton
@@ -14,7 +14,7 @@ import GroupMoreMenu from "./collections/GroupMoreMenu";
import { useStyles_GroupView } from "./GroupView.styles"; import { useStyles_GroupView } from "./GroupView.styles";
import TabView from "./TabView"; import TabView from "./TabView";
export default function GroupView({ group, indices, dragOverlay }: GroupViewProps): ReactElement export default function GroupView({ group, indices, dragOverlay, collectionId }: GroupViewProps): ReactElement
{ {
const [alwaysShowToolbars] = useSettings("alwaysShowToolbars"); const [alwaysShowToolbars] = useSettings("alwaysShowToolbars");
const { tilesView } = useCollections(); const { tilesView } = useCollections();
@@ -101,7 +101,9 @@ export default function GroupView({ group, indices, dragOverlay }: GroupViewProp
strategy={ !tilesView ? verticalListSortingStrategy : horizontalListSortingStrategy } strategy={ !tilesView ? verticalListSortingStrategy : horizontalListSortingStrategy }
> >
{ group.items.map((i, index) => { group.items.map((i, index) =>
<TabView key={ index } tab={ i } indices={ [...indices, index] } /> <TabView
key={ index } tab={ i } indices={ [...indices, index] }
collectionId={ collectionId } />
) } ) }
</SortableContext> </SortableContext>
</div> </div>
@@ -117,4 +119,5 @@ export type GroupViewProps =
group: GroupItem; group: GroupItem;
indices: number[]; indices: number[];
dragOverlay?: boolean; dragOverlay?: boolean;
collectionId: number;
}; };
@@ -0,0 +1,69 @@
import { track } from "@/features/analytics";
import { TabItem } from "@/models/CollectionModels";
import { Button, DialogActions, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger, Field, Input, makeStyles, tokens } from "@fluentui/react-components";
export default function TabEditDialog({ tab, onSave }: TabEditDialogProps): React.ReactElement
{
const cls = useStyles();
const [title, setTitle] = useState(tab.title ?? "");
const [url, setUrl] = useState(tab.url);
const isValid = useMemo(() => url.trim().length > 0, [url]);
const onSubmit = (e: React.FormEvent<HTMLFormElement>) =>
{
e.preventDefault();
track("item_edited", { type: "tab" });
onSave({
...tab,
title: title.trim().length > 0 ? title : undefined,
url: url.trim()
});
};
return (
<DialogSurface>
<form onSubmit={ onSubmit }>
<DialogBody>
<DialogTitle>{ i18n.t("dialogs.edit.title.edit_tab") }</DialogTitle>
<DialogContent className={ cls.content }>
<Input
value={ title } onChange={ (_, e) => setTitle(e.value) }
placeholder={ i18n.t("dialogs.edit.collection_title") } />
<Field validationMessage={ isValid ? undefined : i18n.t("dialogs.edit.url_error") }>
<Input
value={ url } onChange={ (_, e) => setUrl(e.value) }
placeholder="URL" />
</Field>
</DialogContent>
<DialogActions>
<DialogTrigger disableButtonEnhancement>
<Button disabled={ !isValid } appearance="primary" as="button" type="submit">
{ i18n.t("common.actions.save") }
</Button>
</DialogTrigger>
<DialogTrigger disableButtonEnhancement>
<Button appearance="subtle">{ i18n.t("common.actions.cancel") }</Button>
</DialogTrigger>
</DialogActions>
</DialogBody>
</form>
</DialogSurface>
);
}
const useStyles = makeStyles({
content:
{
display: "flex",
flexFlow: "column",
gap: tokens.spacingVerticalMNudge
}
});
export type TabEditDialogProps =
{
tab: TabItem;
onSave: (updatedTab: TabItem) => void;
};
@@ -0,0 +1,42 @@
import { useDangerStyles } from "@/hooks/useDangerStyles";
import { Button, Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, Tooltip } from "@fluentui/react-components";
import { bundleIcon, Delete20Filled, Delete20Regular, Edit20Filled, Edit20Regular, MoreHorizontal20Regular } from "@fluentui/react-icons";
import { ButtonHTMLAttributes } from "react";
export default function TabMoreButton({ onEdit, onDelete, ...props }: TabMoreButtonProps): React.ReactElement
{
const EditIcon = bundleIcon(Edit20Filled, Edit20Regular);
const DeleteIcon = bundleIcon(Delete20Filled, Delete20Regular);
const dangerCls = useDangerStyles();
return (
<Menu>
<Tooltip relationship="label" content={ i18n.t("common.tooltips.more") }>
<MenuTrigger>
<Button
appearance="subtle" icon={ <MoreHorizontal20Regular /> }
onClick={ ev => ev.stopPropagation() }
{ ...props } />
</MenuTrigger>
</Tooltip>
<MenuPopover onClick={ ev => ev.stopPropagation() }>
<MenuList>
<MenuItem icon={ <EditIcon /> } onClick={ onEdit }>
{ i18n.t("dialogs.edit.title.edit_tab") }
</MenuItem>
<MenuItem icon={ <DeleteIcon /> } className={ dangerCls.menuItem } onClick={ onDelete }>
{ i18n.t("tabs.delete") }
</MenuItem>
</MenuList>
</MenuPopover>
</Menu>
);
}
export type TabMoreButtonProps =
ButtonHTMLAttributes<HTMLButtonElement> &
{
onDelete?: () => void;
onEdit?: () => void;
};
+32 -15
View File
@@ -4,16 +4,17 @@ import { useDialog } from "@/contexts/DialogProvider";
import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider"; import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider";
import useDndItem from "@/entrypoints/sidepanel/hooks/useDndItem"; import useDndItem from "@/entrypoints/sidepanel/hooks/useDndItem";
import useSettings from "@/hooks/useSettings"; import useSettings from "@/hooks/useSettings";
import { TabItem } from "@/models/CollectionModels"; import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels";
import { Button, Caption1, Link, mergeClasses, Tooltip } from "@fluentui/react-components"; import { Caption1, Link, mergeClasses, Tooltip } from "@fluentui/react-components";
import { Dismiss20Regular } from "@fluentui/react-icons";
import { MouseEventHandler, ReactElement } from "react"; import { MouseEventHandler, ReactElement } from "react";
import { useStyles_TabView } from "./TabView.styles"; import { useStyles_TabView } from "./TabView.styles";
import CollectionContext, { CollectionContextType } from "../contexts/CollectionContext"; import CollectionContext, { CollectionContextType } from "../contexts/CollectionContext";
import TabMoreButton from "./TabMoreButton";
import TabEditDialog from "./TabEditDialog";
export default function TabView({ tab, indices, dragOverlay }: TabViewProps): ReactElement export default function TabView({ tab, indices, dragOverlay, collectionId }: TabViewProps): ReactElement
{ {
const { removeItem, graphics, tilesView } = useCollections(); const { removeItem, graphics, tilesView, collections, updateCollection } = useCollections();
const { collection } = useContext<CollectionContextType>(CollectionContext); const { collection } = useContext<CollectionContextType>(CollectionContext);
const { const {
setNodeRef, setActivatorNodeRef, setNodeRef, setActivatorNodeRef,
@@ -26,11 +27,8 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
const cls = useStyles_TabView(); const cls = useStyles_TabView();
const handleDelete: MouseEventHandler<HTMLButtonElement> = (args) => const handleDelete = (): void =>
{ {
args.preventDefault();
args.stopPropagation();
const removeIndex: number[] = [collection.timestamp, ...indices.slice(1)]; const removeIndex: number[] = [collection.timestamp, ...indices.slice(1)];
if (deletePrompt) if (deletePrompt)
@@ -45,6 +43,26 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
removeItem(...removeIndex); removeItem(...removeIndex);
}; };
const handleEdit = (): void =>
{
if (collectionId < 0)
return;
const updateTab = async (updatedTab: TabItem): Promise<void> =>
{
const collection: CollectionItem = collections!.find(i => i.timestamp === collectionId)!;
if (indices.length > 2)
(collection.items[indices[1]] as GroupItem).items[indices[2]] = updatedTab;
else
collection.items[indices[1]] = updatedTab;
await updateCollection(collection, collection.timestamp);
};
dialog.pushCustom(<TabEditDialog tab={ tab } onSave={ updateTab } />);
};
const handleClick: MouseEventHandler<HTMLAnchorElement> = (args) => const handleClick: MouseEventHandler<HTMLAnchorElement> = (args) =>
{ {
args.preventDefault(); args.preventDefault();
@@ -91,12 +109,10 @@ export default function TabView({ tab, indices, dragOverlay }: TabViewProps): Re
</Caption1> </Caption1>
</Tooltip> </Tooltip>
<Tooltip relationship="label" content={ i18n.t("tabs.delete") }> <TabMoreButton
<Button className={ mergeClasses(cls.deleteButton, showToolbar === true && cls.showDeleteButton) }
className={ mergeClasses(cls.deleteButton, showToolbar === true && cls.showDeleteButton) } onEdit={ handleEdit }
appearance="subtle" icon={ <Dismiss20Regular /> } onDelete={ handleDelete } />
onClick={ handleDelete } />
</Tooltip>
</div> </div>
</Link> </Link>
); );
@@ -107,4 +123,5 @@ export type TabViewProps =
tab: TabItem; tab: TabItem;
indices: number[]; indices: number[];
dragOverlay?: boolean; dragOverlay?: boolean;
collectionId: number;
}; };
@@ -2,7 +2,7 @@ import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionT
import useSettings from "@/hooks/useSettings"; import useSettings from "@/hooks/useSettings";
import { TabItem } from "@/models/CollectionModels"; import { TabItem } from "@/models/CollectionModels";
import { Button, Caption1, makeStyles, mergeClasses, Subtitle2, tokens, Tooltip } from "@fluentui/react-components"; import { Button, Caption1, makeStyles, mergeClasses, Subtitle2, tokens, Tooltip } from "@fluentui/react-components";
import { Add20Filled, Add20Regular, bundleIcon } from "@fluentui/react-icons"; import { Add20Filled, Add20Regular, bundleIcon, EyeOff16Regular } from "@fluentui/react-icons";
import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext"; import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext";
import { useCollections } from "../../contexts/CollectionsProvider"; import { useCollections } from "../../contexts/CollectionsProvider";
import CollectionMoreButton from "./CollectionMoreButton"; import CollectionMoreButton from "./CollectionMoreButton";
@@ -23,7 +23,7 @@ export default function CollectionHeader({ dragHandleRef, dragHandleProps }: Col
const handleAddSelected = async () => const handleAddSelected = async () =>
{ {
const [newTabs, skipCount] = await getTabsToSaveAsync(); const [newTabs, skipCount] = await getTabsToSaveAsync(true);
if (newTabs.length > 0) if (newTabs.length > 0)
await updateCollection({ await updateCollection({
@@ -45,9 +45,12 @@ export default function CollectionHeader({ dragHandleRef, dragHandleProps }: Col
content={ getCollectionTitle(collection) } content={ getCollectionTitle(collection) }
positioning="above-start" positioning="above-start"
> >
<Subtitle2 truncate wrap={ false } className={ cls.titleText }> <div className={ cls.titleContainer }>
{ getCollectionTitle(collection) } { collection.hidden && <EyeOff16Regular /> }
</Subtitle2> <Subtitle2 truncate wrap={ false } className={ cls.titleText }>
{ getCollectionTitle(collection) }
</Subtitle2>
</div>
</Tooltip> </Tooltip>
<Caption1> <Caption1>
@@ -112,5 +115,11 @@ const useStyles = makeStyles({
showToolbar: showToolbar:
{ {
display: "flex" display: "flex"
},
titleContainer:
{
display: "flex",
alignItems: "center",
gap: tokens.spacingHorizontalS
} }
}); });
@@ -22,6 +22,8 @@ export default function CollectionMoreButton({ onAddSelected, onOpenChange }: Co
const EditIcon = ic.bundleIcon(ic.Edit20Filled, ic.Edit20Regular); const EditIcon = ic.bundleIcon(ic.Edit20Filled, ic.Edit20Regular);
const DeleteIcon = ic.bundleIcon(ic.Delete20Filled, ic.Delete20Regular); const DeleteIcon = ic.bundleIcon(ic.Delete20Filled, ic.Delete20Regular);
const BookmarkIcon = ic.bundleIcon(ic.BookmarkAdd20Filled, ic.BookmarkAdd20Regular); const BookmarkIcon = ic.bundleIcon(ic.BookmarkAdd20Filled, ic.BookmarkAdd20Regular);
const ShowIcon = ic.bundleIcon(ic.Eye20Filled, ic.Eye20Regular);
const HideIcon = ic.bundleIcon(ic.EyeOff20Filled, ic.EyeOff20Regular);
const dangerCls = useDangerStyles(); const dangerCls = useDangerStyles();
@@ -39,6 +41,11 @@ export default function CollectionMoreButton({ onAddSelected, onOpenChange }: Co
removeItem(collection.timestamp); removeItem(collection.timestamp);
}; };
const toggleHidden = () =>
{
updateCollection({ ...collection, hidden: !collection.hidden }, collection.timestamp);
};
const handleEdit = () => const handleEdit = () =>
dialog.pushCustom( dialog.pushCustom(
<EditDialog <EditDialog
@@ -82,6 +89,9 @@ export default function CollectionMoreButton({ onAddSelected, onOpenChange }: Co
<MenuItem icon={ <EditIcon /> } onClick={ handleEdit }> <MenuItem icon={ <EditIcon /> } onClick={ handleEdit }>
{ i18n.t("collections.menu.edit") } { i18n.t("collections.menu.edit") }
</MenuItem> </MenuItem>
<MenuItem icon={ collection.hidden ? <ShowIcon /> : <HideIcon /> } onClick={ toggleHidden }>
{ collection.hidden ? i18n.t("collections.menu.unhide") : i18n.t("collections.menu.hide") }
</MenuItem>
<MenuItem icon={ <DeleteIcon /> } className={ dangerCls.menuItem } onClick={ handleDelete }> <MenuItem icon={ <DeleteIcon /> } className={ dangerCls.menuItem } onClick={ handleDelete }>
{ i18n.t("collections.menu.delete") } { i18n.t("collections.menu.delete") }
</MenuItem> </MenuItem>
@@ -67,7 +67,7 @@ export default function GroupMoreMenu(): ReactElement
const handleAddSelected = async () => const handleAddSelected = async () =>
{ {
const [newTabs, skipCount] = await getTabsToSaveAsync(); const [newTabs, skipCount] = await getTabsToSaveAsync(true);
if (newTabs.length > 0) if (newTabs.length > 0)
await updateGroup({ await updateGroup({
@@ -51,5 +51,9 @@ export const useStyles_CollectionListView = makeStyles({
{ {
gridTemplateColumns: "repeat(auto-fit, minmax(360px, 1fr))" gridTemplateColumns: "repeat(auto-fit, minmax(360px, 1fr))"
} }
},
compactList:
{
alignItems: "baseline"
} }
}); });
@@ -18,10 +18,10 @@ import CollectionContext from "../../contexts/CollectionContext";
import { useCollections } from "../../contexts/CollectionsProvider"; import { useCollections } from "../../contexts/CollectionsProvider";
import applyReorder from "../../utils/dnd/applyReorder"; import applyReorder from "../../utils/dnd/applyReorder";
import { collisionDetector } from "../../utils/dnd/collisionDetector"; import { collisionDetector } from "../../utils/dnd/collisionDetector";
import { snapHandleToCursor } from "../../utils/dnd/snapHandleToCursor";
import { useStyles_CollectionListView } from "./CollectionListView.styles"; import { useStyles_CollectionListView } from "./CollectionListView.styles";
import SearchBar from "./SearchBar"; import SearchBar from "./SearchBar";
import StorageCapacityIssueMessage from "./messages/StorageCapacityIssueMessage"; import StorageCapacityIssueMessage from "./messages/StorageCapacityIssueMessage";
import { snapHandleToCursor } from "../../utils/dnd/snapHandleToCursor";
export default function CollectionListView(): ReactElement export default function CollectionListView(): ReactElement
{ {
@@ -30,17 +30,19 @@ export default function CollectionListView(): ReactElement
const [sortMode, setSortMode] = useSettings("sortMode"); const [sortMode, setSortMode] = useSettings("sortMode");
const [query, setQuery] = useState<string>(""); const [query, setQuery] = useState<string>("");
const [colors, setColors] = useState<CollectionFilterType["colors"]>([]); const [colors, setColors] = useState<CollectionFilterType["colors"]>([]);
const [showHidden, setShowHidden] = useState<boolean>(false);
const [compactView] = useSettings("compactView");
const [active, setActive] = useState<DndItem | null>(null); const [active, setActive] = useState<DndItem | null>(null);
const sensors = useSensors( const sensors = useSensors(
useSensor(MouseSensor, { activationConstraint: { delay: 10, tolerance: 20 } }), useSensor(MouseSensor, { activationConstraint: { delay: 150, tolerance: 20 } }),
useSensor(TouchSensor, { activationConstraint: { delay: 300, tolerance: 20 } }) useSensor(TouchSensor, { activationConstraint: { delay: 300, tolerance: 20 } })
); );
const resultList = useMemo( const resultList = useMemo(
() => sortCollections(filterCollections(collections, { query, colors }), sortMode), () => sortCollections(filterCollections(collections, { query, colors, showHidden }), sortMode),
[query, colors, sortMode, collections] [query, colors, sortMode, collections, showHidden]
); );
const cls = useStyles_CollectionListView(); const cls = useStyles_CollectionListView();
@@ -49,6 +51,13 @@ export default function CollectionListView(): ReactElement
{ {
setQuery(""); setQuery("");
setColors([]); setColors([]);
setShowHidden(false);
}, []);
const updateFilter = useCallback((newColors: CollectionFilterType["colors"], newShowHidden: boolean) =>
{
setColors(newColors);
setShowHidden(newShowHidden);
}, []); }, []);
const handleDragStart = (event: DragStartEvent): void => const handleDragStart = (event: DragStartEvent): void =>
@@ -87,8 +96,9 @@ export default function CollectionListView(): ReactElement
<article className={ cls.root }> <article className={ cls.root }>
<SearchBar <SearchBar
query={ query } onQueryChange={ setQuery } query={ query } onQueryChange={ setQuery }
filter={ colors } onFilterChange={ setColors } filter={ colors } onFilterChange={ updateFilter }
sort={ sortMode } onSortChange={ setSortMode } sort={ sortMode } onSortChange={ setSortMode }
showHidden={ showHidden }
onReset={ resetFilter } /> onReset={ resetFilter } />
<CtaMessage className={ cls.msgBar } /> <CtaMessage className={ cls.msgBar } />
@@ -105,7 +115,7 @@ export default function CollectionListView(): ReactElement
</Button> </Button>
</div> </div>
: :
<section className={ mergeClasses(cls.collectionList, !tilesView && cls.listView) }> <section className={ mergeClasses(cls.collectionList, !tilesView && cls.listView, !!(!tilesView && compactView) && cls.compactList) }>
<DndContext <DndContext
sensors={ sensors } sensors={ sensors }
collisionDetection={ collisionDetector(!tilesView) } collisionDetection={ collisionDetector(!tilesView) }
@@ -118,7 +128,7 @@ export default function CollectionListView(): ReactElement
strategy={ tilesView ? verticalListSortingStrategy : rectSortingStrategy } strategy={ tilesView ? verticalListSortingStrategy : rectSortingStrategy }
> >
{ resultList.map((collection, index) => { resultList.map((collection, index) =>
<CollectionView key={ index } collection={ collection } index={ index } /> <CollectionView key={ index } collection={ collection } index={ index } compact={ compactView } />
) } ) }
</SortableContext> </SortableContext>
@@ -135,9 +145,9 @@ export default function CollectionListView(): ReactElement
} } } }
> >
{ active.item.type === "group" ? { active.item.type === "group" ?
<GroupView group={ active.item } indices={ [-1] } dragOverlay /> <GroupView group={ active.item } indices={ [-1] } collectionId={ -1 } dragOverlay />
: :
<TabView tab={ active.item } indices={ [-1] } dragOverlay /> <TabView tab={ active.item } indices={ [-1] } collectionId={ -1 } dragOverlay />
} }
</CollectionContext.Provider> </CollectionContext.Provider>
: :
@@ -3,32 +3,48 @@ import * as fui from "@fluentui/react-components";
import * as ic from "@fluentui/react-icons"; import * as ic from "@fluentui/react-icons";
import { CollectionFilterType } from "../../utils/filterCollections"; import { CollectionFilterType } from "../../utils/filterCollections";
export default function FilterCollectionsButton({ value, onChange }: FilterCollectionsButtonProps): React.ReactElement export default function FilterCollectionsButton({ value, onChange, showHidden }: FilterCollectionsButtonProps): React.ReactElement
{ {
const cls = useStyles(); const cls = useStyles();
const colorCls = useGroupColors(); const colorCls = useGroupColors();
const ColorFilterIcon = ic.bundleIcon(ic.Color20Filled, ic.Color20Regular); const FilterIcon = ic.bundleIcon(ic.Filter20Filled, ic.Filter20Regular);
const ColorIcon = ic.bundleIcon(ic.Circle20Filled, ic.CircleHalfFill20Regular); const ColorIcon = ic.bundleIcon(ic.Circle20Filled, ic.CircleHalfFill20Regular);
const NoColorIcon = ic.bundleIcon(ic.CircleOffFilled, ic.CircleOffRegular); const NoColorIcon = ic.bundleIcon(ic.CircleOffFilled, ic.CircleOffRegular);
const AnyColorIcon = ic.bundleIcon(ic.PhotoFilter20Filled, ic.PhotoFilter20Regular); const AnyColorIcon = ic.bundleIcon(ic.PhotoFilter20Filled, ic.PhotoFilter20Regular);
const HiddenIcon = ic.bundleIcon(ic.EyeOff20Filled, ic.EyeOff20Regular);
const values: Record<string, string[]> = useMemo(() => ({
default: !value || value.length < 1 ? ["any"] : [],
colors: value || [],
hidden: showHidden ? ["show"] : []
}), [value, showHidden]);
const onCheckedValueChange = useCallback((_: fui.MenuCheckedValueChangeEvent, e: fui.MenuCheckedValueChangeData) =>
{
if (e.name === "hidden")
onChange?.(value ?? [], e.checkedItems.includes("show"));
else
onChange?.(e.checkedItems.includes("any") ? [] : e.checkedItems as CollectionFilterType["colors"], showHidden ?? false);
}, [onChange, showHidden, value]);
return ( return (
<fui.Menu <fui.Menu
checkedValues={ !value || value.length < 1 ? { default: ["any"] } : { colors: value } } checkedValues={ values }
onCheckedValueChange={ (_, e) => onCheckedValueChange={ onCheckedValueChange }
onChange?.(e.checkedItems.includes("any") ? [] : e.checkedItems as CollectionFilterType["colors"])
}
> >
<fui.Tooltip relationship="label" content={ i18n.t("main.list.searchbar.filter") }> <fui.Tooltip relationship="label" content={ i18n.t("main.list.searchbar.filter") }>
<fui.MenuTrigger disableButtonEnhancement> <fui.MenuTrigger disableButtonEnhancement>
<fui.Button appearance="subtle" icon={ <ColorFilterIcon /> } /> <fui.Button appearance="subtle" icon={ <FilterIcon /> } />
</fui.MenuTrigger> </fui.MenuTrigger>
</fui.Tooltip> </fui.Tooltip>
<fui.MenuPopover> <fui.MenuPopover>
<fui.MenuList> <fui.MenuList>
<fui.MenuItemCheckbox name="hidden" value="show" icon={ <HiddenIcon /> }>
{ i18n.t("main.list.searchbar.show_hidden") }
</fui.MenuItemCheckbox>
<fui.MenuItemCheckbox name="default" value="any" icon={ <AnyColorIcon /> }> <fui.MenuItemCheckbox name="default" value="any" icon={ <AnyColorIcon /> }>
{ i18n.t("colors.any") } { i18n.t("colors.any") }
</fui.MenuItemCheckbox> </fui.MenuItemCheckbox>
@@ -60,7 +76,8 @@ export default function FilterCollectionsButton({ value, onChange }: FilterColle
export type FilterCollectionsButtonProps = export type FilterCollectionsButtonProps =
{ {
value?: CollectionFilterType["colors"]; value?: CollectionFilterType["colors"];
onChange?: (value: CollectionFilterType["colors"]) => void; showHidden?: boolean;
onChange?: (value: CollectionFilterType["colors"], showHidden: boolean) => void;
}; };
const useStyles = fui.makeStyles({ const useStyles = fui.makeStyles({
@@ -25,7 +25,7 @@ export default function SearchBar(props: SearchBarProps): React.ReactElement
<Button appearance="subtle" icon={ <ResetIcon /> } onClick={ props.onReset } /> <Button appearance="subtle" icon={ <ResetIcon /> } onClick={ props.onReset } />
</Tooltip> </Tooltip>
} }
<FilterCollectionsButton value={ props.filter } onChange={ props.onFilterChange } /> <FilterCollectionsButton value={ props.filter } onChange={ props.onFilterChange } showHidden={ props.showHidden } />
<SortCollectionsButton value={ props.sort } onChange={ props.onSortChange } /> <SortCollectionsButton value={ props.sort } onChange={ props.onSortChange } />
</> </>
} /> } />
@@ -37,8 +37,9 @@ export type SearchBarProps =
query?: string; query?: string;
onQueryChange?: (query: string) => void; onQueryChange?: (query: string) => void;
filter?: CollectionFilterType["colors"]; filter?: CollectionFilterType["colors"];
onFilterChange?: (filter: CollectionFilterType["colors"]) => void; onFilterChange?: (filter: CollectionFilterType["colors"], showHidden: boolean) => void;
sort?: CollectionSortMode; sort?: CollectionSortMode;
showHidden?: boolean;
onSortChange?: (sort: CollectionSortMode) => void; onSortChange?: (sort: CollectionSortMode) => void;
onReset?: () => void; onReset?: () => void;
}; };
@@ -11,18 +11,32 @@ import { ReactElement } from "react";
export default function MoreButton(): ReactElement export default function MoreButton(): ReactElement
{ {
const [tilesView, setTilesView] = useSettings("tilesView"); const [tilesView, setTilesView] = useSettings("tilesView");
const [compactView, setCompactView] = useSettings("compactView");
const SettingsIcon: ic.FluentIcon = ic.bundleIcon(ic.Settings20Filled, ic.Settings20Regular); const SettingsIcon: ic.FluentIcon = ic.bundleIcon(ic.Settings20Filled, ic.Settings20Regular);
const ViewIcon: ic.FluentIcon = ic.bundleIcon(ic.GridKanban20Filled, ic.GridKanban20Regular); const GridIcon: ic.FluentIcon = ic.bundleIcon(ic.GridKanban20Filled, ic.GridKanban20Regular);
const CompactIcon: ic.FluentIcon = ic.bundleIcon(ic.ArrowMinimizeVerticalFilled, ic.ArrowMinimizeVerticalRegular);
const FeedbackIcon: ic.FluentIcon = ic.bundleIcon(ic.PersonFeedback20Filled, ic.PersonFeedback20Regular); const FeedbackIcon: ic.FluentIcon = ic.bundleIcon(ic.PersonFeedback20Filled, ic.PersonFeedback20Regular);
const LearnIcon: ic.FluentIcon = ic.bundleIcon(ic.QuestionCircle20Filled, ic.QuestionCircle20Regular); const LearnIcon: ic.FluentIcon = ic.bundleIcon(ic.QuestionCircle20Filled, ic.QuestionCircle20Regular);
const BmcIcon: ic.FluentIcon = ic.bundleIcon(BuyMeACoffee20Filled, BuyMeACoffee20Regular); const BmcIcon: ic.FluentIcon = ic.bundleIcon(BuyMeACoffee20Filled, BuyMeACoffee20Regular);
const checkedValues = useMemo(() => ({
view: [
tilesView ? "tiles" : "",
compactView ? "compact" : ""
]
}), [tilesView, compactView]);
const onCheckedValueChange = (_: unknown, e: fui.MenuCheckedValueChangeData) =>
{
setTilesView(e.checkedItems.includes("tiles"));
setCompactView(e.checkedItems.includes("compact"));
};
return ( return (
<fui.Menu <fui.Menu
hasIcons hasCheckmarks hasIcons hasCheckmarks
checkedValues={ { tilesView: tilesView ? ["true"] : [] } } checkedValues={ checkedValues } onCheckedValueChange={ onCheckedValueChange }
onCheckedValueChange={ (_, e) => setTilesView(e.checkedItems.length > 0) }
> >
<fui.Tooltip relationship="label" content={ i18n.t("common.tooltips.more") }> <fui.Tooltip relationship="label" content={ i18n.t("common.tooltips.more") }>
<fui.MenuTrigger disableButtonEnhancement> <fui.MenuTrigger disableButtonEnhancement>
@@ -36,9 +50,12 @@ export default function MoreButton(): ReactElement
<fui.MenuItem icon={ <SettingsIcon /> } onClick={ () => browser.runtime.openOptionsPage() }> <fui.MenuItem icon={ <SettingsIcon /> } onClick={ () => browser.runtime.openOptionsPage() }>
{ i18n.t("options_page.title") } { i18n.t("options_page.title") }
</fui.MenuItem> </fui.MenuItem>
<fui.MenuItemCheckbox name="tilesView" value="true" icon={ <ViewIcon /> }> <fui.MenuItemCheckbox name="view" value="tiles" icon={ <GridIcon /> }>
{ i18n.t("main.header.menu.tiles_view") } { i18n.t("main.header.menu.tiles_view") }
</fui.MenuItemCheckbox> </fui.MenuItemCheckbox>
<fui.MenuItemCheckbox name="view" value="compact" icon={ <CompactIcon /> }>
{ i18n.t("main.header.menu.compact_view") }
</fui.MenuItemCheckbox>
<fui.MenuDivider /> <fui.MenuDivider />
@@ -9,13 +9,16 @@ export default function filterCollections(
if (!collections || collections.length < 1) if (!collections || collections.length < 1)
return []; return [];
if (!filter.query && filter.colors.length < 1) if (!filter.query && filter.colors.length < 1 && filter.showHidden)
return collections; return collections;
const query: string = filter.query.toLocaleLowerCase(); const query: string = filter.query.toLocaleLowerCase();
return collections.filter(collection => return collections.filter(collection =>
{ {
if (filter.showHidden === false && collection.hidden === true)
return false;
let querySatisfied: boolean = query.length < 1 || let querySatisfied: boolean = query.length < 1 ||
getCollectionTitle(collection).toLocaleLowerCase().includes(query); getCollectionTitle(collection).toLocaleLowerCase().includes(query);
let colorSatisfied: boolean = filter.colors.length < 1 || let colorSatisfied: boolean = filter.colors.length < 1 ||
@@ -62,4 +65,5 @@ export type CollectionFilterType =
{ {
query: string; query: string;
colors: (`${Browser.tabGroups.Color}` | "none")[]; colors: (`${Browser.tabGroups.Color}` | "none")[];
showHidden: boolean;
}; };
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Add empty group" add_group: "Add empty group"
export_bookmarks: "Export to bookmarks" export_bookmarks: "Export to bookmarks"
edit: "Edit collection" edit: "Edit collection"
hide: "Hide collection"
unhide: "Unhide collection"
groups: groups:
title: "Group" title: "Group"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Edit collection" edit_collection: "Edit collection"
edit_group: "Edit group" edit_group: "Edit group"
edit_tab: "Edit tab"
new_group: "New group" new_group: "New group"
new_collection: "New collection" new_collection: "New collection"
collection_title: "Title" collection_title: "Title"
color: "Color" color: "Color"
url_error: "URL is required"
main: main:
header: header:
create_collection: "Create new collection" create_collection: "Create new collection"
menu: menu:
tiles_view: "Tiles view" tiles_view: "Tiles view"
compact_view: "Compact view"
changelog: "What's new?" changelog: "What's new?"
list: list:
searchbar: searchbar:
title: "Search" title: "Search"
filter: "Filter" filter: "Filter"
show_hidden: "Show hidden"
sort: sort:
title: "Sort" title: "Sort"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Agregar grupo vacío" add_group: "Agregar grupo vacío"
export_bookmarks: "Exportar a marcadores" export_bookmarks: "Exportar a marcadores"
edit: "Editar colección" edit: "Editar colección"
hide: "Ocultar colección"
unhide: "Mostrar colección"
groups: groups:
title: "Grupo" title: "Grupo"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Editar colección" edit_collection: "Editar colección"
edit_group: "Editar grupo" edit_group: "Editar grupo"
edit_tab: "Editar pestaña"
new_group: "Nuevo grupo" new_group: "Nuevo grupo"
new_collection: "Nueva colección" new_collection: "Nueva colección"
collection_title: "Título" collection_title: "Título"
color: "Color" color: "Color"
url_error: "La URL es obligatoria"
main: main:
header: header:
create_collection: "Crear nueva colección" create_collection: "Crear nueva colección"
menu: menu:
tiles_view: "Vista de mosaicos" tiles_view: "Vista de mosaicos"
compact_view: "Vista compacta"
changelog: "¿Qué hay de nuevo?" changelog: "¿Qué hay de nuevo?"
list: list:
searchbar: searchbar:
title: "Buscar" title: "Buscar"
filter: "Filtrar" filter: "Filtrar"
show_hidden: "Mostrar ocultas"
sort: sort:
title: "Ordenar" title: "Ordenar"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Aggiungi gruppo vuoto" add_group: "Aggiungi gruppo vuoto"
export_bookmarks: "Esporta nei segnalibri" export_bookmarks: "Esporta nei segnalibri"
edit: "Modifica collezione" edit: "Modifica collezione"
hide: "Nascondi collezione"
unhide: "Mostra collezione"
groups: groups:
title: "Gruppo" title: "Gruppo"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Modifica collezione" edit_collection: "Modifica collezione"
edit_group: "Modifica gruppo" edit_group: "Modifica gruppo"
edit_tab: "Modifica scheda"
new_group: "Nuovo gruppo" new_group: "Nuovo gruppo"
new_collection: "Nuova collezione" new_collection: "Nuova collezione"
collection_title: "Titolo" collection_title: "Titolo"
color: "Colore" color: "Colore"
url_error: "L'URL è obbligatorio"
main: main:
header: header:
create_collection: "Crea nuova collezione" create_collection: "Crea nuova collezione"
menu: menu:
tiles_view: "Vista a riquadri" tiles_view: "Vista a riquadri"
compact_view: "Vista compatta"
changelog: "Cosa c'è di nuovo?" changelog: "Cosa c'è di nuovo?"
list: list:
searchbar: searchbar:
title: "Cerca" title: "Cerca"
filter: "Filtra" filter: "Filtra"
show_hidden: "Mostra nascoste"
sort: sort:
title: "Ordina" title: "Ordina"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Dodaj pustą grupę" add_group: "Dodaj pustą grupę"
export_bookmarks: "Eksportuj do zakładek" export_bookmarks: "Eksportuj do zakładek"
edit: "Edytuj kolekcję" edit: "Edytuj kolekcję"
hide: "Ukryj kolekcję"
unhide: "Pokaż kolekcję"
groups: groups:
title: "Grupa" title: "Grupa"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Edytuj kolekcję" edit_collection: "Edytuj kolekcję"
edit_group: "Edytuj grupę" edit_group: "Edytuj grupę"
edit_tab: "Edytuj zakładkę"
new_group: "Nowa grupa" new_group: "Nowa grupa"
new_collection: "Nowa kolekcja" new_collection: "Nowa kolekcja"
collection_title: "Nazwij" collection_title: "Nazwij"
color: "Kolor" color: "Kolor"
url_error: "URL jest wymagany"
main: main:
header: header:
create_collection: "Utwórz nową kolekcję" create_collection: "Utwórz nową kolekcję"
menu: menu:
tiles_view: "Kafelki" tiles_view: "Kafelki"
compact_view: "Widok kompaktowy"
changelog: "Co nowego?" changelog: "Co nowego?"
list: list:
searchbar: searchbar:
title: "Szukaj" title: "Szukaj"
filter: "Filtr" filter: "Filtr"
show_hidden: "Pokaż ukryte"
sort: sort:
title: "Sortowanie" title: "Sortowanie"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Adicionar grupo vazio" add_group: "Adicionar grupo vazio"
export_bookmarks: "Exportar para favoritos" export_bookmarks: "Exportar para favoritos"
edit: "Editar coleção" edit: "Editar coleção"
hide: "Ocultar coleção"
unhide: "Mostrar coleção"
groups: groups:
title: "Grupo" title: "Grupo"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Editar coleção" edit_collection: "Editar coleção"
edit_group: "Editar grupo" edit_group: "Editar grupo"
edit_tab: "Editar aba"
new_group: "Novo grupo" new_group: "Novo grupo"
new_collection: "Nova coleção" new_collection: "Nova coleção"
collection_title: "Título" collection_title: "Título"
color: "Cor" color: "Cor"
url_error: "A URL é obrigatória"
main: main:
header: header:
create_collection: "Criar nova coleção" create_collection: "Criar nova coleção"
menu: menu:
tiles_view: "Visualização em blocos" tiles_view: "Visualização em blocos"
compact_view: "Visualização compacta"
changelog: "O que há de novo?" changelog: "O que há de novo?"
list: list:
searchbar: searchbar:
title: "Pesquisar" title: "Pesquisar"
filter: "Filtrar" filter: "Filtrar"
show_hidden: "Mostrar ocultas"
sort: sort:
title: "Ordenar" title: "Ordenar"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Добавить пустую группу" add_group: "Добавить пустую группу"
export_bookmarks: "Экспортировать в закладки" export_bookmarks: "Экспортировать в закладки"
edit: "Редактировать коллекцию" edit: "Редактировать коллекцию"
hide: "Скрыть коллекцию"
unhide: "Показать коллекцию"
groups: groups:
title: "Группа" title: "Группа"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Редактировать коллекцию" edit_collection: "Редактировать коллекцию"
edit_group: "Редактировать группу" edit_group: "Редактировать группу"
edit_tab: "Редактировать вкладку"
new_group: "Новая группа" new_group: "Новая группа"
new_collection: "Новая коллекция" new_collection: "Новая коллекция"
collection_title: "Название" collection_title: "Название"
color: "Цвет" color: "Цвет"
url_error: "URL является обязательным"
main: main:
header: header:
create_collection: "Создать новую коллекцию" create_collection: "Создать новую коллекцию"
menu: menu:
tiles_view: "Плитки" tiles_view: "Плитки"
compact_view: "Компактный вид"
changelog: "Что нового?" changelog: "Что нового?"
list: list:
searchbar: searchbar:
title: "Поиск" title: "Поиск"
filter: "Фильтр" filter: "Фильтр"
show_hidden: "Показать скрытые"
sort: sort:
title: "Сортировка" title: "Сортировка"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "Додати порожню групу" add_group: "Додати порожню групу"
export_bookmarks: "Експортувати в закладки" export_bookmarks: "Експортувати в закладки"
edit: "Редагувати колекцію" edit: "Редагувати колекцію"
hide: "Приховати колекцію"
unhide: "Показати колекцію"
groups: groups:
title: "Група" title: "Група"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "Редагувати колекцію" edit_collection: "Редагувати колекцію"
edit_group: "Редагувати групу" edit_group: "Редагувати групу"
edit_tab: "Редагувати вкладку"
new_group: "Нова група" new_group: "Нова група"
new_collection: "Нова колекція" new_collection: "Нова колекція"
collection_title: "Назва" collection_title: "Назва"
color: "Колір" color: "Колір"
url_error: "URL є обов'язковим"
main: main:
header: header:
create_collection: "Створити нову колекцію" create_collection: "Створити нову колекцію"
menu: menu:
tiles_view: "Плитки" tiles_view: "Плитки"
compact_view: "Компактний вид"
changelog: "Що нового?" changelog: "Що нового?"
list: list:
searchbar: searchbar:
title: "Пошук" title: "Пошук"
filter: "Фільтр" filter: "Фільтр"
show_hidden: "Показати приховані"
sort: sort:
title: "Сортування" title: "Сортування"
options: options:
+6
View File
@@ -184,6 +184,8 @@ collections:
add_group: "添加空分组" add_group: "添加空分组"
export_bookmarks: "导出到书签" export_bookmarks: "导出到书签"
edit: "编辑收藏" edit: "编辑收藏"
hide: "隐藏收藏"
unhide: "显示收藏"
groups: groups:
title: "分组" title: "分组"
@@ -219,21 +221,25 @@ dialogs:
title: title:
edit_collection: "编辑收藏" edit_collection: "编辑收藏"
edit_group: "编辑分组" edit_group: "编辑分组"
edit_tab: "编辑标签页"
new_group: "新分组" new_group: "新分组"
new_collection: "新收藏" new_collection: "新收藏"
collection_title: "标题" collection_title: "标题"
color: "颜色" color: "颜色"
url_error: "需要 URL"
main: main:
header: header:
create_collection: "创建新收藏" create_collection: "创建新收藏"
menu: menu:
tiles_view: "平铺视图" tiles_view: "平铺视图"
compact_view: "紧凑视图"
changelog: "更新内容" changelog: "更新内容"
list: list:
searchbar: searchbar:
title: "搜索" title: "搜索"
filter: "筛选" filter: "筛选"
show_hidden: "显示隐藏项"
sort: sort:
title: "排序" title: "排序"
options: options:
+1
View File
@@ -30,6 +30,7 @@ export type CollectionItem =
title?: string; title?: string;
color?: `${Browser.tabGroups.Color}`; color?: `${Browser.tabGroups.Color}`;
items: (TabItem | GroupItem)[]; items: (TabItem | GroupItem)[];
hidden?: boolean;
}; };
export type GraphicsStorage = Record<string, GraphicsItem>; export type GraphicsStorage = Record<string, GraphicsItem>;
+625 -605
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,7 +1,7 @@
{ {
"name": "tabs-aside", "name": "tabs-aside",
"private": true, "private": true,
"version": "3.2.3", "version": "3.3.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "wxt", "dev": "wxt",
@@ -16,27 +16,27 @@
"@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@fluentui/react-components": "^9.72.8", "@fluentui/react-components": "^9.72.9",
"@fluentui/react-icons": "^2.0.316", "@fluentui/react-icons": "^2.0.316",
"@webext-core/messaging": "^2.3.0", "@webext-core/messaging": "^2.3.0",
"@wxt-dev/analytics": "^0.5.1", "@wxt-dev/analytics": "^0.5.1",
"@wxt-dev/i18n": "^0.2.4", "@wxt-dev/i18n": "^0.2.4",
"lzutf8": "^0.6.3", "lzutf8": "^0.6.3",
"react": "^19.2.1", "react": "^19.2.3",
"react-dom": "^19.2.1" "react-dom": "^19.2.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/css": "^0.14.1", "@eslint/css": "^0.14.1",
"@eslint/js": "^9.39.1", "@eslint/js": "^9.39.2",
"@eslint/json": "^0.14.0", "@eslint/json": "^0.14.0",
"@stylistic/eslint-plugin": "^5.6.1", "@stylistic/eslint-plugin": "^5.6.1",
"@types/react": "^19.2.7", "@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@wxt-dev/module-react": "^1.1.5", "@wxt-dev/module-react": "^1.1.5",
"eslint": "^9.39.1", "eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.49.0", "typescript-eslint": "^8.51.0",
"wxt": "^0.20.11" "wxt": "^0.20.11"
} }
} }
+2 -2
View File
@@ -1,13 +1,13 @@
import { settings } from "./settings"; import { settings } from "./settings";
export async function getTabsToSaveAsync(): Promise<[Browser.tabs.Tab[], number]> export async function getTabsToSaveAsync(forceSelected: boolean = false): Promise<[Browser.tabs.Tab[], number]>
{ {
let tabs: Browser.tabs.Tab[] = await browser.tabs.query({ let tabs: Browser.tabs.Tab[] = await browser.tabs.query({
currentWindow: true, currentWindow: true,
highlighted: true highlighted: true
}); });
if (tabs.length < 2) if (!forceSelected && tabs.length < 2)
{ {
const ignorePinned: boolean = await settings.ignorePinned.getValue(); const ignorePinned: boolean = await settings.ignorePinned.getValue();
tabs = await browser.tabs.query({ tabs = await browser.tabs.query({
+8
View File
@@ -103,5 +103,13 @@ export const settings = {
fallback: true, fallback: true,
version: 1 version: 1
} }
),
compactView: storage.defineItem<boolean>(
"sync:compactView",
{
fallback: false,
version: 1
}
) )
}; };