pl-fe: migrate even more icons

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-10-13 10:42:19 +02:00
parent 369f252035
commit f2259d0bf4
17 changed files with 30 additions and 563 deletions

View File

@ -197,7 +197,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
onOpen,
onShiftClick,
placement: initialPlacement = 'top',
src = require('@tabler/icons/outline/dots.svg'),
src = require('@phosphor-icons/core/regular/dots-three.svg'),
title = 'Menu',
width,
} = props;

View File

@ -70,7 +70,7 @@ const EventPreview: React.FC<IEventPreview> = ({ status, className, hideAction,
<div className='flex flex-wrap gap-x-2 gap-y-1 text-gray-700 dark:text-gray-600'>
<HStack alignItems='center' space={2}>
<Icon src={require('@tabler/icons/outline/user.svg')} />
<Icon src={require('@phosphor-icons/core/regular/user.svg')} />
<HStack space={1} alignItems='center' grow>
<span>
<Emojify text={account.display_name} emojis={account.emojis} />

View File

@ -20,7 +20,7 @@ const LoadGap: React.FC<ILoadGap> = ({ disabled, maxId, onClick }) => {
return (
<button className='m-0 box-border block w-full border-0 bg-transparent p-4 text-gray-900' disabled={disabled} onClick={handleClick} aria-label={intl.formatMessage(messages.load_more)}>
<Icon className='mx-auto' src={require('@tabler/icons/outline/dots.svg')} />
<Icon className='mx-auto' src={require('@phosphor-icons/core/regular/dots-three.svg')} />
</button>
);
};

View File

@ -1,533 +0,0 @@
import React, { useRef, useEffect } from 'react';
import Icon from './icon';
interface IMultiselectProps {
options: any;
// disablePreSelectedValues?: boolean;
selectedValues?: any;
displayValue: string;
placeholder?: string;
emptyRecordMsg?: string;
onSelect?: (selectedList:any, selectedItem: any) => void;
onRemove?: (selectedList:any, selectedItem: any) => void;
disable?: boolean;
className?: string;
}
function useOutsideAlerter(ref, clickEvent) {
useEffect(() => {
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
clickEvent();
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref]);
}
/**
* Component that alerts if you click outside of it
*/
function OutsideAlerter(props) {
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef, props.outsideClick);
return <div ref={wrapperRef}>{props.children}</div>;
}
/**
*
*/
export class Multiselect extends React.Component<IMultiselectProps, any> {
static defaultProps: IMultiselectProps;
/**
*
*/
constructor(props: IMultiselectProps) {
super(props);
this.state = {
inputValue: '',
options: props.options,
filteredOptions: props.options,
unfilteredOptions: props.options,
selectedValues: Object.assign([], props.selectedValues),
preSelectedValues: Object.assign([], props.selectedValues),
toggleOptionsList: false,
highlightOption: 0,
// keepSearchTerm: props.keepSearchTerm,
groupedObject: [],
};
// @ts-ignore
this.optionTimeout = null;
// @ts-ignore
this.searchWrapper = React.createRef();
// @ts-ignore
this.searchBox = React.createRef();
this.onChange = this.onChange.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
this.renderMultiselectContainer = this.renderMultiselectContainer.bind(this);
this.renderSelectedList = this.renderSelectedList.bind(this);
this.onRemoveSelectedItem = this.onRemoveSelectedItem.bind(this);
this.toggelOptionList = this.toggelOptionList.bind(this);
this.onArrowKeyNavigation = this.onArrowKeyNavigation.bind(this);
this.onSelectItem = this.onSelectItem.bind(this);
this.filterOptionsByInput = this.filterOptionsByInput.bind(this);
this.removeSelectedValuesFromOptions = this.removeSelectedValuesFromOptions.bind(this);
this.isSelectedValue = this.isSelectedValue.bind(this);
this.isDisablePreSelectedValues = this.isDisablePreSelectedValues.bind(this);
this.renderNormalOption = this.renderNormalOption.bind(this);
this.listenerCallback = this.listenerCallback.bind(this);
this.resetSelectedValues = this.resetSelectedValues.bind(this);
this.getSelectedItems = this.getSelectedItems.bind(this);
this.getSelectedItemsCount = this.getSelectedItemsCount.bind(this);
this.hideOnClickOutside = this.hideOnClickOutside.bind(this);
this.onCloseOptionList = this.onCloseOptionList.bind(this);
this.isVisible = this.isVisible.bind(this);
}
/**
*
*/
initialSetValue() {
this.removeSelectedValuesFromOptions(false);
}
/**
*
*/
resetSelectedValues() {
const { unfilteredOptions } = this.state;
return new Promise((resolve) => {
this.setState({
selectedValues: [],
preSelectedValues: [],
options: unfilteredOptions,
filteredOptions: unfilteredOptions,
}, () => {
// @ts-ignore
resolve();
this.initialSetValue();
});
});
}
/**
*
*/
getSelectedItems() {
return this.state.selectedValues;
}
/**
*
*/
getSelectedItemsCount() {
return this.state.selectedValues.length;
}
/**
*
*/
componentDidMount() {
this.initialSetValue();
// @ts-ignore
this.searchWrapper.current.addEventListener('click', this.listenerCallback);
}
/**
*
*/
componentDidUpdate(prevProps: IMultiselectProps) {
const { options, selectedValues } = this.props;
const { options: prevOptions, selectedValues: prevSelectedvalues } = prevProps;
if (JSON.stringify(prevOptions) !== JSON.stringify(options)) {
this.setState({ options, filteredOptions: options, unfilteredOptions: options }, this.initialSetValue);
}
if (JSON.stringify(prevSelectedvalues) !== JSON.stringify(selectedValues)) {
this.setState({ selectedValues: Object.assign([], selectedValues), preSelectedValues: Object.assign([], selectedValues) }, this.initialSetValue);
}
}
/**
*
*/
listenerCallback() {
// @ts-ignore
this.searchBox.current.focus();
}
/**
*
*/
componentWillUnmount() {
// @ts-ignore
if (this.optionTimeout) {
// @ts-ignore
clearTimeout(this.optionTimeout);
}
// @ts-ignore
this.searchWrapper.current.removeEventListener('click', this.listenerCallback);
}
// Skipcheck flag - value will be true when the func called from on deselect anything.
/**
*
*/
removeSelectedValuesFromOptions(skipCheck: boolean) {
const { displayValue } = this.props;
const { selectedValues = [], unfilteredOptions } = this.state;
if (!selectedValues.length && !skipCheck) {
return;
}
const optionList = unfilteredOptions.filter(item => {
return selectedValues.findIndex(
v => v[displayValue] === item[displayValue],
) === -1
? true
: false;
});
this.setState(
{ options: optionList, filteredOptions: optionList },
this.filterOptionsByInput,
);
return;
}
/**
*
*/
onChange(event) {
const { onSearch } = this.props;
this.setState(
{ inputValue: event.target.value },
this.filterOptionsByInput,
);
if (onSearch) {
onSearch(event.target.value);
}
}
/**
*
*/
filterOptionsByInput() {
let { options, filteredOptions, inputValue } = this.state;
const { displayValue } = this.props;
options = filteredOptions.filter(i => this.matchValues(i[displayValue], inputValue));
this.setState({ options });
}
/**
*
*/
matchValues(value, search) {
if (value.toLowerCase) {
return value.toLowerCase().indexOf(search.toLowerCase()) > -1;
}
return value.toString().indexOf(search) > -1;
}
/**
*
*/
onArrowKeyNavigation(e) {
const {
options,
highlightOption,
toggleOptionsList,
inputValue,
selectedValues,
} = this.state;
if (e.keyCode === 8 && !inputValue && selectedValues.length) {
this.onRemoveSelectedItem(selectedValues.length - 1);
}
if (!options.length) {
return;
}
if (e.keyCode === 38) {
if (highlightOption > 0) {
this.setState(previousState => ({
highlightOption: previousState.highlightOption - 1,
}));
} else {
this.setState({ highlightOption: options.length - 1 });
}
} else if (e.keyCode === 40) {
if (highlightOption < options.length - 1) {
this.setState(previousState => ({
highlightOption: previousState.highlightOption + 1,
}));
} else {
this.setState({ highlightOption: 0 });
}
} else if (e.key === 'Enter' && options.length && toggleOptionsList) {
if (highlightOption === -1) {
return;
}
this.onSelectItem(options[highlightOption]);
}
// TODO: Instead of scrollIntoView need to find better soln for scroll the dropwdown container.
// setTimeout(() => {
// const element = document.querySelector("ul.optionContainer .highlight");
// if (element) {
// element.scrollIntoView();
// }
// });
}
/**
*
*/
onRemoveSelectedItem(item) {
let { selectedValues, index = 0 } = this.state;
const { onRemove, displayValue } = this.props;
index = selectedValues.findIndex(
i => i[displayValue] === item[displayValue],
);
selectedValues.splice(index, 1);
onRemove(selectedValues, item);
this.setState({ selectedValues }, () => {
this.removeSelectedValuesFromOptions(true);
});
}
/**
*
*/
onSelectItem(item) {
const { selectedValues } = this.state;
const { onSelect } = this.props;
this.setState({
inputValue: '',
});
if (this.isSelectedValue(item)) {
this.onRemoveSelectedItem(item);
return;
}
selectedValues.push(item);
onSelect(selectedValues, item);
this.setState({ selectedValues }, () => {
this.removeSelectedValuesFromOptions(true);
});
}
/**
*
*/
isSelectedValue(item) {
const { displayValue } = this.props;
const { selectedValues } = this.state;
return (
selectedValues.filter(i => i[displayValue] === item[displayValue])
.length > 0
);
}
/**
*
*/
renderOptionList() {
const { emptyRecordMsg } = this.props;
const { options } = this.state;
return (
<ul className={'optionContainer'}>
{options.length === 0 && <span className={'notFound'}>{emptyRecordMsg}</span>}
{this.renderNormalOption()}
</ul>
);
}
/**
*
*/
renderNormalOption() {
const { displayValue } = this.props;
const { highlightOption } = this.state;
return this.state.options.map((option, i) => {
const isSelected = this.isSelectedValue(option);
return (
<li
key={`option${i}`}
className={`option ${isSelected ? 'selected' : ''} ${highlightOption === i ? 'highlightOption highlight' : ''} ${this.isDisablePreSelectedValues(option) ? 'disableSelection' : ''}`}
onClick={() => this.onSelectItem(option)}
>
{option[displayValue]}
</li>
);
});
}
/**
*
*/
renderSelectedList() {
const { displayValue } = this.props;
const { selectedValues } = this.state;
return selectedValues.map((value, index) => (
<span className={`chip} ${this.isDisablePreSelectedValues(value) && 'disableSelection'}`} key={index}>
{value[displayValue]}
{!this.isDisablePreSelectedValues(value) && (
<div
role='button'
onClick={() => this.onRemoveSelectedItem(value)}
>
<Icon
className='ml-1 size-4 hover:cursor-pointer' src={require('@tabler/icons/outline/circle-x.svg')}
/>
</div>
)}
</span>
));
}
/**
*
*/
isDisablePreSelectedValues(value) {
const { displayValue } = this.props;
const { preSelectedValues } = this.state;
if (!preSelectedValues.length) {
return false;
}
return (
preSelectedValues.filter(i => i[displayValue] === value[displayValue])
.length > 0
);
}
/**
*
*/
toggelOptionList() {
this.setState({
toggleOptionsList: !this.state.toggleOptionsList,
highlightOption: 0,
});
}
/**
*
*/
onCloseOptionList() {
this.setState({
toggleOptionsList: false,
highlightOption: 0,
inputValue: '',
});
}
/**
*
*/
onFocus(){
if (this.state.toggleOptionsList) {
// @ts-ignore
clearTimeout(this.optionTimeout);
} else {
this.toggelOptionList();
}
}
/**
*
*/
onBlur(){
this.setState({ inputValue: '' }, this.filterOptionsByInput);
// @ts-ignore
this.optionTimeout = setTimeout(this.onCloseOptionList, 250);
}
/**
*
*/
isVisible(elem) {
return !!elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
}
/**
*
*/
hideOnClickOutside() {
const element = document.getElementsByClassName('multiselect-container')[0];
const outsideClickListener = event => {
if (element && !element.contains(event.target) && this.isVisible(element)) {
this.toggelOptionList();
}
};
document.addEventListener('click', outsideClickListener);
}
/**
*
*/
renderMultiselectContainer() {
const { inputValue, toggleOptionsList, selectedValues } = this.state;
const { placeholder, id, name, disable, className } = this.props;
return (
<div className={`multiselect-container multiSelectContainer ${disable ? 'disable_ms' : ''} ${className || ''}`} id={id || 'multiselectContainerReact'}>
<div
className={'search-wrapper searchWrapper'}
ref={this.searchWrapper}
>
{this.renderSelectedList()}
<input
type='text'
ref={this.searchBox}
className='searchBox'
id={`${id || 'search'}_input`}
name={`${name || 'search_name'}_input`}
onChange={this.onChange}
value={inputValue}
onFocus={this.onFocus}
onBlur={this.onBlur}
placeholder={(selectedValues.length) ? '' : placeholder}
onKeyDown={this.onArrowKeyNavigation}
autoComplete='off'
disabled={disable}
/>
</div>
<div
className={`optionListContainer ${
toggleOptionsList ? 'displayBlock' : 'displayNone'
}`}
onMouseDown={(e) => {
e.preventDefault();
}}
>
{this.renderOptionList()}
</div>
</div>
);
}
/**
*
*/
render() {
return (
<OutsideAlerter outsideClick={this.onCloseOptionList}>
{this.renderMultiselectContainer()}
</OutsideAlerter>
);
}
}
Multiselect.defaultProps = {
options: [],
selectedValues: [],
displayValue: 'model',
placeholder: 'Select',
emptyRecordMsg: 'No Options Available',
onSelect: () => {},
onRemove: () => {},
id: '',
name: '',
className: '',
} as IMultiselectProps;

View File

@ -340,7 +340,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
if (features.rssFeeds && account.local && (software !== GOTOSOCIAL || account.enable_rss)) {
menu.push({
text: intl.formatMessage(messages.subscribeFeed),
icon: require('@tabler/icons/outline/rss.svg'),
icon: require('@phosphor-icons/core/regular/rss.svg'),
href: software === MASTODON ? `${account.url}.rss` : `${account.url}/feed.rss`,
target: '_blank',
});
@ -350,7 +350,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(messages.share, { name: account.username }),
action: handleShare,
icon: require('@tabler/icons/outline/upload.svg'),
icon: require('@phosphor-icons/core/regular/export.svg'),
});
}
@ -388,18 +388,18 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(messages.edit_profile),
to: '/settings/profile',
icon: require('@tabler/icons/outline/user.svg'),
icon: require('@phosphor-icons/core/regular/user.svg'),
});
menu.push({
text: intl.formatMessage(messages.preferences),
to: '/settings',
icon: require('@tabler/icons/outline/settings.svg'),
icon: require('@phosphor-icons/core/regular/sliders-horizontal.svg'),
});
menu.push(null);
menu.push({
text: intl.formatMessage(messages.mutes),
to: '/mutes',
icon: require('@tabler/icons/outline/circle-x.svg'),
icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'),
});
menu.push({
text: intl.formatMessage(messages.blocks),
@ -440,7 +440,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(messages.add_or_remove_from_list),
action: onAddToList,
icon: require('@tabler/icons/outline/list.svg'),
icon: require('@phosphor-icons/core/regular/list-bullets.svg'),
});
}
@ -448,14 +448,14 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(account.relationship?.endorsed ? messages.unendorse : messages.endorse),
action: onEndorseToggle,
icon: require('@tabler/icons/outline/user-check.svg'),
icon: account.relationship?.endorsed ? require('@phosphor-icons/core/regular/user-minus.svg') : require('@phosphor-icons/core/regular/user-check.svg'),
});
}
} else if (features.lists && features.unrestrictedLists) {
menu.push({
text: intl.formatMessage(messages.add_or_remove_from_list),
action: onAddToList,
icon: require('@tabler/icons/outline/list.svg'),
icon: require('@phosphor-icons/core/regular/list-bullets.svg'),
});
}
@ -481,7 +481,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(messages.removeFromFollowers),
action: onRemoveFromFollowers,
icon: require('@tabler/icons/outline/user-x.svg'),
icon: require('@phosphor-icons/core/regular/user-minus.svg'),
});
}
@ -489,13 +489,13 @@ const Header: React.FC<IHeader> = ({ account }) => {
menu.push({
text: intl.formatMessage(messages.unmute, { name: account.username }),
action: onMute,
icon: require('@tabler/icons/outline/circle-x.svg'),
icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'),
});
} else {
menu.push({
text: intl.formatMessage(messages.mute, { name: account.username }),
action: onMute,
icon: require('@tabler/icons/outline/circle-x.svg'),
icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'),
});
}
@ -673,7 +673,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
return (
<IconButton
src={require('@tabler/icons/outline/upload.svg')}
src={require('@phosphor-icons/core/regular/export.svg')}
onClick={handleShare}
title={intl.formatMessage(messages.share, { name: account.username })}
theme='outlined'
@ -692,7 +692,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
return (
<IconButton
src={require('@tabler/icons/outline/rss.svg')}
src={require('@phosphor-icons/core/regular/rss.svg')}
href={href}
title={intl.formatMessage(messages.subscribeFeed)}
theme='outlined'
@ -757,7 +757,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
{menu.length > 0 && (
<DropdownMenu items={menu} placement='bottom-end'>
<IconButton
src={require('@tabler/icons/outline/dots.svg')}
src={require('@phosphor-icons/core/regular/dots-three.svg')}
theme='outlined'
className='px-2'
iconClassName='h-4 w-4'

View File

@ -141,7 +141,7 @@ const ChatListItem: React.FC<IChatListItemInterface> = ({ chat, onClick }) => {
<div className='max-w-0 overflow-hidden text-gray-600 hover:text-gray-100 group-hover:max-w-full'>
<DropdownMenu items={menu}>
<IconButton
src={require('@tabler/icons/outline/dots.svg')}
src={require('@phosphor-icons/core/regular/dots-three.svg')}
title='Settings'
className='text-gray-600 hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-500'
iconClassName='h-4 w-4'

View File

@ -187,7 +187,7 @@ const ChatMessage = (props: IChatMessage) => {
data-testid='chat-message-menu'
>
<Icon
src={require('@tabler/icons/outline/dots.svg')}
src={require('@phosphor-icons/core/regular/dots-three.svg')}
className='size-4'
/>
</button>

View File

@ -39,7 +39,7 @@ const ChatPageSidebar = () => {
<HStack space={1}>
<IconButton
src={require('@tabler/icons/outline/settings.svg')}
src={require('@phosphor-icons/core/regular/sliders-horizontal.svg')}
iconClassName='h-5 w-5 text-gray-600'
onClick={handleSettingsClick}
/>

View File

@ -56,7 +56,7 @@ const messages = defineMessages({
});
const blockTypeToIcon = {
bullet: require('@tabler/icons/outline/list.svg'),
bullet: require('@phosphor-icons/core/regular/list-bullets.svg'),
check: require('@tabler/icons/outline/list-check.svg'),
code: require('@tabler/icons/outline/code.svg'),
h1: require('@tabler/icons/outline/h-1.svg'),

View File

@ -335,7 +335,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
menu.push({
text: intl.formatMessage(messages.mute, { name: username }),
action: handleMuteClick,
icon: require('@tabler/icons/outline/circle-x.svg'),
icon: require('@phosphor-icons/core/regular/speaker-simple-x.svg'),
});
menu.push({
text: intl.formatMessage(messages.block, { name: username }),
@ -419,7 +419,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
<DropdownMenu items={makeMenu()} placement='bottom-end'>
<IconButton
src={require('@tabler/icons/outline/dots.svg')}
src={require('@phosphor-icons/core/regular/dots-three.svg')}
theme='outlined'
className='h-[30px] px-2'
iconClassName='h-4 w-4'

View File

@ -84,7 +84,7 @@ const GroupOptionsButton = ({ group }: IGroupActionButton) => {
return (
<DropdownMenu items={menu} placement='bottom'>
<IconButton
src={require('@tabler/icons/outline/dots.svg')}
src={require('@phosphor-icons/core/regular/dots-three.svg')}
theme='secondary'
iconClassName='h-5 w-5'
className='self-stretch px-2.5'

View File

@ -25,7 +25,7 @@ const STATUS_TYPE_ICONS: Record<string, string> = {
private: require('@phosphor-icons/core/regular/lock.svg'),
mutuals_only: require('@tabler/icons/outline/users-group.svg'),
local: require('@tabler/icons/outline/affiliate.svg'),
list: require('@tabler/icons/outline/list.svg'),
list: require('@phosphor-icons/core/regular/list-bullets.svg'),
subscribers: require('@tabler/icons/outline/coin.svg'),
};

View File

@ -39,7 +39,7 @@ const List: React.FC<IList> = ({ listId, accountId, added }) => {
return (
<div className='flex items-center gap-1.5 px-2 py-4 text-black dark:text-white'>
<Icon src={require('@tabler/icons/outline/list.svg')} />
<Icon src={require('@phosphor-icons/core/regular/list-bullets.svg')} />
<span className='grow'>
{list.title}
</span>

View File

@ -100,7 +100,7 @@ const CirclesPage: React.FC = () => {
to={`/circles/${circle.id}`}
label={
<HStack alignItems='center' space={2}>
<Icon src={require('@tabler/icons/outline/list.svg')} size={20} />
<Icon src={require('@phosphor-icons/core/regular/list-bullets.svg')} size={20} />
<span>{circle.title}</span>
</HStack>
}

View File

@ -109,7 +109,7 @@ const ListsPage: React.FC = () => {
to={`/list/${list.id}`}
label={
<HStack alignItems='center' space={2}>
<Icon src={require('@tabler/icons/outline/list.svg')} size={20} />
<Icon src={require('@phosphor-icons/core/regular/list-bullets.svg')} size={20} />
<span>{list.title}</span>
</HStack>
}

View File

@ -225,7 +225,7 @@ const ThemeEditorPage: React.FC<IThemeEditor> = () => {
}, {
text: intl.formatMessage(messages.import),
action: importTheme,
icon: require('@tabler/icons/outline/upload.svg'),
icon: require('@phosphor-icons/core/regular/export.svg'),
}, {
text: intl.formatMessage(messages.export),
action: exportTheme,

View File

@ -91,7 +91,7 @@ const StatusPage: React.FC<IStatusDetails> = (props) => {
{
text: intl.formatMessage(messages.linearView),
action: () => dispatch(changeSetting(['threads', 'displayMode'], 'linear')),
icon: require('@tabler/icons/outline/list.svg'),
icon: require('@phosphor-icons/core/regular/list-bullets.svg'),
type: 'radio',
checked: displayMode === 'linear',
},