mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 14:43:24 +00:00
feat: create tab and selector components
This commit is contained in:
79
inertia/components/common/form/selector.tsx
Normal file
79
inertia/components/common/form/selector.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import { InputHTMLAttributes, ReactNode, useEffect, useState } from 'react';
|
||||||
|
import Select, {
|
||||||
|
FormatOptionLabelMeta,
|
||||||
|
GroupBase,
|
||||||
|
OptionsOrGroups,
|
||||||
|
} from 'react-select';
|
||||||
|
import FormField from '~/components/common/form/_form_field';
|
||||||
|
|
||||||
|
type Option = { label: string | number; value: string | number };
|
||||||
|
|
||||||
|
interface SelectorProps
|
||||||
|
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
errors?: string[];
|
||||||
|
options: OptionsOrGroups<Option, GroupBase<Option>>;
|
||||||
|
value: number | string;
|
||||||
|
onChangeCallback?: (value: number | string) => void;
|
||||||
|
formatOptionLabel?: (
|
||||||
|
data: Option,
|
||||||
|
formatOptionLabelMeta: FormatOptionLabelMeta<Option>
|
||||||
|
) => ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Selector({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
errors = [],
|
||||||
|
options,
|
||||||
|
onChangeCallback,
|
||||||
|
formatOptionLabel,
|
||||||
|
required = false,
|
||||||
|
...props
|
||||||
|
}: SelectorProps): JSX.Element {
|
||||||
|
const theme = useTheme();
|
||||||
|
const [selectorValue, setSelectorValue] = useState<Option>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (options.length === 0) return;
|
||||||
|
|
||||||
|
const option = options.find((o: any) => o.value === value);
|
||||||
|
if (option) {
|
||||||
|
setSelectorValue(option as Option);
|
||||||
|
}
|
||||||
|
}, [options, value]);
|
||||||
|
|
||||||
|
const handleChange = (selectedOption: Option) => {
|
||||||
|
setSelectorValue(selectedOption);
|
||||||
|
if (onChangeCallback) {
|
||||||
|
onChangeCallback(selectedOption.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormField required={required}>
|
||||||
|
{label && (
|
||||||
|
<label htmlFor={name} title={`${name} field`}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
<Select
|
||||||
|
value={selectorValue}
|
||||||
|
onChange={(newValue) => handleChange(newValue as Option)}
|
||||||
|
options={options}
|
||||||
|
isDisabled={props.disabled}
|
||||||
|
menuPlacement="auto"
|
||||||
|
formatOptionLabel={
|
||||||
|
formatOptionLabel
|
||||||
|
? (val, formatOptionLabelMeta) =>
|
||||||
|
formatOptionLabel(val, formatOptionLabelMeta)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
css={{ color: theme.colors.black }}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ const ModalBody = styled.div({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
overflow: 'auto',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ModalBody;
|
export default ModalBody;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import TransitionLayout from '~/components/layouts/_transition_layout';
|
|||||||
|
|
||||||
const ModalContainer = styled(TransitionLayout)(({ theme }) => ({
|
const ModalContainer = styled(TransitionLayout)(({ theme }) => ({
|
||||||
minWidth: '500px',
|
minWidth: '500px',
|
||||||
background: theme.colors.secondary,
|
background: theme.colors.background,
|
||||||
padding: '1em',
|
padding: '1em',
|
||||||
borderRadius: theme.border.radius,
|
borderRadius: theme.border.radius,
|
||||||
marginTop: '6em',
|
marginTop: '6em',
|
||||||
|
|||||||
24
inertia/components/common/tabs/tab_item.tsx
Normal file
24
inertia/components/common/tabs/tab_item.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { rgba } from '~/lib/color';
|
||||||
|
|
||||||
|
const TabItem = styled.li<{ active?: boolean; danger?: boolean }>(
|
||||||
|
({ theme, active, danger }) => {
|
||||||
|
const activeColor = !danger ? theme.colors.primary : theme.colors.lightRed;
|
||||||
|
return {
|
||||||
|
userSelect: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
backgroundColor: active
|
||||||
|
? rgba(activeColor, 0.15)
|
||||||
|
: theme.colors.secondary,
|
||||||
|
padding: '10px 20px',
|
||||||
|
border: `1px solid ${active ? rgba(activeColor, 0.1) : theme.colors.secondary}`,
|
||||||
|
borderBottom: `1px solid ${active ? rgba(activeColor, 0.25) : theme.colors.secondary}`,
|
||||||
|
display: 'flex',
|
||||||
|
gap: '0.35em',
|
||||||
|
alignItems: 'center',
|
||||||
|
transition: '.075s',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default TabItem;
|
||||||
10
inertia/components/common/tabs/tab_list.tsx
Normal file
10
inertia/components/common/tabs/tab_list.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
const TabList = styled.ul({
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
display: 'flex',
|
||||||
|
listStyle: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TabList;
|
||||||
12
inertia/components/common/tabs/tab_panel.tsx
Normal file
12
inertia/components/common/tabs/tab_panel.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { rgba } from '~/lib/color';
|
||||||
|
|
||||||
|
const TabPanel = styled.div(({ theme }) => ({
|
||||||
|
zIndex: 1,
|
||||||
|
position: 'relative',
|
||||||
|
border: `1px solid ${rgba(theme.colors.primary, 0.25)}`,
|
||||||
|
padding: '20px',
|
||||||
|
marginTop: '-1px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default TabPanel;
|
||||||
42
inertia/components/common/tabs/tabs.tsx
Normal file
42
inertia/components/common/tabs/tabs.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { ReactNode, useState } from 'react';
|
||||||
|
import { IconType } from 'react-icons/lib';
|
||||||
|
import TabItem from '~/components/common/tabs/tab_item';
|
||||||
|
import TabList from '~/components/common/tabs/tab_list';
|
||||||
|
import TabPanel from '~/components/common/tabs/tab_panel';
|
||||||
|
import TransitionLayout from '~/components/layouts/_transition_layout';
|
||||||
|
|
||||||
|
export interface Tab {
|
||||||
|
title: string;
|
||||||
|
content: ReactNode;
|
||||||
|
icon?: IconType;
|
||||||
|
danger?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Tabs({ tabs }: { tabs: Tab[] }) {
|
||||||
|
const [activeTabIndex, setActiveTabIndex] = useState<number>(0);
|
||||||
|
|
||||||
|
const handleTabClick = (index: number) => {
|
||||||
|
setActiveTabIndex(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div css={{ width: '100%' }}>
|
||||||
|
<TabList>
|
||||||
|
{tabs.map(({ title, icon: Icon, danger }, index) => (
|
||||||
|
<TabItem
|
||||||
|
key={index}
|
||||||
|
active={index === activeTabIndex}
|
||||||
|
onClick={() => handleTabClick(index)}
|
||||||
|
danger={danger ?? false}
|
||||||
|
>
|
||||||
|
{!!Icon && <Icon size={20} />}
|
||||||
|
{title}
|
||||||
|
</TabItem>
|
||||||
|
))}
|
||||||
|
</TabList>
|
||||||
|
<TabPanel key={tabs[activeTabIndex].title}>
|
||||||
|
<TransitionLayout>{tabs[activeTabIndex].content}</TransitionLayout>
|
||||||
|
</TabPanel>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { FormEvent } from 'react';
|
import { FormEvent } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Checkbox from '~/components/common/form/checkbox';
|
import Checkbox from '~/components/common/form/checkbox';
|
||||||
|
import Selector from '~/components/common/form/selector';
|
||||||
import TextBox from '~/components/common/form/textbox';
|
import TextBox from '~/components/common/form/textbox';
|
||||||
import BackToDashboard from '~/components/common/navigation/back_to_dashboard';
|
import BackToDashboard from '~/components/common/navigation/back_to_dashboard';
|
||||||
import FormLayout from '~/components/layouts/form_layout';
|
import FormLayout from '~/components/layouts/form_layout';
|
||||||
@@ -91,17 +92,18 @@ export default function FormLink({
|
|||||||
errors={errors?.description}
|
errors={errors?.description}
|
||||||
disabled={disableInputs}
|
disabled={disableInputs}
|
||||||
/>
|
/>
|
||||||
<select
|
<Selector
|
||||||
onChange={({ target }) => setData('collectionId', target.value)}
|
label={t('collection.collections')}
|
||||||
defaultValue={data.collectionId}
|
name="collection"
|
||||||
disabled={disableInputs}
|
placeholder={t('collection.collections')}
|
||||||
>
|
value={data.collectionId}
|
||||||
{collections?.map((collection) => (
|
onChangeCallback={(value) => setData('collectionId', value)}
|
||||||
<option key={collection.id} value={collection.id}>
|
options={collections.map(({ id, name }) => ({
|
||||||
{collection.name}
|
label: name,
|
||||||
</option>
|
value: id,
|
||||||
))}
|
}))}
|
||||||
</select>
|
required
|
||||||
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={t('favorite')}
|
label={t('favorite')}
|
||||||
name="favorite"
|
name="favorite"
|
||||||
|
|||||||
@@ -1,32 +1,53 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { ChangeEvent } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Selector from '~/components/common/form/selector';
|
||||||
import { LS_LANG_KEY } from '~/constants';
|
import { LS_LANG_KEY } from '~/constants';
|
||||||
import { languages } from '~/i18n';
|
import { languages } from '~/i18n';
|
||||||
|
|
||||||
export default function LangSelector() {
|
type Country = 'fr' | 'en';
|
||||||
|
|
||||||
|
export default function LangSelector({
|
||||||
|
onSelected,
|
||||||
|
}: {
|
||||||
|
onSelected?: (country: Country) => void;
|
||||||
|
}) {
|
||||||
const { t, i18n } = useTranslation('common');
|
const { t, i18n } = useTranslation('common');
|
||||||
|
|
||||||
const onToggleLanguageClick = ({
|
const onToggleLanguageClick = (newLocale: string) => {
|
||||||
target,
|
dayjs.locale(newLocale);
|
||||||
}: ChangeEvent<HTMLSelectElement>) => {
|
i18n.changeLanguage(newLocale);
|
||||||
dayjs.locale(target.value);
|
localStorage.setItem(LS_LANG_KEY, newLocale);
|
||||||
i18n.changeLanguage(target.value);
|
|
||||||
localStorage.setItem(LS_LANG_KEY, target.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<Selector
|
||||||
onChange={onToggleLanguageClick}
|
name="lng-select"
|
||||||
name="lang-selector"
|
label={t('select-your-lang')}
|
||||||
id="lang-selector"
|
value={i18n.language}
|
||||||
defaultValue={i18n.language}
|
onChangeCallback={(value) => {
|
||||||
>
|
onToggleLanguageClick(value.toString());
|
||||||
{languages.map((lang) => (
|
if (onSelected) {
|
||||||
<option value={lang} key={lang}>
|
setTimeout(() => onSelected(value.toString() as Country), 150);
|
||||||
{t(`language.${lang}`)}
|
}
|
||||||
</option>
|
}}
|
||||||
))}
|
options={languages.map((lang: Country) => ({
|
||||||
</select>
|
label: t(`language.${lang}`),
|
||||||
|
value: lang,
|
||||||
|
}))}
|
||||||
|
formatOptionLabel={(country) => (
|
||||||
|
<div
|
||||||
|
className="country-option"
|
||||||
|
style={{ display: 'flex', gap: '.5em', alignItems: 'center' }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={`/icons/${country.value}.svg`}
|
||||||
|
alt="country-image"
|
||||||
|
height={24}
|
||||||
|
width={24}
|
||||||
|
/>
|
||||||
|
<span>{country.label}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,9 +67,10 @@ function GlobalStyles() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
hr: {
|
hr: {
|
||||||
color: localTheme.colors.secondary,
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
marginBlock: '1em',
|
marginBlock: '1em',
|
||||||
|
border: 0,
|
||||||
|
borderTop: `1px solid ${localTheme.colors.background}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
114
package-lock.json
generated
114
package-lock.json
generated
@@ -40,6 +40,7 @@
|
|||||||
"react-hotkeys-hook": "^4.5.0",
|
"react-hotkeys-hook": "^4.5.0",
|
||||||
"react-i18next": "^14.1.2",
|
"react-i18next": "^14.1.2",
|
||||||
"react-icons": "^5.2.1",
|
"react-icons": "^5.2.1",
|
||||||
|
"react-select": "^5.8.0",
|
||||||
"react-swipeable": "^7.0.1",
|
"react-swipeable": "^7.0.1",
|
||||||
"react-toggle": "^4.1.3",
|
"react-toggle": "^4.1.3",
|
||||||
"reflect-metadata": "^0.2.2"
|
"reflect-metadata": "^0.2.2"
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
"@types/node": "^20.12.12",
|
"@types/node": "^20.12.12",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"@types/react-select": "^5.0.1",
|
||||||
"@types/react-toggle": "^4.0.5",
|
"@types/react-toggle": "^4.0.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
@@ -2680,6 +2682,28 @@
|
|||||||
"npm": ">=6.14.13"
|
"npm": ">=6.14.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/utils": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
|
||||||
|
"integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.0.0",
|
||||||
|
"@floating-ui/utils": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/utils": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
@@ -4230,14 +4254,11 @@
|
|||||||
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
|
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {},
|
||||||
"devOptional": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.3.3",
|
"version": "18.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
|
||||||
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
|
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
|
||||||
"devOptional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@@ -4252,6 +4273,16 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-select": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-h5Im0AP0dr4AVeHtrcvQrLV+gmPa7SA0AGdxl2jOhtwiE6KgXBFSogWw8az32/nusE6AQHlCOHQWjP1S/+oMWA==",
|
||||||
|
"deprecated": "This is a stub types definition. react-select provides its own type definitions, so you do not need this installed.",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"react-select": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react-toggle": {
|
"node_modules/@types/react-toggle": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-toggle/-/react-toggle-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-toggle/-/react-toggle-4.0.5.tgz",
|
||||||
@@ -4261,6 +4292,14 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-transition-group": {
|
||||||
|
"version": "4.4.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
|
||||||
|
"integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.5.8",
|
"version": "7.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||||
@@ -7650,6 +7689,15 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-helpers": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.8.7",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/edge-error": {
|
"node_modules/edge-error": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/edge-error/-/edge-error-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/edge-error/-/edge-error-4.0.1.tgz",
|
||||||
@@ -9639,6 +9687,11 @@
|
|||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/memoize-one": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
@@ -10278,7 +10331,6 @@
|
|||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
@@ -10289,7 +10341,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -10297,8 +10348,7 @@
|
|||||||
"node_modules/prop-types/node_modules/react-is": {
|
"node_modules/prop-types/node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/property-information": {
|
"node_modules/property-information": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
@@ -10639,6 +10689,26 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-select": {
|
||||||
|
"version": "5.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz",
|
||||||
|
"integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.0",
|
||||||
|
"@emotion/cache": "^11.4.0",
|
||||||
|
"@emotion/react": "^11.8.1",
|
||||||
|
"@floating-ui/dom": "^1.0.1",
|
||||||
|
"@types/react-transition-group": "^4.4.0",
|
||||||
|
"memoize-one": "^6.0.0",
|
||||||
|
"prop-types": "^15.6.0",
|
||||||
|
"react-transition-group": "^4.3.0",
|
||||||
|
"use-isomorphic-layout-effect": "^1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-swipeable": {
|
"node_modules/react-swipeable": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.1.tgz",
|
||||||
@@ -10660,6 +10730,21 @@
|
|||||||
"react-dom": ">= 15.3.0 < 19"
|
"react-dom": ">= 15.3.0 < 19"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-transition-group": {
|
||||||
|
"version": "4.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"dom-helpers": "^5.0.1",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.6.0",
|
||||||
|
"react-dom": ">=16.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-package-up": {
|
"node_modules/read-package-up": {
|
||||||
"version": "11.0.0",
|
"version": "11.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
|
||||||
@@ -12046,6 +12131,19 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-isomorphic-layout-effect": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/v8-compile-cache-lib": {
|
"node_modules/v8-compile-cache-lib": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
|
"@types/react-select": "^5.0.1",
|
||||||
"vite": "^5.2.11"
|
"vite": "^5.2.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -94,6 +95,7 @@
|
|||||||
"react-hotkeys-hook": "^4.5.0",
|
"react-hotkeys-hook": "^4.5.0",
|
||||||
"react-i18next": "^14.1.2",
|
"react-i18next": "^14.1.2",
|
||||||
"react-icons": "^5.2.1",
|
"react-icons": "^5.2.1",
|
||||||
|
"react-select": "^5.8.0",
|
||||||
"react-swipeable": "^7.0.1",
|
"react-swipeable": "^7.0.1",
|
||||||
"react-toggle": "^4.1.3",
|
"react-toggle": "^4.1.3",
|
||||||
"reflect-metadata": "^0.2.2"
|
"reflect-metadata": "^0.2.2"
|
||||||
@@ -120,4 +122,4 @@
|
|||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.js,*.ts,*.jsx,*.tsx": "eslint --cache --fix"
|
"*.js,*.ts,*.jsx,*.tsx": "eslint --cache --fix"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
public/icons/en.svg
Normal file
1
public/icons/en.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30"><clipPath id="a"><path d="M0 0v30h60V0z"/></clipPath><clipPath id="b"><path d="M30 15h30v15zv15H0zH0V0zV0h30z"/></clipPath><g clip-path="url(#a)"><path d="M0 0v30h60V0z" fill="#012169"/><path d="M0 0l60 30m0-30L0 30" stroke="#fff" stroke-width="6"/><path d="M0 0l60 30m0-30L0 30" clip-path="url(#b)" stroke="#C8102E" stroke-width="4"/><path d="M30 0v30M0 15h60" stroke="#fff" stroke-width="10"/><path d="M30 0v30M0 15h60" stroke="#C8102E" stroke-width="6"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 527 B |
1
public/icons/fr.svg
Normal file
1
public/icons/fr.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 2"><path fill="#EC1920" d="M0 0h3v2H0z"/><path fill="#fff" d="M0 0h2v2H0z"/><path fill="#051440" d="M0 0h1v2H0z"/></svg>
|
||||||
|
After Width: | Height: | Size: 175 B |
Reference in New Issue
Block a user