mobile: update ui
9
apps/mobile/.nanoicons.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"iconSets": [
|
||||
{
|
||||
"inputDir": "../../packages/icons/svgs",
|
||||
"fontFamily": "notesnook-icons",
|
||||
"outputDir": "./fonts"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
export const FontSizes = {
|
||||
XXS: 10,
|
||||
XS: 12,
|
||||
@@ -15,10 +33,24 @@ export const FontFamily = {
|
||||
BOLD: "Inter-Bold" // 700
|
||||
};
|
||||
|
||||
const LineHeightMultipliers = {
|
||||
"100%": 1,
|
||||
"110%": 1.1,
|
||||
"120%": 1.2,
|
||||
"130%": 1.3,
|
||||
"140%": 1.4,
|
||||
"150%": 1.5
|
||||
};
|
||||
export type LineHeightVariants =
|
||||
| "100%"
|
||||
| "110%"
|
||||
| "120%"
|
||||
| "130%"
|
||||
| "140%"
|
||||
| "150%";
|
||||
export const getLineHeight = (
|
||||
fontSize: keyof typeof FontSizes,
|
||||
type: 1 | 2
|
||||
type: LineHeightVariants
|
||||
) => {
|
||||
if (type === 1) return (FontSizes[fontSize] / 100) * 120;
|
||||
if (type === 2) return (FontSizes[fontSize] / 100) * 150;
|
||||
return FontSizes[fontSize] * LineHeightMultipliers[type];
|
||||
};
|
||||
|
||||
@@ -17,15 +17,15 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useState } from "react";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
|
||||
import { NavigationProps } from "../../services/navigation";
|
||||
import { Toast } from "../toast";
|
||||
import { AuthMode, initialAuthMode } from "./common";
|
||||
import { Login } from "./login";
|
||||
import { Signup } from "./signup";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { NavigationProps } from "../../services/navigation";
|
||||
|
||||
const Auth = ({ navigation, route }: NavigationProps<"Auth">) => {
|
||||
const [currentAuthMode, setCurrentAuthMode] = useState(
|
||||
|
||||
@@ -178,6 +178,7 @@ export const Login = ({
|
||||
formRef={formRef}
|
||||
fwdRef={emailInputRef}
|
||||
testID="input.email"
|
||||
label={strings.email()}
|
||||
returnKeyLabel="Next"
|
||||
returnKeyType="next"
|
||||
autoComplete="email"
|
||||
@@ -200,6 +201,7 @@ export const Login = ({
|
||||
name="password"
|
||||
formRef={formRef}
|
||||
fwdRef={passwordInputRef}
|
||||
label={strings.password()}
|
||||
testID="input.password"
|
||||
returnKeyLabel={strings.done()}
|
||||
returnKeyType="done"
|
||||
|
||||
@@ -22,9 +22,9 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { TextInput, View } from "react-native";
|
||||
import { db } from "../../common/database/index";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import useTimer from "../../hooks/use-timer";
|
||||
import { eSendEvent, ToastManager } from "../../services/event-manager";
|
||||
import { eCloseSimpleDialog } from "../../utils/events";
|
||||
import { hexToRGBA, RGB_Linear_Shade } from "../../utils/colors";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
@@ -35,7 +35,6 @@ import PinInput from "../ui/pin-input/index";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
|
||||
type MFAInfo = {
|
||||
primaryMethod: string;
|
||||
@@ -119,12 +118,14 @@ const TwoFactorVerification = ({
|
||||
{
|
||||
id: "sms",
|
||||
title: strings.sendCodeSms(),
|
||||
icon: "message-plus-outline"
|
||||
icon: "chat",
|
||||
iconFamily: "notesnook"
|
||||
},
|
||||
{
|
||||
id: "email",
|
||||
title: strings.sendCodeEmail(),
|
||||
icon: "email-outline"
|
||||
icon: "envelope-simple",
|
||||
iconFamily: "notesnook"
|
||||
},
|
||||
{
|
||||
id: "app",
|
||||
@@ -134,7 +135,8 @@ const TwoFactorVerification = ({
|
||||
{
|
||||
id: "recoveryCode",
|
||||
title: strings.recoveryCode(),
|
||||
icon: "key"
|
||||
icon: "lock-simple",
|
||||
iconFamily: "notesnook"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -182,47 +184,54 @@ const TwoFactorVerification = ({
|
||||
}, 500);
|
||||
}}
|
||||
style={{
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
<View
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
backgroundColor: colors.primary.shade,
|
||||
borderRadius: Radius.XS,
|
||||
marginBottom: Spacing.LEVEL_7
|
||||
}}
|
||||
size={25}
|
||||
name="key"
|
||||
color={colors.primary.accent}
|
||||
/>
|
||||
<Heading
|
||||
style={{
|
||||
textAlign: "center",
|
||||
marginBottom: Spacing.LEVEL_1
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
{currentMethod.method ? strings["2fa"]() : strings.select2faMethod()}
|
||||
</Heading>
|
||||
<Paragraph
|
||||
style={{
|
||||
width: "80%",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{currentMethod.method
|
||||
? strings["2faCodeHelpText"][
|
||||
currentMethod.method as keyof (typeof strings)["2faCodeHelpText"]
|
||||
]?.() || strings.select2faCodeHelpText()
|
||||
: strings.select2faCodeHelpText()}
|
||||
</Paragraph>
|
||||
<IconButton
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
backgroundColor: colors.primary.shade,
|
||||
borderRadius: Radius.XS,
|
||||
marginBottom: Spacing.LEVEL_7
|
||||
}}
|
||||
size={25}
|
||||
iconFamily="notesnook"
|
||||
name="shield-check"
|
||||
color={colors.primary.accent}
|
||||
/>
|
||||
<Heading
|
||||
style={{
|
||||
textAlign: "center",
|
||||
marginBottom: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{currentMethod.method ? strings["2fa"]() : strings.select2faMethod()}
|
||||
</Heading>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{currentMethod.method
|
||||
? strings["2faCodeHelpText"][
|
||||
currentMethod.method as keyof (typeof strings)["2faCodeHelpText"]
|
||||
]?.() || strings.select2faCodeHelpText()
|
||||
: strings.select2faCodeHelpText()}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
{currentMethod.method ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
marginTop: Spacing.LEVEL_4,
|
||||
borderRadius: Radius.S,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
@@ -303,7 +312,7 @@ const TwoFactorVerification = ({
|
||||
currentMethod.method === "email" ? (
|
||||
<Button
|
||||
onPress={onSendCode}
|
||||
type={seconds ? "secondary" : "transparent"}
|
||||
type={"plain"}
|
||||
disabled={!seconds}
|
||||
title={
|
||||
sending
|
||||
@@ -316,6 +325,7 @@ const TwoFactorVerification = ({
|
||||
}
|
||||
loading={sending}
|
||||
fontSize={AppFontSize.sm}
|
||||
fontFamily="REGULAR"
|
||||
style={{
|
||||
paddingVertical: 0,
|
||||
alignSelf: "flex-start",
|
||||
@@ -335,7 +345,12 @@ const TwoFactorVerification = ({
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_2,
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
{getMethods().map((item) => (
|
||||
<Pressable
|
||||
key={item.title}
|
||||
@@ -346,35 +361,42 @@ const TwoFactorVerification = ({
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
marginTop: 0,
|
||||
padding: Spacing.LEVEL_2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
flexDirection: "row",
|
||||
borderRadius: 0,
|
||||
borderRadius: Radius.S,
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start"
|
||||
justifyContent: "flex-start",
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
type="secondaryAccented"
|
||||
style={{
|
||||
marginRight: 10
|
||||
borderRadius: Radius.XS,
|
||||
padding: Spacing.LEVEL_1,
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
backgroundColor: RGB_Linear_Shade(
|
||||
0.04,
|
||||
hexToRGBA(colors.secondary.background)
|
||||
)
|
||||
}}
|
||||
size={15}
|
||||
color={colors.primary.accent}
|
||||
size={17}
|
||||
color={colors.primary.icon}
|
||||
name={item.icon}
|
||||
iconFamily={item.iconFamily as "notesnook"}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
<Paragraph size={AppFontSize.md}>{item.title}</Paragraph>
|
||||
<Paragraph size={AppFontSize.sm}>{item.title}</Paragraph>
|
||||
</View>
|
||||
</Pressable>
|
||||
))}
|
||||
</>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -20,14 +20,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { ActivityIndicator, StyleSheet, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { View } from "react-native";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { getColorLinearShade } from "../../utils/colors";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Button, ButtonProps } from "../ui/button";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
const DialogButtons = ({
|
||||
onPressPositive,
|
||||
@@ -51,82 +48,39 @@ const DialogButtons = ({
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
backgroundColor: colors.secondary.background,
|
||||
height: 60,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
borderTopWidth: 0.7,
|
||||
borderTopColor: getColorLinearShade(
|
||||
colors.secondary.background,
|
||||
0.05,
|
||||
isDark
|
||||
)
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_2,
|
||||
paddingHorizontal: Spacing.LEVEL_4
|
||||
}
|
||||
]}
|
||||
>
|
||||
{doneText ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
color={colors.primary.accent}
|
||||
name="check-circle-outline"
|
||||
size={AppFontSize.md}
|
||||
/>
|
||||
<Paragraph color={colors.primary.accent}>{" " + doneText}</Paragraph>
|
||||
</View>
|
||||
) : loading ? (
|
||||
<ActivityIndicator
|
||||
size={AppFontSize.lg}
|
||||
color={colors.primary.accent}
|
||||
/>
|
||||
) : (
|
||||
<View />
|
||||
)}
|
||||
|
||||
<View
|
||||
<Button
|
||||
onPress={onPressNegative}
|
||||
fontSize={AppFontSize.sm}
|
||||
testID={notesnook.ids.default.dialog.no}
|
||||
type="plain-outline"
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
width: "48.5%"
|
||||
}}
|
||||
>
|
||||
title={negativeTitle}
|
||||
/>
|
||||
{onPressPositive ? (
|
||||
<Button
|
||||
onPress={onPressNegative}
|
||||
onPress={onPressPositive}
|
||||
fontSize={AppFontSize.sm}
|
||||
testID={notesnook.ids.default.dialog.no}
|
||||
type="plain"
|
||||
testID={notesnook.ids.default.dialog.yes}
|
||||
style={{
|
||||
width: "48.5%"
|
||||
}}
|
||||
loading={loading}
|
||||
bold
|
||||
title={negativeTitle}
|
||||
type={positiveType || "accent"}
|
||||
title={positiveTitle}
|
||||
/>
|
||||
{onPressPositive ? (
|
||||
<Button
|
||||
onPress={onPressPositive}
|
||||
fontSize={AppFontSize.sm}
|
||||
testID={notesnook.ids.default.dialog.yes}
|
||||
style={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
bold
|
||||
type={positiveType || "transparent"}
|
||||
title={positiveTitle}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default DialogButtons;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ import { DDS } from "../../services/device-detection";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { getContainerBorder } from "../../utils/colors";
|
||||
import { Radius } from "../../common/design/spacing";
|
||||
|
||||
const DialogContainer = ({
|
||||
width,
|
||||
@@ -44,7 +45,7 @@ const DialogContainer = ({
|
||||
{
|
||||
width: width || DDS.isTab ? 500 : "85%",
|
||||
maxHeight: height || 450,
|
||||
borderRadius: 10,
|
||||
borderRadius: Radius.LG,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12
|
||||
},
|
||||
|
||||
@@ -57,7 +57,6 @@ const DialogHeader = ({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
minHeight: 50,
|
||||
paddingHorizontal: padding,
|
||||
...style
|
||||
}}
|
||||
@@ -76,7 +75,7 @@ const DialogHeader = ({
|
||||
>
|
||||
<Heading
|
||||
style={{ textAlign: centered ? "center" : "left" }}
|
||||
size={AppFontSize.lg}
|
||||
fontSize="XL"
|
||||
>
|
||||
{title}{" "}
|
||||
{titlePart ? (
|
||||
|
||||
@@ -58,6 +58,7 @@ export type DialogInfo = {
|
||||
onFormSubmit?: (form: FormRef) => Promise<boolean>;
|
||||
};
|
||||
input: boolean;
|
||||
inputLabel?: string;
|
||||
inputPlaceholder: string;
|
||||
defaultValue: string;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
|
||||
@@ -19,13 +19,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, {
|
||||
RefObject,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
RefObject
|
||||
useState
|
||||
} from "react";
|
||||
import { TextInput, View, ViewStyle } from "react-native";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
@@ -42,7 +43,6 @@ import { Button } from "../ui/button";
|
||||
import Input from "../ui/input";
|
||||
import { FormInput, type FormRef } from "../ui/input/form-input";
|
||||
import { Notice } from "../ui/notice";
|
||||
import Seperator from "../ui/seperator";
|
||||
import BaseDialog from "./base-dialog";
|
||||
import DialogButtons from "./dialog-buttons";
|
||||
import DialogHeader from "./dialog-header";
|
||||
@@ -155,7 +155,7 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
maxHeight: 450,
|
||||
borderRadius: defaultBorderRadius,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12,
|
||||
gap: Spacing.LEVEL_4,
|
||||
...getContainerBorder(colors.primary.border, 0.5),
|
||||
overflow: "hidden"
|
||||
};
|
||||
@@ -170,9 +170,7 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
bounce={!dialogInfo.input && !dialogInfo.form}
|
||||
closeOnTouch={!dialogInfo.disableBackdropClosing}
|
||||
background={dialogInfo.background}
|
||||
transparent={
|
||||
dialogInfo.transparent === undefined ? false : dialogInfo.transparent
|
||||
}
|
||||
transparent={dialogInfo.transparent}
|
||||
onShow={async () => {
|
||||
if (dialogInfo.input && !dialogInfo.form) {
|
||||
inputRef.current?.setNativeProps({
|
||||
@@ -201,54 +199,61 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
: dialogInfo.component}
|
||||
|
||||
{dialogInfo.component ? null : (
|
||||
<View style={style}>
|
||||
<DialogHeader
|
||||
title={dialogInfo.title}
|
||||
icon={dialogInfo.icon}
|
||||
paragraph={dialogInfo.paragraph}
|
||||
paragraphColor={dialogInfo.paragraphColor}
|
||||
padding={12}
|
||||
style={{
|
||||
minHeight: 0
|
||||
}}
|
||||
/>
|
||||
<Seperator half />
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
paddingVertical: Spacing.LEVEL_4
|
||||
},
|
||||
style
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
gap: Spacing.LEVEL_4
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DialogHeader
|
||||
title={dialogInfo.title}
|
||||
icon={dialogInfo.icon}
|
||||
paragraph={dialogInfo.paragraph}
|
||||
paragraphColor={dialogInfo.paragraphColor}
|
||||
style={{
|
||||
minHeight: 0
|
||||
}}
|
||||
/>
|
||||
|
||||
{dialogInfo.form ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
gap: DefaultAppStyles.GAP / 2
|
||||
}}
|
||||
>
|
||||
{dialogInfo.form.items.map((item, index) => (
|
||||
<FormInput
|
||||
key={item.name}
|
||||
fwdRef={item.ref}
|
||||
name={item.name}
|
||||
autoFocus={index === 0}
|
||||
placeholder={item.placeholder}
|
||||
formRef={formRef as RefObject<FormRef>}
|
||||
validators={item.validators}
|
||||
defaultValue={item.defaultValue}
|
||||
secureTextEntry={dialogInfo.secureTextEntry}
|
||||
onSubmitEditing={() => {
|
||||
const nextItem = dialogInfo?.form?.items?.[index + 1];
|
||||
if (nextItem) {
|
||||
nextItem?.ref.current?.focus();
|
||||
} else {
|
||||
onPressPositive();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
) : dialogInfo.input ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
{dialogInfo.form ? (
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{dialogInfo.form.items.map((item, index) => (
|
||||
<FormInput
|
||||
key={item.name}
|
||||
fwdRef={item.ref}
|
||||
name={item.name}
|
||||
autoFocus={index === 0}
|
||||
placeholder={item.placeholder}
|
||||
formRef={formRef as RefObject<FormRef>}
|
||||
validators={item.validators}
|
||||
defaultValue={item.defaultValue}
|
||||
secureTextEntry={dialogInfo.secureTextEntry}
|
||||
onSubmitEditing={() => {
|
||||
const nextItem = dialogInfo?.form?.items?.[index + 1];
|
||||
if (nextItem) {
|
||||
nextItem?.ref.current?.focus();
|
||||
} else {
|
||||
onPressPositive();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
) : dialogInfo.input ? (
|
||||
<Input
|
||||
fwdRef={inputRef}
|
||||
autoCapitalize="none"
|
||||
@@ -258,6 +263,7 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
testID="input-value"
|
||||
secureTextEntry={dialogInfo.secureTextEntry}
|
||||
defaultValue={dialogInfo.defaultValue}
|
||||
label={dialogInfo.inputLabel}
|
||||
onSubmit={() => {
|
||||
onPressPositive();
|
||||
}}
|
||||
@@ -266,48 +272,47 @@ export const Dialog = ({ context = "global" }: { context?: string }) => {
|
||||
keyboardType={dialogInfo.keyboardType || "default"}
|
||||
placeholder={dialogInfo.inputPlaceholder}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
) : null}
|
||||
|
||||
{dialogInfo?.notice ? (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Notice
|
||||
type={dialogInfo.notice.type || "information"}
|
||||
text={dialogInfo.notice.text}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{dialogInfo.check ? (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setChecked(!checked);
|
||||
}}
|
||||
icon={
|
||||
checked
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
iconColor={
|
||||
checked ? colors.secondary.icon : colors.primary.icon
|
||||
}
|
||||
{dialogInfo?.notice ? (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "flex-start"
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
height={35}
|
||||
iconSize={20}
|
||||
width="100%"
|
||||
title={dialogInfo.check.info}
|
||||
type={checked ? dialogInfo.check.type || "plain" : "plain"}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
>
|
||||
<Notice
|
||||
type={dialogInfo.notice.type || "information"}
|
||||
text={dialogInfo.notice.text}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{dialogInfo.check ? (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setChecked(!checked);
|
||||
}}
|
||||
icon={
|
||||
checked
|
||||
? "check-circle-outline"
|
||||
: "checkbox-blank-circle-outline"
|
||||
}
|
||||
iconColor={
|
||||
checked ? colors.secondary.icon : colors.primary.icon
|
||||
}
|
||||
style={{
|
||||
justifyContent: "flex-start"
|
||||
}}
|
||||
height={35}
|
||||
iconSize={20}
|
||||
width="100%"
|
||||
title={dialogInfo.check.info}
|
||||
type={checked ? dialogInfo.check.type || "plain" : "plain"}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
<DialogButtons
|
||||
onPressNegative={onNegativePress}
|
||||
onPressPositive={
|
||||
|
||||
@@ -17,25 +17,24 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { IconButtonProps } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { IconButton, IconButtonProps } from "../ui/icon-button";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { LeftMenus } from "./left-menus";
|
||||
import { RightMenus } from "./right-menus";
|
||||
|
||||
export const Header = ({
|
||||
renderedInRoute,
|
||||
@@ -64,6 +63,9 @@ export const Header = ({
|
||||
state.selectionMode
|
||||
]);
|
||||
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const isTablet = deviceMode === "tablet";
|
||||
|
||||
const onScroll = useCallback(
|
||||
(data: { x: number; y: number; id?: string; route: string }) => {
|
||||
if (data.route !== renderedInRoute || data.id !== id) return;
|
||||
@@ -85,34 +87,58 @@ export const Header = ({
|
||||
};
|
||||
}, [borderHidden, onScroll]);
|
||||
|
||||
const HeaderWrapper = hasSearch ? Pressable : View;
|
||||
const _onLeftButtonPress = () => {
|
||||
if (onLeftMenuButtonPress) return onLeftMenuButtonPress();
|
||||
|
||||
if (!canGoBack) {
|
||||
if (fluidTabsRef.current?.isDrawerOpen()) {
|
||||
Navigation.closeDrawer();
|
||||
} else {
|
||||
Navigation.openDrawer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
Navigation.goBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginBottom: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<HeaderWrapper
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
borderRadius: 10,
|
||||
paddingVertical: 3,
|
||||
borderWidth: hasSearch ? 1 : 0,
|
||||
borderColor: colors.primary.border,
|
||||
paddingHorizontal: !hasSearch ? 0 : DefaultAppStyles.GAP_SMALL,
|
||||
borderRadius: Radius.S,
|
||||
paddingHorizontal: Spacing.LEVEL_2,
|
||||
paddingVertical: Spacing.LEVEL_3,
|
||||
backgroundColor: colors.secondary.background,
|
||||
alignItems: "center"
|
||||
}}
|
||||
testID="search-header"
|
||||
onPress={() => {
|
||||
onSearch?.();
|
||||
}}
|
||||
>
|
||||
<LeftMenus
|
||||
canGoBack={canGoBack}
|
||||
onLeftButtonPress={onLeftMenuButtonPress}
|
||||
/>
|
||||
{isTablet && !canGoBack ? null : (
|
||||
<IconButton
|
||||
testID={notesnook.ids.default.header.buttons.left}
|
||||
left={40}
|
||||
top={40}
|
||||
onPress={_onLeftButtonPress}
|
||||
onLongPress={() => {
|
||||
Navigation.popToTop();
|
||||
}}
|
||||
style={{
|
||||
width: 20,
|
||||
height: 20
|
||||
}}
|
||||
size={20}
|
||||
name={canGoBack ? "arrow-left" : "menu"}
|
||||
iconFamily="notesnook"
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!title ? (
|
||||
<View
|
||||
@@ -123,18 +149,39 @@ export const Header = ({
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
) : hasSearch ? (
|
||||
<Paragraph>
|
||||
{selectionMode
|
||||
? `${selectedItemsList.length} selected`
|
||||
: strings.searchInRoute(title)}
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Heading size={AppFontSize.lg}>{title}</Heading>
|
||||
<Heading fontSize="XL">
|
||||
{selectionMode ? `${selectedItemsList.length} selected` : title}
|
||||
</Heading>
|
||||
)}
|
||||
|
||||
<RightMenus rightButton={rightButton} />
|
||||
</HeaderWrapper>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{rightButton ? (
|
||||
<IconButton {...rightButton} color={colors.primary.icon} />
|
||||
) : null}
|
||||
|
||||
{hasSearch ? (
|
||||
<IconButton
|
||||
color={colors.primary.icon}
|
||||
size={20}
|
||||
onPress={() => {
|
||||
onSearch?.();
|
||||
}}
|
||||
style={{
|
||||
width: 20,
|
||||
height: 20
|
||||
}}
|
||||
iconFamily="notesnook"
|
||||
name="search"
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
|
||||
export const LeftMenus = ({
|
||||
canGoBack,
|
||||
onLeftButtonPress
|
||||
}: {
|
||||
canGoBack?: boolean;
|
||||
onLeftButtonPress?: () => void;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const isTablet = deviceMode === "tablet";
|
||||
|
||||
const _onLeftButtonPress = () => {
|
||||
if (onLeftButtonPress) return onLeftButtonPress();
|
||||
|
||||
if (!canGoBack) {
|
||||
if (fluidTabsRef.current?.isDrawerOpen()) {
|
||||
Navigation.closeDrawer();
|
||||
} else {
|
||||
Navigation.openDrawer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
Navigation.goBack();
|
||||
};
|
||||
|
||||
return isTablet && !canGoBack ? null : (
|
||||
<IconButton
|
||||
testID={notesnook.ids.default.header.buttons.left}
|
||||
left={40}
|
||||
top={40}
|
||||
onPress={_onLeftButtonPress}
|
||||
onLongPress={() => {
|
||||
Navigation.popToTop();
|
||||
}}
|
||||
name={canGoBack ? "arrow-left" : "menu"}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -22,22 +22,22 @@ import {
|
||||
GroupingByIdKey,
|
||||
GroupingKey,
|
||||
GroupOptions,
|
||||
ItemType
|
||||
ItemType,
|
||||
Item,
|
||||
VirtualizedGrouping
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import React, { RefObject } from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import { Radius, Spacing } from "../../../common/design/spacing";
|
||||
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
|
||||
import { presentSheet } from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { RouteName } from "../../../stores/use-navigation-store";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import Sort from "../../sheets/sort";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
|
||||
type SectionHeaderProps = {
|
||||
item: GroupHeader;
|
||||
@@ -49,8 +49,9 @@ type SectionHeaderProps = {
|
||||
group: GroupingKey;
|
||||
groupId?: string;
|
||||
type?: GroupingByIdKey;
|
||||
onOpenJumpToDialog: () => void;
|
||||
itemCount?: number;
|
||||
data?: VirtualizedGrouping<Item>;
|
||||
ref?: RefObject<FlatList>;
|
||||
};
|
||||
|
||||
export const SectionHeader = React.memo<
|
||||
@@ -64,10 +65,11 @@ export const SectionHeader = React.memo<
|
||||
screen,
|
||||
groupOptions,
|
||||
group,
|
||||
onOpenJumpToDialog,
|
||||
itemCount,
|
||||
groupId,
|
||||
type
|
||||
type,
|
||||
data,
|
||||
ref
|
||||
}: SectionHeaderProps) {
|
||||
const { colors } = useThemeColors();
|
||||
const isCompactModeEnabled = useIsCompactModeEnabled(
|
||||
@@ -78,8 +80,9 @@ export const SectionHeader = React.memo<
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginBottom: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
marginBottom: Spacing.LEVEL_3,
|
||||
marginTop: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -88,58 +91,36 @@ export const SectionHeader = React.memo<
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
justifyContent: "space-between",
|
||||
borderBottomWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
paddingBottom: 1,
|
||||
paddingTop:
|
||||
index === 0
|
||||
? DefaultAppStyles.GAP_VERTICAL
|
||||
: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
onOpenJumpToDialog();
|
||||
}}
|
||||
hitSlop={{ top: 10, left: 10, right: 30, bottom: 15 }}
|
||||
<Paragraph
|
||||
fontSize="MD"
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
flexDirection: "row",
|
||||
width: "auto"
|
||||
alignSelf: "center",
|
||||
textAlignVertical: "center"
|
||||
}}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
<Heading
|
||||
size={AppFontSize.xxs}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
textAlignVertical: "center"
|
||||
}}
|
||||
color={color || colors.primary.accent}
|
||||
>
|
||||
{!item.title || item.title === ""
|
||||
? screen === "Search"
|
||||
? strings.results(itemCount || 0)
|
||||
: strings.pinned().toUpperCase()
|
||||
: item.title.toUpperCase()}
|
||||
</Heading>
|
||||
</Pressable>
|
||||
{!item.title || item.title === ""
|
||||
? screen === "Search"
|
||||
? strings.results(itemCount || 0)
|
||||
: strings.pinned().toUpperCase()
|
||||
: item.title.toUpperCase()}
|
||||
</Paragraph>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{index === 0 ? (
|
||||
<>
|
||||
<IconButton
|
||||
name={
|
||||
groupOptions.sortDirection === "asc"
|
||||
? "sort-ascending"
|
||||
: "sort-descending"
|
||||
}
|
||||
name={"sliders"}
|
||||
iconFamily="notesnook"
|
||||
color={colors.secondary.icon}
|
||||
testID="icon-sort"
|
||||
onPress={() => {
|
||||
@@ -155,15 +136,20 @@ export const SectionHeader = React.memo<
|
||||
hideGroupOptions={
|
||||
screen === "Reminders" || screen === "Search"
|
||||
}
|
||||
data={data}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderWidth: 1,
|
||||
borderRadius: Radius.XS,
|
||||
borderColor: colors.secondary.border
|
||||
}}
|
||||
size={AppFontSize.lg - 2}
|
||||
size={16}
|
||||
/>
|
||||
<IconButton
|
||||
hidden={
|
||||
@@ -173,14 +159,16 @@ export const SectionHeader = React.memo<
|
||||
screen !== "Search"
|
||||
}
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderWidth: 1,
|
||||
borderRadius: Radius.XXS,
|
||||
borderColor: colors.secondary.border
|
||||
}}
|
||||
testID="icon-compact-mode"
|
||||
color={colors.secondary.icon}
|
||||
name={
|
||||
isCompactModeEnabled ? "view-list" : "view-list-outline"
|
||||
}
|
||||
name={"view-list"}
|
||||
iconFamily="notesnook"
|
||||
onPress={() => {
|
||||
SettingsService.set({
|
||||
[dataType === "notebook"
|
||||
@@ -192,20 +180,10 @@ export const SectionHeader = React.memo<
|
||||
: "normal"
|
||||
});
|
||||
}}
|
||||
size={AppFontSize.lg - 2}
|
||||
size={16}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{/* <IconButton
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25
|
||||
}}
|
||||
color={colors.secondary.icon}
|
||||
name={"chevron-down"}
|
||||
size={SIZE.lg - 2}
|
||||
/> */}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -28,7 +28,6 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import { EntityLevel, decode } from "entities";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
|
||||
import useNavigationStore, {
|
||||
RouteParams
|
||||
@@ -56,6 +55,9 @@ import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import dayjs from "dayjs";
|
||||
import { ExpiryDate } from "../../ui/expiry-date";
|
||||
import { Radius, Spacing } from "../../../common/design/spacing";
|
||||
import { create } from "zustand";
|
||||
import { FontFamily } from "../../../common/design/font";
|
||||
|
||||
type NoteItemProps = {
|
||||
item: Note | BaseTrashItem<Note>;
|
||||
@@ -72,6 +74,24 @@ type NoteItemProps = {
|
||||
renderedInRoute?: keyof RouteParams;
|
||||
};
|
||||
|
||||
const useShowMoreStore = create<{
|
||||
showMoreStatus: Record<string, boolean>;
|
||||
show: (id: string) => void;
|
||||
hide: (id: string) => void;
|
||||
}>((set) => ({
|
||||
showMoreStatus: {},
|
||||
show(id) {
|
||||
set((state) => ({
|
||||
showMoreStatus: { ...state.showMoreStatus, [id]: true }
|
||||
}));
|
||||
},
|
||||
hide(id) {
|
||||
set((state) => ({
|
||||
showMoreStatus: { ...state.showMoreStatus, [id]: false }
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
const NoteItem = ({
|
||||
item,
|
||||
isTrash,
|
||||
@@ -98,68 +118,54 @@ const NoteItem = ({
|
||||
const primaryColors = isEditingNote ? colors.selected : colors.primary;
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
const [selected] = useIsSelected(item);
|
||||
const showMore = useShowMoreStore((state) => state.showMoreStatus[item.id]);
|
||||
const statusIcons = [
|
||||
{
|
||||
condition: item.conflicted,
|
||||
name: "alert-circle",
|
||||
color: colors.error.accent
|
||||
},
|
||||
{
|
||||
condition: item.localOnly,
|
||||
testID: "sync-off",
|
||||
name: "sync-off",
|
||||
color: primaryColors.icon
|
||||
},
|
||||
{
|
||||
condition: item.readonly,
|
||||
testID: "pencil-lock",
|
||||
name: "pencil-lock",
|
||||
color: primaryColors.icon
|
||||
},
|
||||
{
|
||||
condition: item.pinned,
|
||||
testID: "icon-pinned",
|
||||
name: "pin",
|
||||
color: primaryColors.icon
|
||||
},
|
||||
{
|
||||
condition: !!locked,
|
||||
testID: "lock",
|
||||
name: "lock",
|
||||
color: primaryColors.icon
|
||||
},
|
||||
{
|
||||
condition: item.favorite,
|
||||
testID: "star-filled",
|
||||
name: "star-outline",
|
||||
color: "orange"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
flexShrink: 1
|
||||
flexShrink: 1,
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{compactMode ? null : (
|
||||
<Paragraph
|
||||
style={{
|
||||
fontSize: AppFontSize.xxxs,
|
||||
color: colors.secondary.paragraph
|
||||
}}
|
||||
>
|
||||
{getFormattedDate(
|
||||
date,
|
||||
dayjs(date).isBefore(dayjs().subtract(1, "day").hour(23))
|
||||
? "date"
|
||||
: "time"
|
||||
)}
|
||||
</Paragraph>
|
||||
)}
|
||||
|
||||
{compactMode ? (
|
||||
<Paragraph
|
||||
numberOfLines={1}
|
||||
color={color?.colorCode || primaryColors.heading}
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
paddingRight: 10
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
) : (
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
color={color?.colorCode || primaryColors.heading}
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
paddingRight: 10
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
)}
|
||||
|
||||
{item.headline && !compactMode ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
color={primaryColors.paragraph}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{decode(item.headline, {
|
||||
level: EntityLevel.HTML
|
||||
})}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
{compactMode ? null : (
|
||||
<View
|
||||
style={{
|
||||
@@ -167,50 +173,52 @@ const NoteItem = ({
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
marginTop: DefaultAppStyles.GAP_VERTICAL_SMALL,
|
||||
columnGap: 8,
|
||||
rowGap: 4,
|
||||
columnGap: Spacing.LEVEL_0,
|
||||
rowGap: Spacing.LEVEL_0,
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{!isTrash ? (
|
||||
<>
|
||||
{item.conflicted ? (
|
||||
<Icon
|
||||
name="alert-circle"
|
||||
size={AppFontSize.sm}
|
||||
color={colors.error.accent}
|
||||
/>
|
||||
) : null}
|
||||
{statusIcons
|
||||
.filter((statusIcon) => statusIcon.condition)
|
||||
.map((statusIcon) => (
|
||||
<View
|
||||
key={statusIcon.testID || statusIcon.name}
|
||||
style={{
|
||||
borderRadius: Radius.XXS,
|
||||
paddingHorizontal: 3,
|
||||
paddingVertical: 3,
|
||||
// borderWidth: 1,
|
||||
// borderColor: primaryColors.border,
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
testID={statusIcon.testID}
|
||||
name={statusIcon.name}
|
||||
size={12}
|
||||
iconFamily="notesnook"
|
||||
color={statusIcon.color}
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
|
||||
{item.localOnly ? (
|
||||
<Icon
|
||||
testID="sync-off"
|
||||
name="sync-off"
|
||||
size={AppFontSize.sm}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{item.readonly ? (
|
||||
<Icon
|
||||
testID="pencil-lock"
|
||||
name="pencil-lock"
|
||||
size={AppFontSize.sm}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{attachmentsCount > 0 ? (
|
||||
{attachmentsCount !== 0 ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 2
|
||||
borderRadius: Radius.XXS,
|
||||
paddingHorizontal: 3,
|
||||
paddingVertical: 3,
|
||||
gap: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="attachment"
|
||||
<AppIcon
|
||||
name="link"
|
||||
iconFamily="notesnook"
|
||||
size={AppFontSize.sm}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
@@ -223,93 +231,34 @@ const NoteItem = ({
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{item.pinned ? (
|
||||
<Icon
|
||||
testID="icon-pinned"
|
||||
name="pin-outline"
|
||||
size={AppFontSize.sm}
|
||||
color={color?.colorCode || primaryColors.accent}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{locked ? (
|
||||
<Icon
|
||||
name="lock"
|
||||
testID="note-locked-icon"
|
||||
size={AppFontSize.sm}
|
||||
color={primaryColors.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{item.favorite ? (
|
||||
<Icon
|
||||
testID="icon-star"
|
||||
name="star-outline"
|
||||
size={AppFontSize.sm}
|
||||
color="orange"
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{reminder ? (
|
||||
<ReminderTime
|
||||
reminder={reminder}
|
||||
color={color?.colorCode}
|
||||
textStyle={{
|
||||
fontSize: AppFontSize.xxs
|
||||
}}
|
||||
short
|
||||
iconSize={AppFontSize.xxs}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL / 2,
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{item.expiryDate?.value ? (
|
||||
<ExpiryDate
|
||||
note={item as Note}
|
||||
color={color?.colorCode}
|
||||
textStyle={{ fontSize: AppFontSize.xxs }}
|
||||
short
|
||||
iconSize={AppFontSize.xxs}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL / 2,
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{notebooks?.items
|
||||
?.filter(
|
||||
(item) =>
|
||||
renderedInRoute !== "Notebook" ||
|
||||
item.id !== useNavigationStore.getState().focusedRouteId
|
||||
)
|
||||
.filter((_, index) => showMore || index < 1)
|
||||
.map((item) => (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
paddingVertical: 2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL / 2,
|
||||
borderWidth: 0.5,
|
||||
borderColor: primaryColors.border,
|
||||
paddingVertical: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL / 2
|
||||
gap: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="book-outline"
|
||||
size={AppFontSize.xxxs}
|
||||
name="bookmark"
|
||||
iconFamily="notesnook"
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Paragraph
|
||||
size={AppFontSize.xxxs}
|
||||
fontSize="XS"
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{item.title}
|
||||
@@ -317,31 +266,64 @@ const NoteItem = ({
|
||||
</View>
|
||||
))}
|
||||
|
||||
{!isTrash && !compactMode && tags
|
||||
? tags.items?.map((item) =>
|
||||
item.id ? (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL / 2,
|
||||
borderWidth: 0.5,
|
||||
borderColor:
|
||||
color?.colorCode || primaryColors.border,
|
||||
paddingVertical: 1
|
||||
}}
|
||||
{tags?.items
|
||||
?.filter((_, index) => showMore || index < 1)
|
||||
.map((item) =>
|
||||
item.id ? (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
paddingVertical: 2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
<Paragraph
|
||||
size={AppFontSize.xxxs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
#{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null
|
||||
)
|
||||
: null}
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null
|
||||
)}
|
||||
|
||||
{(() => {
|
||||
const filteredNotebooks = (notebooks?.items || []).filter(
|
||||
(nb) =>
|
||||
renderedInRoute !== "Notebook" ||
|
||||
nb.id !== useNavigationStore.getState().focusedRouteId
|
||||
);
|
||||
const filteredTags = (tags?.items || []).filter((t) => t.id);
|
||||
const totalNotebooks = filteredNotebooks.length;
|
||||
const totalTags = filteredTags.length;
|
||||
const hasMore = totalNotebooks > 1 || totalTags > 1;
|
||||
if (!hasMore) return null;
|
||||
const hiddenCount =
|
||||
(totalNotebooks > 1 ? totalNotebooks - 1 : 0) +
|
||||
(totalTags > 1 ? totalTags - 1 : 0);
|
||||
return (
|
||||
<Heading
|
||||
size={AppFontSize.xs}
|
||||
color={colors.primary.accent}
|
||||
onPress={() => {
|
||||
if (showMore) {
|
||||
useShowMoreStore.getState().hide(item.id);
|
||||
} else {
|
||||
useShowMoreStore.getState().show(item.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{showMore
|
||||
? "Show less"
|
||||
: `+${hiddenCount} ${strings.more()}`}
|
||||
</Heading>
|
||||
);
|
||||
})()}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -372,6 +354,140 @@ const NoteItem = ({
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{color ? (
|
||||
<View
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 100,
|
||||
backgroundColor: color?.colorCode
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
flexGrow: 1,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
numberOfLines={1}
|
||||
color={primaryColors.heading}
|
||||
size={AppFontSize.sm}
|
||||
style={{
|
||||
flexShrink: 1
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Heading>
|
||||
|
||||
<IconButton
|
||||
testID={notesnook.listitem.menu}
|
||||
color={colors.secondary.icon}
|
||||
name="dots-three"
|
||||
iconFamily="notesnook"
|
||||
size={20}
|
||||
onPress={() => !noOpen && Properties.present(item)}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
height: undefined,
|
||||
width: undefined,
|
||||
borderRadius: 100,
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{item.headline && !compactMode ? (
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
color={primaryColors.paragraph}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{decode(item.headline, {
|
||||
level: EntityLevel.HTML
|
||||
})}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{compactMode ? null : (
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_0,
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<AppIcon size={13} name="calendar" iconFamily="notesnook" />
|
||||
<Paragraph
|
||||
style={{
|
||||
fontSize: AppFontSize.xs,
|
||||
color: colors.secondary.paragraph
|
||||
}}
|
||||
>
|
||||
{getFormattedDate(
|
||||
date,
|
||||
dayjs(date).isBefore(dayjs().subtract(1, "day").hour(23))
|
||||
? "date"
|
||||
: "time"
|
||||
)}
|
||||
</Paragraph>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{item.expiryDate?.value ? (
|
||||
<ExpiryDate
|
||||
note={item as Note}
|
||||
color={color?.colorCode}
|
||||
textStyle={{ fontSize: AppFontSize.xxs }}
|
||||
short
|
||||
iconSize={AppFontSize.xxs}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL / 2,
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{reminder ? (
|
||||
<ReminderTime
|
||||
reminder={reminder}
|
||||
color={color?.colorCode}
|
||||
textStyle={{
|
||||
fontSize: AppFontSize.xs,
|
||||
fontFamily: FontFamily.REGULAR
|
||||
}}
|
||||
short
|
||||
iconSize={AppFontSize.xxs}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL / 2,
|
||||
alignSelf: "flex-start",
|
||||
backgroundColor: colors.primary.shade
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
@@ -382,7 +498,7 @@ const NoteItem = ({
|
||||
{compactMode ? (
|
||||
<>
|
||||
{item.conflicted ? (
|
||||
<Icon
|
||||
<AppIcon
|
||||
name="alert-circle"
|
||||
style={{
|
||||
marginRight: 6
|
||||
@@ -393,7 +509,7 @@ const NoteItem = ({
|
||||
) : null}
|
||||
|
||||
{locked ? (
|
||||
<Icon
|
||||
<AppIcon
|
||||
name="lock"
|
||||
testID="note-locked-icon"
|
||||
size={AppFontSize.sm}
|
||||
@@ -405,7 +521,7 @@ const NoteItem = ({
|
||||
) : null}
|
||||
|
||||
{item.favorite ? (
|
||||
<Icon
|
||||
<AppIcon
|
||||
testID="icon-star"
|
||||
name="star-outline"
|
||||
size={AppFontSize.sm}
|
||||
@@ -445,22 +561,7 @@ const NoteItem = ({
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<IconButton
|
||||
testID={notesnook.listitem.menu}
|
||||
color={colors.secondary.icon}
|
||||
name="dots-horizontal"
|
||||
size={AppFontSize.lg}
|
||||
onPress={() => !noOpen && Properties.present(item)}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
height: 35,
|
||||
width: 35,
|
||||
borderRadius: 100,
|
||||
alignItems: "center"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,8 @@ import { useTabStore } from "../../../screens/editor/tiptap/use-tab-store";
|
||||
import { editorController } from "../../../screens/editor/tiptap/utils";
|
||||
import { RouteParams } from "../../../stores/use-navigation-store";
|
||||
import NotePreview from "../../note-history/preview";
|
||||
import SelectionWrapper, { selectItem } from "../selection-wrapper";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
import { selectItem } from "../../../stores/use-selection-store";
|
||||
|
||||
export const openNote = async (
|
||||
item: Note,
|
||||
@@ -94,6 +95,7 @@ type NoteWrapperProps = {
|
||||
isRenderedInActionSheet: boolean;
|
||||
locked?: boolean;
|
||||
renderedInRoute?: keyof RouteParams;
|
||||
hasGroupHeader?: boolean;
|
||||
};
|
||||
|
||||
export const NoteWrapper = React.memo<
|
||||
@@ -103,6 +105,7 @@ export const NoteWrapper = React.memo<
|
||||
item,
|
||||
index,
|
||||
isRenderedInActionSheet,
|
||||
hasGroupHeader,
|
||||
...restProps
|
||||
}: NoteWrapperProps) {
|
||||
const isTrash = item.type === "trash";
|
||||
@@ -113,6 +116,7 @@ export const NoteWrapper = React.memo<
|
||||
onPress={() => openNote(item as Note, isTrash, isRenderedInActionSheet)}
|
||||
isSheet={isRenderedInActionSheet}
|
||||
item={item}
|
||||
hasGroupHeader={hasGroupHeader}
|
||||
index={index}
|
||||
color={restProps.color?.colorCode}
|
||||
>
|
||||
|
||||
@@ -27,7 +27,8 @@ import Navigation from "../../../services/navigation";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { useTrashStore } from "../../../stores/use-trash-store";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import SelectionWrapper, { selectItem } from "../selection-wrapper";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
import { selectItem } from "../../../stores/use-selection-store";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export const openNotebook = (item: Notebook | BaseTrashItem<Notebook>) => {
|
||||
|
||||
@@ -36,7 +36,8 @@ import { IconButton } from "../../ui/icon-button";
|
||||
import { ReminderTime } from "../../ui/reminder-time";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import SelectionWrapper, { selectItem } from "../selection-wrapper";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
import { selectItem } from "../../../stores/use-selection-store";
|
||||
|
||||
const ReminderItem = React.memo(
|
||||
({
|
||||
|
||||
@@ -23,26 +23,9 @@ import React, { PropsWithChildren, useRef } from "react";
|
||||
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
|
||||
import { useTabStore } from "../../../screens/editor/tiptap/use-tab-store";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import { View } from "react-native";
|
||||
|
||||
export function selectItem(item: Item) {
|
||||
if (useSelectionStore.getState().selectionMode === item.type) {
|
||||
const { selectionMode, clearSelection, setSelectedItem } =
|
||||
useSelectionStore.getState();
|
||||
|
||||
if (selectionMode === item.type) {
|
||||
setSelectedItem(item.id);
|
||||
}
|
||||
|
||||
if (useSelectionStore.getState().selectedItemsList.length === 0) {
|
||||
clearSelection();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
|
||||
type SelectionWrapperProps = PropsWithChildren<{
|
||||
item: Item;
|
||||
@@ -51,6 +34,7 @@ type SelectionWrapperProps = PropsWithChildren<{
|
||||
isSheet?: boolean;
|
||||
color?: string;
|
||||
index?: number;
|
||||
hasGroupHeader?: boolean;
|
||||
}>;
|
||||
|
||||
const SelectionWrapper = ({
|
||||
@@ -60,6 +44,7 @@ const SelectionWrapper = ({
|
||||
isSheet,
|
||||
children,
|
||||
color,
|
||||
hasGroupHeader,
|
||||
index = 0
|
||||
}: SelectionWrapperProps) => {
|
||||
const itemId = useRef(item.id);
|
||||
@@ -86,47 +71,37 @@ const SelectionWrapper = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
customColor={
|
||||
isEditingNote
|
||||
? colors.selected.background
|
||||
: isSheet
|
||||
? colors.primary.hover
|
||||
: "transparent"
|
||||
}
|
||||
testID={testID}
|
||||
onLongPress={onLongPress}
|
||||
onPress={onPress}
|
||||
customSelectedColor={colors.primary.hover}
|
||||
customAlpha={!isDark ? -0.02 : 0.02}
|
||||
customOpacity={1}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
overflow: "hidden",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: compactMode ? 4 : DefaultAppStyles.GAP_VERTICAL,
|
||||
borderRadius: isSheet ? 10 : 0,
|
||||
marginBottom: isSheet ? DefaultAppStyles.GAP_VERTICAL : undefined
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
backgroundColor: isEditingNote ? colors.selected.background : undefined
|
||||
}}
|
||||
>
|
||||
{isEditingNote ? (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: color || colors.selected.accent,
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 5
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{children}
|
||||
</Pressable>
|
||||
<Pressable
|
||||
customColor={isSheet ? colors.primary.hover : "transparent"}
|
||||
testID={testID}
|
||||
onLongPress={onLongPress}
|
||||
onPress={onPress}
|
||||
customSelectedColor={colors.primary.hover}
|
||||
customAlpha={!isDark ? -0.02 : 0.02}
|
||||
customOpacity={1}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
overflow: "hidden",
|
||||
borderRadius: 0,
|
||||
marginTop: hasGroupHeader ? 0 : Spacing.LEVEL_3,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
alignItems: "flex-start",
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
paddingTop: hasGroupHeader ? 0 : Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ import { Properties } from "../../properties";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import SelectionWrapper, { selectItem } from "../selection-wrapper";
|
||||
import SelectionWrapper from "../selection-wrapper";
|
||||
import { selectItem } from "../../../stores/use-selection-store";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
|
||||
|
||||
@@ -21,10 +21,11 @@ import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import { Message, useMessageStore } from "../../stores/use-message-store";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
export const Card = ({
|
||||
@@ -47,15 +48,19 @@ export const Card = ({
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
marginBottom: Spacing.LEVEL_4,
|
||||
marginTop: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Pressable
|
||||
onPress={messageBoardState.onPress}
|
||||
type="plain"
|
||||
style={{
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
paddingVertical: Spacing.LEVEL_3,
|
||||
paddingHorizontal: Spacing.LEVEL_2,
|
||||
backgroundColor: colors.primary.shade,
|
||||
borderRadius: Radius.S,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
@@ -66,23 +71,24 @@ export const Card = ({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
width: "100%",
|
||||
flexShrink: 1,
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale,
|
||||
borderRadius: 100,
|
||||
width: 34,
|
||||
height: 34,
|
||||
borderRadius: Radius.XXS,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
justifyContent: "center",
|
||||
backgroundColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
size={AppFontSize.xxxl}
|
||||
color={
|
||||
messageBoardState.type === "error" ? colors.error.icon : color
|
||||
}
|
||||
size={16}
|
||||
color={colors.primary.icon}
|
||||
allowFontScaling
|
||||
name={messageBoardState.icon}
|
||||
/>
|
||||
@@ -90,25 +96,26 @@ export const Card = ({
|
||||
|
||||
<View
|
||||
style={{
|
||||
marginLeft: 10,
|
||||
marginRight: 10
|
||||
gap: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
<Heading
|
||||
style={{
|
||||
flexWrap: "nowrap",
|
||||
flexShrink: 1
|
||||
}}
|
||||
size={AppFontSize.sm}
|
||||
fontSize="MD"
|
||||
color={colors.primary.heading}
|
||||
>
|
||||
{messageBoardState.actionText}
|
||||
</Paragraph>
|
||||
<Paragraph color={colors.secondary.paragraph} size={AppFontSize.xs}>
|
||||
</Heading>
|
||||
<Paragraph color={colors.secondary.paragraph} fontSize="SM">
|
||||
{messageBoardState.message}
|
||||
</Paragraph>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<AppIcon size={24} name="chevron-right" color={colors.primary.icon} />
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -43,9 +43,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { getGroupOptions } from "../../hooks/use-group-options";
|
||||
import { useIsCompactModeEnabled } from "../../hooks/use-is-compact-mode-enabled";
|
||||
import { eSendEvent } from "../../services/event-manager";
|
||||
import { RouteName } from "../../stores/use-navigation-store";
|
||||
import { eOpenJumpToDialog } from "../../utils/events";
|
||||
import { SectionHeader } from "../list-items/headers/section-header";
|
||||
import { NoteWrapper } from "../list-items/note/wrapper";
|
||||
import { NotebookWrapper } from "../list-items/notebook/wrapper";
|
||||
@@ -190,12 +188,8 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
type={props.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
ref={props.scrollRef}
|
||||
data={items}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -205,6 +199,9 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
color={color.current}
|
||||
notebooks={notebooks.current}
|
||||
reminder={reminder.current}
|
||||
hasGroupHeader={
|
||||
groupHeader && previousIndex.current === index && !isSheet
|
||||
}
|
||||
attachmentsCount={attachmentsCount.current}
|
||||
date={getDate(item as Note, group)}
|
||||
isRenderedInActionSheet={isSheet}
|
||||
@@ -229,12 +226,8 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
type={props.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
ref={props.scrollRef}
|
||||
data={items}
|
||||
/>
|
||||
) : null}
|
||||
<NotebookWrapper
|
||||
@@ -260,12 +253,8 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
type={props.type}
|
||||
groupId={props.groupId}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
ref={props.scrollRef}
|
||||
data={items}
|
||||
/>
|
||||
) : null}
|
||||
<ReminderItem
|
||||
@@ -289,12 +278,8 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
type={props.type}
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
ref={props.scrollRef}
|
||||
data={items}
|
||||
/>
|
||||
) : null}
|
||||
<TagItem
|
||||
@@ -319,12 +304,8 @@ export function ListItemWrapper(props: ListItemWrapperProps) {
|
||||
color={props.customAccentColor}
|
||||
groupOptions={groupOptions}
|
||||
itemCount={items?.placeholders.length}
|
||||
onOpenJumpToDialog={() => {
|
||||
eSendEvent(eOpenJumpToDialog, {
|
||||
ref: props.scrollRef,
|
||||
data: items
|
||||
});
|
||||
}}
|
||||
ref={props.scrollRef}
|
||||
data={items}
|
||||
/>
|
||||
) : null}
|
||||
<SearchResult item={item as HighlightedResult} />
|
||||
|
||||
@@ -43,6 +43,8 @@ import ColorPicker from "../dialogs/color-picker";
|
||||
import PaywallSheet from "../sheets/paywall";
|
||||
import { Button } from "../ui/button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
|
||||
const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -80,12 +82,11 @@ const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
|
||||
key={item.id}
|
||||
onPress={toggleColor}
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 5
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{isLinked ? (
|
||||
@@ -151,31 +152,10 @@ export const ColorTags = ({ item }: { item: Note }) => {
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexGrow: isTablet ? undefined : 1,
|
||||
flexDirection: "row",
|
||||
marginLeft: 5,
|
||||
flexShrink: 2
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{!colorNotes || !colorNotes.length ? (
|
||||
<Button
|
||||
onPress={onPress}
|
||||
buttonType={{
|
||||
text: colors.primary.accent
|
||||
}}
|
||||
title={strings.addColor()}
|
||||
type="secondary"
|
||||
icon="plus"
|
||||
iconPosition="right"
|
||||
height={30}
|
||||
fontSize={AppFontSize.xs}
|
||||
style={{
|
||||
marginRight: 5,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
{colorNotes?.length ? (
|
||||
<LegendList
|
||||
data={colorNotes}
|
||||
estimatedItemSize={30}
|
||||
@@ -184,11 +164,14 @@ export const ColorTags = ({ item }: { item: Note }) => {
|
||||
bounces={false}
|
||||
renderItem={renderItem}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={{
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
ListFooterComponent={
|
||||
<Pressable
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
@@ -197,16 +180,17 @@ export const ColorTags = ({ item }: { item: Note }) => {
|
||||
type="secondary"
|
||||
onPress={onPress}
|
||||
>
|
||||
<Icon
|
||||
<AppIcon
|
||||
testID="icon-plus"
|
||||
iconFamily="notesnook"
|
||||
name="plus"
|
||||
color={colors.primary.icon}
|
||||
color={colors.primary.accent}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
</Pressable>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,7 @@ import DateTimePickerModal from "react-native-modal-datetime-picker";
|
||||
import { db } from "../../common/database";
|
||||
import { Item, Note } from "@notesnook/core";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
export const DateMeta = ({ item }: { item: Item }) => {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const [isDatePickerVisible, setIsDatePickerVisible] = useState(false);
|
||||
@@ -50,44 +51,52 @@ export const DateMeta = ({ item }: { item: Item }) => {
|
||||
key={key}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "48.5%",
|
||||
justifyContent: "space-between",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL / 2
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: Radius.XS,
|
||||
padding: Spacing.LEVEL_2,
|
||||
gap: Spacing.LEVEL_1,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{strings.dateDescFromKey(
|
||||
key as
|
||||
| "dateDeleted"
|
||||
| "dateEdited"
|
||||
| "dateModified"
|
||||
| "dateCreated"
|
||||
| "dateUploaded"
|
||||
)}
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
onPress={
|
||||
item.type !== "note"
|
||||
? undefined
|
||||
: () => {
|
||||
setIsDatePickerVisible(true);
|
||||
}
|
||||
}
|
||||
>
|
||||
{getFormattedDate(
|
||||
key === "dateCreated"
|
||||
? dateCreated
|
||||
: (item[key as keyof Item] as string),
|
||||
"date-time"
|
||||
)}
|
||||
{key === "dateCreated" && item.type === "note" ? (
|
||||
<>
|
||||
{" "}
|
||||
<AppIcon name="pencil" size={AppFontSize.md} />
|
||||
</>
|
||||
) : null}
|
||||
</Paragraph>
|
||||
<View>
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{strings.dateDescFromKey(
|
||||
key as
|
||||
| "dateDeleted"
|
||||
| "dateEdited"
|
||||
| "dateModified"
|
||||
| "dateCreated"
|
||||
| "dateUploaded"
|
||||
)}
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
size={AppFontSize.xs}
|
||||
color={colors.primary.paragraph}
|
||||
fontFamily="MEDIUM"
|
||||
onPress={
|
||||
item.type !== "note"
|
||||
? undefined
|
||||
: () => {
|
||||
setIsDatePickerVisible(true);
|
||||
}
|
||||
}
|
||||
>
|
||||
{getFormattedDate(
|
||||
key === "dateCreated"
|
||||
? dateCreated
|
||||
: (item[key as keyof Item] as string),
|
||||
"date-time"
|
||||
)}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
{key === "dateCreated" && item.type === "note" ? (
|
||||
<>
|
||||
<AppIcon name="edit-pencil" size={10} iconFamily="notesnook" />
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -119,9 +128,10 @@ export const DateMeta = ({ item }: { item: Item }) => {
|
||||
<View
|
||||
style={{
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.primary.border,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingTop: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
borderColor: colors.primary.border,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{getDateMeta().map(renderItem)}
|
||||
|
||||
@@ -18,14 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
import { eSendEvent, presentSheet } from "../../services/event-manager";
|
||||
import { eOnLoadNote } from "../../utils/events";
|
||||
import {
|
||||
eSendEvent,
|
||||
presentSheet,
|
||||
sendItemUpdateEvent,
|
||||
ToastManager
|
||||
} from "../../services/event-manager";
|
||||
import { eOnLoadNote, refreshNotesPage } from "../../utils/events";
|
||||
import { fluidTabsRef } from "../../utils/global-refs";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
@@ -37,27 +41,43 @@ import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { DateMeta } from "./date-meta";
|
||||
import { Items } from "./items";
|
||||
import Notebooks from "./notebooks";
|
||||
import { TagStrip, Tags } from "./tags";
|
||||
import { Tags } from "./tags";
|
||||
import { Dialog } from "../dialog";
|
||||
|
||||
const Line = ({ top = 6, bottom = 6 }) => {
|
||||
const { colors } = useThemeColors();
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
height: 1,
|
||||
backgroundColor: colors.primary.border,
|
||||
width: "100%",
|
||||
marginTop: top,
|
||||
marginBottom: bottom
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import { Button } from "../ui/button";
|
||||
import ManageTags from "../../screens/manage-tags";
|
||||
import ColorPicker from "../dialogs/color-picker";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { useMenuStore } from "../../stores/use-menu-store";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { useIsFeatureAvailable } from "@notesnook/common";
|
||||
import PaywallSheet from "../sheets/paywall";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
|
||||
export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const colorFeature = useIsFeatureAvailable("colors");
|
||||
const [noteNotebooks, setNoteNotebooks] = useState([]);
|
||||
const [tags, setTags] = useState([]);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const colorNotes = useMenuStore((state) => state.colorNotes);
|
||||
useEffect(() => {
|
||||
async function getNotebooks() {
|
||||
let filteredNotebooks = await db.relations.to(item, "notebook").resolve();
|
||||
return filteredNotebooks || [];
|
||||
}
|
||||
if (item.type === "note") {
|
||||
getNotebooks().then((notebooks) => setNoteNotebooks(notebooks));
|
||||
db.relations
|
||||
.to(item, "tag")
|
||||
.resolve()
|
||||
.then((tags) => {
|
||||
setTags(tags);
|
||||
});
|
||||
}
|
||||
}, [item]);
|
||||
|
||||
if (!item || !item.id) {
|
||||
return (
|
||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||
@@ -74,7 +94,8 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
backgroundColor: colors.primary.background,
|
||||
borderBottomRightRadius: DDS.isLargeTablet() ? 10 : 1,
|
||||
borderBottomLeftRadius: DDS.isLargeTablet() ? 10 : 1,
|
||||
maxHeight: "100%"
|
||||
maxHeight: "100%",
|
||||
paddingTop: Spacing.LEVEL_3
|
||||
}}
|
||||
nestedScrollEnabled
|
||||
bounces={false}
|
||||
@@ -83,111 +104,240 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
renderItem={() => (
|
||||
<View
|
||||
style={{
|
||||
gap: DefaultAppStyles.GAP_VERTICAL
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{item.type === "note" ? (
|
||||
<ColorPicker
|
||||
visible={visible}
|
||||
setVisible={setVisible}
|
||||
onColorAdded={async (color) => {
|
||||
await db.relations.to(item, "color").unlink();
|
||||
await db.relations.add(color, item);
|
||||
useRelationStore.getState().update();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
Navigation.queueRoutesForUpdate();
|
||||
sendItemUpdateEvent(color.id, "color");
|
||||
eSendEvent(refreshNotesPage);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
paddingHorizontal: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1,
|
||||
gap: 5
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{item.type === "color" ? (
|
||||
<Pressable
|
||||
type="accent"
|
||||
accentColor={item.colorCode}
|
||||
accentText={colors.static.white}
|
||||
{noteNotebooks?.map((item) => (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 100,
|
||||
marginRight: 10
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
paddingVertical: 2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_0
|
||||
}}
|
||||
/>
|
||||
) : item.type === "tag" ? (
|
||||
<Icon
|
||||
name="pound"
|
||||
size={AppFontSize.lg}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
) : null}
|
||||
>
|
||||
<AppIcon
|
||||
name="bookmark"
|
||||
iconFamily="notesnook"
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.icon}
|
||||
/>
|
||||
<Paragraph fontSize="XS" color={colors.secondary.paragraph}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
))}
|
||||
|
||||
<Heading size={AppFontSize.lg}>{item.title}</Heading>
|
||||
{tags?.map((item) =>
|
||||
item.id ? (
|
||||
<View
|
||||
key={item.id}
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
paddingVertical: 2,
|
||||
backgroundColor: colors.secondary.background,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={AppFontSize.xs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null
|
||||
)}
|
||||
</View>
|
||||
|
||||
{item.type === "note" ? (
|
||||
<IconButton
|
||||
name="open-in-new"
|
||||
type="plain"
|
||||
color={colors.primary.icon}
|
||||
size={AppFontSize.lg}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
alignSelf: "flex-start"
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1,
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
onPress={() => {
|
||||
close();
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: item,
|
||||
newTab: true
|
||||
});
|
||||
if (!DDS.isTab) {
|
||||
fluidTabsRef.current?.goToPage("editor");
|
||||
}
|
||||
>
|
||||
{item.type === "color" ? (
|
||||
<Pressable
|
||||
type="accent"
|
||||
accentColor={item.colorCode}
|
||||
accentText={colors.static.white}
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
) : item.type === "tag" ? (
|
||||
<AppIcon
|
||||
name="shopping-mode"
|
||||
iconFamily="evilicons"
|
||||
size={AppFontSize.lg}
|
||||
color={colors.primary.icon}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Heading size={AppFontSize.xl}>{item.title}</Heading>
|
||||
</View>
|
||||
|
||||
{item.type === "note" ? (
|
||||
<IconButton
|
||||
name="square-out"
|
||||
iconFamily="notesnook"
|
||||
type="plain"
|
||||
color={colors.primary.icon}
|
||||
size={AppFontSize.lg}
|
||||
style={{
|
||||
alignSelf: "flex-start"
|
||||
}}
|
||||
onPress={() => {
|
||||
close();
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: item,
|
||||
newTab: true
|
||||
});
|
||||
if (!DDS.isTab) {
|
||||
fluidTabsRef.current?.goToPage("editor");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{(item.type === "notebook" || item.type === "reminder") &&
|
||||
item.description ? (
|
||||
<Paragraph>{item.description}</Paragraph>
|
||||
) : null}
|
||||
|
||||
{item.type === "reminder" ? (
|
||||
<ReminderTime
|
||||
reminder={item}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderWidth: 0,
|
||||
alignSelf: "flex-start",
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 0,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
fontSize={AppFontSize.xs}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{(item.type === "notebook" || item.type === "reminder") &&
|
||||
item.description ? (
|
||||
<Paragraph>{item.description}</Paragraph>
|
||||
<DateMeta item={item} />
|
||||
|
||||
{item.type === "note" && colorNotes.length > 0 ? (
|
||||
<Tags close={close} item={item} />
|
||||
) : null}
|
||||
|
||||
{item.type === "note" ? (
|
||||
<TagStrip close={close} item={item} />
|
||||
) : null}
|
||||
|
||||
{item.type === "reminder" ? (
|
||||
<ReminderTime
|
||||
reminder={item}
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
borderWidth: 0,
|
||||
alignSelf: "flex-start",
|
||||
backgroundColor: "transparent",
|
||||
paddingHorizontal: 0,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
borderBottomWidth: 1,
|
||||
borderTopWidth: colorNotes.length > 0 ? 1 : 0,
|
||||
borderColor: colors.primary.border,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
paddingTop: colorNotes.length > 0 ? Spacing.LEVEL_2 : 0,
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={async () => {
|
||||
ManageTags.present([item.id]);
|
||||
close();
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.primary.paragraph
|
||||
}}
|
||||
title={strings.addTag()}
|
||||
type={colorNotes?.length ? "accent-outline" : "shade"}
|
||||
icon="plus"
|
||||
iconFamily="notesnook"
|
||||
style={{
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
width: colorNotes?.length > 0 ? "100%" : "48.5%"
|
||||
}}
|
||||
fontSize={AppFontSize.xs}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{colorNotes.length > 0 ? null : (
|
||||
<Button
|
||||
onPress={() => {
|
||||
if (colorFeature && !colorFeature.isAllowed) {
|
||||
ToastManager.show({
|
||||
message: colorFeature.error,
|
||||
type: "info",
|
||||
context: "local",
|
||||
actionText: strings.upgrade(),
|
||||
func: () => {
|
||||
PaywallSheet.present(colorFeature);
|
||||
ToastManager.hide();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
useSettingStore.getState().setSheetKeyboardHandler(false);
|
||||
setVisible(true);
|
||||
}}
|
||||
title={strings.addColor()}
|
||||
type="secondaryAccented"
|
||||
icon="plus"
|
||||
iconFamily="notesnook"
|
||||
style={{
|
||||
width: "48.5%",
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_2
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<DateMeta item={item} />
|
||||
<Line bottom={0} top={0} />
|
||||
|
||||
{item.type === "note" ? (
|
||||
<>
|
||||
<Tags close={close} item={item} />
|
||||
<Line bottom={0} top={0} />
|
||||
</>
|
||||
) : null}
|
||||
{item.type === "note" ? (
|
||||
{/* {item.type === "note" ? (
|
||||
<Notebooks note={item} close={close} />
|
||||
) : null}
|
||||
) : null} */}
|
||||
<Items
|
||||
item={item}
|
||||
buttons={buttons}
|
||||
|
||||
@@ -33,6 +33,7 @@ import AppIcon from "../ui/AppIcon";
|
||||
import { Button } from "../ui/button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
|
||||
const TOP_BAR_ITEMS: ActionId[] = [
|
||||
"pin",
|
||||
@@ -152,7 +153,7 @@ export const Items = ({
|
||||
key={item.id}
|
||||
style={{
|
||||
alignItems: "center",
|
||||
width: columnItemWidth - 8,
|
||||
width: columnItemWidth - 10,
|
||||
opacity: item.locked ? 0.5 : 1
|
||||
}}
|
||||
>
|
||||
@@ -161,12 +162,12 @@ export const Items = ({
|
||||
type={item.checked ? "shade" : "secondary"}
|
||||
testID={"icon-" + item.id}
|
||||
style={{
|
||||
height: columnItemWidth / 1.5,
|
||||
width: columnItemWidth - 8,
|
||||
width: columnItemWidth - 10,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
borderRadius: 10,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: DDS.isTab ? 7 : 3.5
|
||||
marginBottom: 6
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
@@ -253,7 +254,7 @@ export const Items = ({
|
||||
key={item.id}
|
||||
testID={"icon-" + item.id}
|
||||
style={{
|
||||
width: columnItemWidth - 8,
|
||||
width: columnItemWidth - 10,
|
||||
alignSelf: "flex-start",
|
||||
gap: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
@@ -261,24 +262,25 @@ export const Items = ({
|
||||
<View
|
||||
style={{
|
||||
height: columnItemWidth / 2,
|
||||
width: columnItemWidth - DefaultAppStyles.GAP_SMALL,
|
||||
width: 65,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderWidth: 1,
|
||||
borderRadius: defaultBorderRadius,
|
||||
borderColor: item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
: colors.primary.border,
|
||||
overflow: "hidden"
|
||||
backgroundColor: item.checked
|
||||
? colors.primary.shade
|
||||
: colors.secondary.background,
|
||||
borderRadius: Radius.XS,
|
||||
overflow: "hidden",
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
paddingHorizontal: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
allowFontScaling
|
||||
size={DDS.isTab ? AppFontSize.xxl : AppFontSize.md + 4}
|
||||
size={16}
|
||||
color={
|
||||
item.checked
|
||||
? item.activeColor || colors.primary.accent
|
||||
? colors.primary.icon
|
||||
: item.id === "delete" || item.id === "trash"
|
||||
? colors.error.icon
|
||||
: colors.secondary.icon
|
||||
@@ -310,7 +312,13 @@ export const Items = ({
|
||||
|
||||
<Paragraph
|
||||
textBreakStrategy="simple"
|
||||
size={AppFontSize.xxs}
|
||||
fontSize="XXS"
|
||||
fontFamily="MEDIUM"
|
||||
color={
|
||||
item.checked
|
||||
? colors.primary.paragraph
|
||||
: colors.secondary.paragraph
|
||||
}
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
{item.title}
|
||||
@@ -321,8 +329,12 @@ export const Items = ({
|
||||
[
|
||||
colors.error.icon,
|
||||
colors.primary.accent,
|
||||
colors.primary.border,
|
||||
colors.primary.icon,
|
||||
colors.primary.paragraph,
|
||||
colors.primary.shade,
|
||||
colors.secondary.background,
|
||||
colors.secondary.icon,
|
||||
colors.secondary.paragraph,
|
||||
colors.static.orange,
|
||||
columnItemWidth,
|
||||
topBarSorting
|
||||
@@ -352,16 +364,18 @@ export const Items = ({
|
||||
autoplay={false}
|
||||
showPagination
|
||||
paginationStyleItemActive={{
|
||||
borderRadius: 2,
|
||||
backgroundColor: colors.selected.background,
|
||||
height: 6,
|
||||
marginHorizontal: 2
|
||||
borderRadius: 6,
|
||||
backgroundColor: colors.selected.accent,
|
||||
height: 5,
|
||||
width: 20,
|
||||
marginHorizontal: 3
|
||||
}}
|
||||
paginationStyleItemInactive={{
|
||||
borderRadius: 2,
|
||||
borderRadius: 6,
|
||||
backgroundColor: colors.secondary.background,
|
||||
height: 6,
|
||||
marginHorizontal: 2
|
||||
height: 5,
|
||||
width: 14,
|
||||
marginHorizontal: 3
|
||||
}}
|
||||
paginationStyle={{
|
||||
position: "relative",
|
||||
@@ -380,7 +394,7 @@ export const Items = ({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
gap: 5,
|
||||
gap: Spacing.LEVEL_2,
|
||||
width: width
|
||||
}}
|
||||
>
|
||||
@@ -394,7 +408,7 @@ export const Items = ({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
gap: 5,
|
||||
gap: Spacing.LEVEL_1,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -17,18 +17,17 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import ManageTags from "../../screens/manage-tags";
|
||||
import { TaggedNotes } from "../../screens/notes/tagged";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { sleep } from "../../utils/time";
|
||||
import { Button } from "../ui/button";
|
||||
import { ColorTags } from "./color-tags";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
|
||||
export const Tags = ({ item, close }) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -37,32 +36,12 @@ export const Tags = ({ item, close }) => {
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
alignSelf: "center",
|
||||
justifyContent: "space-between",
|
||||
width: "100%"
|
||||
width: "100%",
|
||||
borderTopWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
paddingVertical: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onPress={async () => {
|
||||
ManageTags.present([item.id]);
|
||||
close();
|
||||
}}
|
||||
buttonType={{
|
||||
text: colors.primary.accent
|
||||
}}
|
||||
title={strings.addTag()}
|
||||
type="secondary"
|
||||
icon="plus"
|
||||
iconPosition="right"
|
||||
fontSize={AppFontSize.xs}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
}}
|
||||
/>
|
||||
<ColorTags item={item} />
|
||||
</View>
|
||||
) : null;
|
||||
|
||||
@@ -40,6 +40,7 @@ import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { defaultBorderRadius } from "../../../utils/size";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getContainerBorder } from "../../../utils/colors";
|
||||
import { Radius, Spacing } from "../../../common/design/spacing";
|
||||
|
||||
export const AddNotebookSheet = ({
|
||||
notebook,
|
||||
@@ -127,53 +128,63 @@ export const AddNotebookSheet = ({
|
||||
...getElevationStyle(5),
|
||||
width: DDS.isTab ? 400 : "85%",
|
||||
maxHeight: 450,
|
||||
borderRadius: defaultBorderRadius,
|
||||
borderRadius: Radius.LG,
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: 12,
|
||||
gap: Spacing.LEVEL_4,
|
||||
paddingVertical: Spacing.LEVEL_4,
|
||||
...getContainerBorder(colors.primary.border, 0.5),
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
gap: Spacing.LEVEL_4
|
||||
}}
|
||||
>
|
||||
<DialogHeader
|
||||
title={notebook ? strings.editNotebook() : strings.newNotebook()}
|
||||
/>
|
||||
|
||||
<Input
|
||||
fwdRef={titleInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.title}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
<View
|
||||
style={{
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
onLayout={() => {
|
||||
setTimeout(() => {
|
||||
titleInput?.current?.focus();
|
||||
}, 300);
|
||||
}}
|
||||
placeholder={strings.enterNotebookTitle()}
|
||||
onSubmit={() => {
|
||||
descriptionInput.current?.focus();
|
||||
}}
|
||||
returnKeyLabel="Next"
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.title : title.current}
|
||||
/>
|
||||
>
|
||||
<Input
|
||||
fwdRef={titleInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.title}
|
||||
onChangeText={(value) => {
|
||||
title.current = value;
|
||||
}}
|
||||
onLayout={() => {
|
||||
setTimeout(() => {
|
||||
titleInput?.current?.focus();
|
||||
}, 300);
|
||||
}}
|
||||
placeholder={"eg. My Notebook"}
|
||||
onSubmit={() => {
|
||||
descriptionInput.current?.focus();
|
||||
}}
|
||||
label={strings.enterNotebookTitle()}
|
||||
returnKeyLabel="Next"
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.title : title.current}
|
||||
/>
|
||||
|
||||
<Input
|
||||
fwdRef={descriptionInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.description}
|
||||
onChangeText={(value) => {
|
||||
description.current = value;
|
||||
}}
|
||||
placeholder={strings.enterNotebookDescription()}
|
||||
returnKeyLabel={strings.next()}
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.description : ""}
|
||||
/>
|
||||
<Input
|
||||
fwdRef={descriptionInput}
|
||||
testID={notesnook.ids.dialogs.notebook.inputs.description}
|
||||
onChangeText={(value) => {
|
||||
description.current = value;
|
||||
}}
|
||||
label={strings.enterNotebookDescription()}
|
||||
placeholder={"eg. This is My Notebook"}
|
||||
returnKeyLabel={strings.next()}
|
||||
returnKeyType="next"
|
||||
defaultValue={notebook ? notebook.description : ""}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<DialogButtons
|
||||
onPressNegative={() => {
|
||||
|
||||
@@ -19,19 +19,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import {
|
||||
GroupingByIdKey,
|
||||
GroupHeader,
|
||||
GroupingKey,
|
||||
GroupOptions,
|
||||
Item,
|
||||
ItemType,
|
||||
SortOptions
|
||||
SortOptions,
|
||||
VirtualizedGrouping
|
||||
} from "@notesnook/core";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import {
|
||||
getGroupOptions,
|
||||
setGroupOptionsById
|
||||
} from "../../../hooks/use-group-options";
|
||||
import React, { RefObject, useEffect, useRef, useState } from "react";
|
||||
import { FlatList, View } from "react-native";
|
||||
import { eSendEvent } from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import { RouteName } from "../../../stores/use-navigation-store";
|
||||
@@ -46,13 +49,20 @@ import { Button } from "../../ui/button";
|
||||
import { Pressable } from "../../ui/pressable";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { Radius, Spacing } from "../../../common/design/spacing";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { useMessageStore } from "../../../stores/use-message-store";
|
||||
|
||||
const Sort = ({
|
||||
dataType,
|
||||
screen,
|
||||
hideGroupOptions,
|
||||
group: groupType,
|
||||
groupId,
|
||||
type
|
||||
type,
|
||||
hideJumpToSection,
|
||||
ref,
|
||||
data
|
||||
}: {
|
||||
dataType: ItemType;
|
||||
type?: GroupingByIdKey;
|
||||
@@ -60,29 +70,42 @@ const Sort = ({
|
||||
group: GroupingKey;
|
||||
hideGroupOptions?: boolean;
|
||||
groupId?: string;
|
||||
hideJumpToSection?: boolean;
|
||||
data?: VirtualizedGrouping<Item>;
|
||||
ref?: RefObject<FlatList>;
|
||||
}) => {
|
||||
const { colors } = useThemeColors();
|
||||
const [groups, setGroups] = useState<
|
||||
{
|
||||
index: number;
|
||||
group: GroupHeader;
|
||||
}[]
|
||||
>();
|
||||
const offsets = useRef<number[]>([]);
|
||||
const scrollRef = useRef<RefObject<FlatList>>(undefined);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const currentScrollPosition = useRef(0);
|
||||
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
getGroupOptions(groupType, groupId, type)
|
||||
);
|
||||
|
||||
const getSortButtonTitle = () => {
|
||||
const { sortDirection, groupBy, sortBy } = groupOptions || {};
|
||||
const getSortButtonTitle = (type: "asc" | "desc") => {
|
||||
const { groupBy, sortBy } = groupOptions || {};
|
||||
const isAlphabetical = groupBy === "abc" || sortBy === "title";
|
||||
const isDueDate = sortBy === "dueDate";
|
||||
const isRelevance = sortBy === "relevance";
|
||||
|
||||
if (sortDirection === "asc") {
|
||||
if (type === "asc") {
|
||||
if (isAlphabetical) return strings.aToZ();
|
||||
if (isDueDate) return strings.earliestFirst();
|
||||
if (isRelevance) return strings.leastRelevantFirst();
|
||||
return strings.oldNew();
|
||||
return strings.oldestFirst();
|
||||
} else {
|
||||
if (isAlphabetical) return strings.zToA();
|
||||
if (isDueDate) return strings.latestFirst();
|
||||
if (isRelevance) return strings.mostRelevantFirst();
|
||||
return strings.newOld();
|
||||
return strings.newestFirst();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,13 +134,49 @@ const Sort = ({
|
||||
await updateGroupOptions(_groupOptions);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
data?.groups?.().then((groups) => {
|
||||
setGroups(groups);
|
||||
offsets.current = [];
|
||||
groups.map((item, index) => {
|
||||
let offset = 35 * index;
|
||||
let groupIndex = item.index;
|
||||
const messageState = useMessageStore.getState().message;
|
||||
const msgOffset = messageState?.visible ? 60 : 10;
|
||||
|
||||
groupIndex = groupIndex + 1;
|
||||
groupIndex = groupIndex - (index + 1);
|
||||
offset = offset + groupIndex * 100 + msgOffset;
|
||||
offsets.current.push(offset);
|
||||
});
|
||||
|
||||
const index = offsets.current?.findIndex((o, i) => {
|
||||
return (
|
||||
o <= currentScrollPosition.current + 100 &&
|
||||
offsets.current[i + 1] - 100 > currentScrollPosition.current
|
||||
);
|
||||
});
|
||||
|
||||
setCurrentIndex(index < 0 ? 0 : index);
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
const onPress = (item: { index: number; group: GroupHeader }) => {
|
||||
scrollRef.current?.current?.scrollToIndex({
|
||||
index: item.index,
|
||||
animated: true
|
||||
});
|
||||
close();
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
justifyContent: "space-between",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
gap: Spacing.LEVEL_3,
|
||||
paddingTop: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -128,9 +187,9 @@ const Sort = ({
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<Heading size={AppFontSize.lg}>{strings.sortBy()}</Heading>
|
||||
<Heading fontSize="LG">{strings.sortBy()}</Heading>
|
||||
|
||||
<Button
|
||||
{/* <Button
|
||||
title={getSortButtonTitle()}
|
||||
icon={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
@@ -145,14 +204,16 @@ const Sort = ({
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
onPress={setOrderBy}
|
||||
/>
|
||||
/> */}
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
borderBottomColor: colors.primary.border
|
||||
borderBottomColor: colors.primary.border,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
gap: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
{Object.keys(SORT).map((item) => {
|
||||
@@ -178,40 +239,92 @@ const Sort = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
<View
|
||||
key={item}
|
||||
type={groupOptions?.sortBy === item ? "selected" : "plain"}
|
||||
noborder
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
borderRadius: 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
}}
|
||||
onPress={async () => {
|
||||
const _groupOptions: GroupOptions = {
|
||||
...groupOptions,
|
||||
sortBy: item as SortOptions["sortBy"]
|
||||
};
|
||||
await updateGroupOptions(_groupOptions);
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
{strings.sortByStrings[
|
||||
item as keyof typeof strings.sortByStrings
|
||||
]()}
|
||||
</Paragraph>
|
||||
<Pressable
|
||||
type={
|
||||
groupOptions?.sortBy === item ? "selected" : "plain-outline"
|
||||
}
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
borderRadius: Radius.XS,
|
||||
paddingHorizontal: Spacing.LEVEL_2,
|
||||
paddingVertical: Spacing.LEVEL_2
|
||||
}}
|
||||
onPress={async () => {
|
||||
const _groupOptions: GroupOptions = {
|
||||
...groupOptions,
|
||||
sortBy: item as SortOptions["sortBy"]
|
||||
};
|
||||
await updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
{strings.sortByStrings[
|
||||
item as keyof typeof strings.sortByStrings
|
||||
]()}
|
||||
</Paragraph>
|
||||
|
||||
{groupOptions?.sortBy === item ? (
|
||||
<AppIcon
|
||||
size={AppFontSize.lg}
|
||||
name="check"
|
||||
color={colors.selected.accent}
|
||||
/>
|
||||
{groupOptions?.sortBy === item ? (
|
||||
<AppIcon
|
||||
size={AppFontSize.lg}
|
||||
name="checkbox"
|
||||
iconFamily="notesnook"
|
||||
color={[colors.selected.accent, "white"]}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
|
||||
{groupOptions.sortBy === item ? (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.secondary.background,
|
||||
borderRadius: Radius.S,
|
||||
padding: Spacing.LEVEL_1,
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
borderRadius: Radius.XS,
|
||||
...(groupOptions?.sortDirection === "desc"
|
||||
? getElevationStyle(10)
|
||||
: {})
|
||||
}}
|
||||
type={
|
||||
groupOptions?.sortDirection === "desc"
|
||||
? "accent-background"
|
||||
: "plain"
|
||||
}
|
||||
title={getSortButtonTitle("desc")}
|
||||
/>
|
||||
|
||||
<Button
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
borderRadius: Radius.XS,
|
||||
...(groupOptions?.sortDirection === "asc"
|
||||
? getElevationStyle(10)
|
||||
: {})
|
||||
}}
|
||||
type={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
? "accent-background"
|
||||
: "plain"
|
||||
}
|
||||
title={getSortButtonTitle("asc")}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
@@ -223,18 +336,19 @@ const Sort = ({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingBottom: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Heading size={AppFontSize.lg}>{strings.groupBy()}</Heading>
|
||||
<Heading fontSize="LG">{strings.groupBy()}</Heading>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap"
|
||||
flexWrap: "wrap",
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{Object.keys(GROUP).map((item) => (
|
||||
@@ -243,16 +357,14 @@ const Sort = ({
|
||||
type={
|
||||
groupOptions?.groupBy === GROUP[item as keyof typeof GROUP]
|
||||
? "selected"
|
||||
: "plain"
|
||||
: "plain-outline"
|
||||
}
|
||||
noborder
|
||||
style={{
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "row",
|
||||
borderRadius: 0,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
width: "auto",
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_1
|
||||
}}
|
||||
onPress={async () => {
|
||||
const _groupOptions: GroupOptions = {
|
||||
@@ -262,24 +374,82 @@ const Sort = ({
|
||||
await updateGroupOptions(_groupOptions);
|
||||
}}
|
||||
>
|
||||
<Paragraph>
|
||||
<Paragraph
|
||||
fontFamily={
|
||||
groupOptions?.groupBy === GROUP[item as keyof typeof GROUP]
|
||||
? "SEMI_BOLD"
|
||||
: "REGULAR"
|
||||
}
|
||||
color={
|
||||
groupOptions?.groupBy === GROUP[item as keyof typeof GROUP]
|
||||
? colors.primary.paragraph
|
||||
: colors.secondary.paragraph
|
||||
}
|
||||
>
|
||||
{strings.groupByStrings[
|
||||
item as keyof typeof strings.groupByStrings
|
||||
]()}
|
||||
</Paragraph>
|
||||
|
||||
{groupOptions.groupBy === item ? (
|
||||
<AppIcon
|
||||
size={AppFontSize.lg}
|
||||
name="check"
|
||||
color={colors.selected.accent}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{!hideJumpToSection && groups ? (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingBottom: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<Heading fontSize="LG">{strings.jumpToGroup()}</Heading>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{groups?.map((item, index) => {
|
||||
return (
|
||||
<Pressable
|
||||
key={item.group.id}
|
||||
onPress={() => onPress(item)}
|
||||
type={currentIndex === index ? "selected" : "plain-outline"}
|
||||
style={{
|
||||
minWidth: "20%",
|
||||
width: null,
|
||||
borderRadius: 100,
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
paddingVertical: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={AppFontSize.sm}
|
||||
fontFamily={
|
||||
currentIndex === index ? "SEMI_BOLD" : "REGULAR"
|
||||
}
|
||||
color={colors.primary.paragraph}
|
||||
style={{
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{item.group.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,7 +21,6 @@ import { strings } from "@notesnook/intl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { db } from "../../common/database";
|
||||
import { useGroupOptions } from "../../hooks/use-group-options";
|
||||
import { presentSheet, ToastManager } from "../../services/event-manager";
|
||||
@@ -34,9 +33,7 @@ import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import { AddNotebookSheet } from "../sheets/add-notebook";
|
||||
import Sort from "../sheets/sort";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
|
||||
import { SideMenuHome } from "./side-menu-home";
|
||||
import { SideMenuNotebooks } from "./side-menu-notebooks";
|
||||
import { SideMenuTags } from "./side-menu-tags";
|
||||
@@ -44,12 +41,14 @@ import {
|
||||
useSideMenuNotebookSelectionStore,
|
||||
useSideMenuTagsSelectionStore
|
||||
} from "./stores";
|
||||
import { TabBarButton } from "./tab-bar-button";
|
||||
import { useSideBarDraggingStore } from "./dragging-store";
|
||||
import { Button } from "../ui/button";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { isFeatureAvailable } from "@notesnook/common";
|
||||
import PaywallSheet from "../sheets/paywall";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
|
||||
/**
|
||||
* Simple Tab View Implementation for the Side bar
|
||||
@@ -77,7 +76,7 @@ type SimpleTabViewProps = {
|
||||
};
|
||||
|
||||
const createSceneMap = (
|
||||
scenes: Record<string, React.ComponentType<any>>
|
||||
scenes: Record<string, React.ComponentType<unknown>>
|
||||
): ((props: { route: SimpleRoute }) => React.ReactNode) => {
|
||||
// eslint-disable-next-line react/display-name
|
||||
return ({ route }: { route: SimpleRoute }) => {
|
||||
@@ -216,55 +215,61 @@ const TabBar = (props: SimpleTabBarProps) => {
|
||||
const getIcon = (key: string) => {
|
||||
switch (key) {
|
||||
case "home":
|
||||
return "home-outline";
|
||||
return "home";
|
||||
case "notebooks":
|
||||
return "book-outline";
|
||||
return "bookmark";
|
||||
case "tags":
|
||||
return "pound";
|
||||
return "shopping-mode";
|
||||
default:
|
||||
return "home-outline";
|
||||
return "home";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
paddingVertical: DefaultAppStyles.GAP_SMALL,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.primary.border
|
||||
paddingHorizontal: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
{isSelectionEnabled ? (
|
||||
<>
|
||||
{[
|
||||
{
|
||||
title: "Select all",
|
||||
icon: "check-all"
|
||||
},
|
||||
{
|
||||
title: "Delete",
|
||||
icon: "delete"
|
||||
},
|
||||
{
|
||||
title: "Move",
|
||||
icon: "arrow-right-bold-box-outline",
|
||||
hidden:
|
||||
!notebookSelectionEnabled || props.navigationState.index !== 1
|
||||
},
|
||||
{
|
||||
title: "Close",
|
||||
icon: "close"
|
||||
}
|
||||
].map((item) =>
|
||||
item.hidden ? null : (
|
||||
<>
|
||||
<Pressable
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
backgroundColor: colors.primary.background,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.primary.border,
|
||||
paddingTop: Spacing.LEVEL_2
|
||||
}}
|
||||
>
|
||||
{isSelectionEnabled ? (
|
||||
<>
|
||||
{[
|
||||
{
|
||||
title: "Select all",
|
||||
icon: "checks"
|
||||
},
|
||||
{
|
||||
title: "Delete",
|
||||
icon: "trash"
|
||||
},
|
||||
{
|
||||
title: "Move",
|
||||
icon: "drive-file-move",
|
||||
hidden:
|
||||
!notebookSelectionEnabled || props.navigationState.index !== 1
|
||||
},
|
||||
{
|
||||
title: "Close",
|
||||
icon: "close"
|
||||
}
|
||||
].map((item) =>
|
||||
item.hidden ? null : (
|
||||
<TabBarButton
|
||||
key={item.title}
|
||||
icon={item.icon}
|
||||
label={item.title}
|
||||
onPress={async () => {
|
||||
switch (item.title) {
|
||||
case "Select all": {
|
||||
@@ -325,221 +330,177 @@ const TabBar = (props: SimpleTabBarProps) => {
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{dragging ? (
|
||||
<Button
|
||||
onPress={() => {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
type="accent"
|
||||
testID="check"
|
||||
title={strings.done()}
|
||||
icon={"check"}
|
||||
iconSize={AppFontSize.lg - 2}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
paddingVertical: 2,
|
||||
width: "25%"
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_2
|
||||
}}
|
||||
type="plain"
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
color={colors.primary.icon}
|
||||
size={AppFontSize.lg}
|
||||
/>
|
||||
<Paragraph
|
||||
color={colors.primary.paragraph}
|
||||
size={AppFontSize.xxxs - 1}
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</Pressable>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{dragging ? (
|
||||
<Button
|
||||
onPress={() => {
|
||||
useSideBarDraggingStore.setState({
|
||||
dragging: false
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
width: "100%"
|
||||
}}
|
||||
type="accent"
|
||||
testID="check"
|
||||
title={strings.done()}
|
||||
icon={"check"}
|
||||
iconSize={AppFontSize.lg - 2}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{props.navigationState.routes.map((route, index) => {
|
||||
const isFocused = props.navigationState.index === index;
|
||||
{props.navigationState.routes.map((route, index) => {
|
||||
const isFocused = props.navigationState.index === index;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={route.key}
|
||||
testID={`tab-${route.key}`}
|
||||
onPress={() => {
|
||||
props.jumpTo(route.key);
|
||||
switch (route.key) {
|
||||
case "notebooks":
|
||||
Navigation.routeNeedsUpdate(
|
||||
"Notebooks",
|
||||
Navigation.routeUpdateFunctions.Notebooks
|
||||
);
|
||||
break;
|
||||
case "tags":
|
||||
Navigation.routeNeedsUpdate(
|
||||
"Tags",
|
||||
Navigation.routeUpdateFunctions.Tags
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
paddingVertical: 2,
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
type={isFocused ? "selected" : "plain"}
|
||||
>
|
||||
<Icon
|
||||
name={getIcon(route.key)}
|
||||
color={
|
||||
isFocused ? colors.selected.icon : colors.primary.icon
|
||||
}
|
||||
size={AppFontSize.lg}
|
||||
return (
|
||||
<TabBarButton
|
||||
key={route.key}
|
||||
testID={`tab-${route.key}`}
|
||||
icon={getIcon(route.key)}
|
||||
label={route.title || ""}
|
||||
isActive={isFocused}
|
||||
onPress={() => {
|
||||
props.jumpTo(route.key);
|
||||
switch (route.key) {
|
||||
case "notebooks":
|
||||
Navigation.routeNeedsUpdate(
|
||||
"Notebooks",
|
||||
Navigation.routeUpdateFunctions.Notebooks
|
||||
);
|
||||
break;
|
||||
case "tags":
|
||||
Navigation.routeNeedsUpdate(
|
||||
"Tags",
|
||||
Navigation.routeUpdateFunctions.Tags
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{props.navigationState.index > 0 ? (
|
||||
<>
|
||||
<IconButton
|
||||
name="plus"
|
||||
testID="sidebar-add-button"
|
||||
size={AppFontSize.lg - 2}
|
||||
top={10}
|
||||
color={colors.primary.icon}
|
||||
onPress={async () => {
|
||||
if (props.navigationState.index === 1) {
|
||||
const notebooksFeature =
|
||||
await isFeatureAvailable("notebooks");
|
||||
if (!notebooksFeature.isAllowed) {
|
||||
PaywallSheet.present(notebooksFeature);
|
||||
return;
|
||||
}
|
||||
|
||||
AddNotebookSheet.present();
|
||||
} else {
|
||||
const tagsFeature = await isFeatureAvailable("tags");
|
||||
if (!tagsFeature.isAllowed) {
|
||||
PaywallSheet.present(tagsFeature);
|
||||
return;
|
||||
}
|
||||
presentDialog({
|
||||
title: strings.addTag(),
|
||||
paragraph: strings.addTagDesc(),
|
||||
input: true,
|
||||
positiveText: "Add",
|
||||
positivePress: async (tag) => {
|
||||
if (tag) {
|
||||
await db.tags.add({
|
||||
title: tag
|
||||
});
|
||||
useTagStore.getState().refresh();
|
||||
return true;
|
||||
}
|
||||
ToastManager.show({
|
||||
context: "local",
|
||||
type: "error",
|
||||
message: strings.allFieldsRequired()
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL
|
||||
}}
|
||||
>
|
||||
{props.navigationState.index > 0 ? (
|
||||
<>
|
||||
<TabBarButton
|
||||
icon="plus"
|
||||
testID="sidebar-add-button"
|
||||
label={
|
||||
props.navigationState.index === 1 ? "Notebook" : "Tag"
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35
|
||||
}}
|
||||
/>
|
||||
onPress={async () => {
|
||||
if (props.navigationState.index === 1) {
|
||||
const notebooksFeature =
|
||||
await isFeatureAvailable("notebooks");
|
||||
if (!notebooksFeature.isAllowed) {
|
||||
PaywallSheet.present(notebooksFeature);
|
||||
return;
|
||||
}
|
||||
|
||||
<IconButton
|
||||
name={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
? "sort-ascending"
|
||||
: "sort-descending"
|
||||
}
|
||||
top={10}
|
||||
testID="sidebar-sort-button"
|
||||
color={colors.primary.icon}
|
||||
onPress={() => {
|
||||
presentSheet({
|
||||
component: (
|
||||
<Sort
|
||||
dataType={
|
||||
props.navigationState.index === 1
|
||||
? "notebook"
|
||||
: "tag"
|
||||
AddNotebookSheet.present();
|
||||
} else {
|
||||
const tagsFeature =
|
||||
await isFeatureAvailable("tags");
|
||||
if (!tagsFeature.isAllowed) {
|
||||
PaywallSheet.present(tagsFeature);
|
||||
return;
|
||||
}
|
||||
presentDialog({
|
||||
title: strings.addTag(),
|
||||
inputLabel: "Enter title",
|
||||
inputPlaceholder: "eg. journal",
|
||||
input: true,
|
||||
positiveText: "Add",
|
||||
positivePress: async (tag) => {
|
||||
if (tag) {
|
||||
await db.tags.add({
|
||||
title: tag
|
||||
});
|
||||
useTagStore.getState().refresh();
|
||||
return true;
|
||||
}
|
||||
ToastManager.show({
|
||||
context: "local",
|
||||
type: "error",
|
||||
message: strings.allFieldsRequired()
|
||||
});
|
||||
return false;
|
||||
}
|
||||
group={
|
||||
props.navigationState.index === 1
|
||||
? "notebooks"
|
||||
: "tags"
|
||||
}
|
||||
hideGroupOptions
|
||||
/>
|
||||
)
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
width: 35,
|
||||
height: 35
|
||||
}}
|
||||
size={AppFontSize.lg - 2}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
{props.navigationState.index === 0 ? (
|
||||
<>
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
useThemeStore.getState().setColorScheme();
|
||||
}}
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28
|
||||
}}
|
||||
top={10}
|
||||
testID="sidebar-theme-button"
|
||||
color={colors.primary.icon}
|
||||
name={isDark ? "weather-night" : "weather-sunny"}
|
||||
size={AppFontSize.lg - 2}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<TabBarButton
|
||||
icon={
|
||||
groupOptions?.sortDirection === "asc"
|
||||
? "sort-ascending"
|
||||
: "sort-descending"
|
||||
}
|
||||
testID="sidebar-sort-button"
|
||||
label="Sort"
|
||||
onPress={() => {
|
||||
presentSheet({
|
||||
component: (
|
||||
<Sort
|
||||
type={
|
||||
props.navigationState.index === 1
|
||||
? "notebook"
|
||||
: "tag"
|
||||
}
|
||||
group={
|
||||
props.navigationState.index === 1
|
||||
? "notebooks"
|
||||
: "tags"
|
||||
}
|
||||
hideGroupOptions
|
||||
/>
|
||||
)
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{props.navigationState.index === 0 ? (
|
||||
<>
|
||||
<TabBarButton
|
||||
icon={isDark ? "dark-mode-outline" : "sun"}
|
||||
testID="sidebar-theme-button"
|
||||
label={isDark ? "Dark" : "Light"}
|
||||
onPress={() => {
|
||||
useThemeStore.getState().setColorScheme();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,10 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useTotalNotes } from "../../hooks/use-db-item";
|
||||
|
||||
import { db } from "../../common/database";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
subscribeToItemUpdate
|
||||
@@ -32,14 +32,14 @@ import Navigation from "../../services/navigation";
|
||||
import useNavigationStore, {
|
||||
RouteParams
|
||||
} from "../../stores/use-navigation-store";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { eAfterSync, eMenuItemUpdate } from "../../utils/events";
|
||||
import { SideMenuItem } from "../../utils/menu-items";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { useSideBarDraggingStore } from "./dragging-store";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
|
||||
export function MenuItem({
|
||||
item,
|
||||
@@ -144,30 +144,32 @@ export function MenuItem({
|
||||
style={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
borderRadius: defaultBorderRadius,
|
||||
borderRadius: Radius.XS,
|
||||
flexDirection: "row",
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
|
||||
paddingVertical: Spacing.LEVEL_1,
|
||||
marginBottom: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: DefaultAppStyles.GAP_SMALL,
|
||||
flexShrink: 1
|
||||
flexShrink: 1,
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{renderIcon ? (
|
||||
renderIcon(item, AppFontSize.md)
|
||||
) : (
|
||||
<Icon
|
||||
<AppIcon
|
||||
style={{
|
||||
textAlignVertical: "center",
|
||||
textAlign: "left"
|
||||
}}
|
||||
iconFamily="notesnook"
|
||||
allowFontScaling
|
||||
name={item.icon}
|
||||
color={
|
||||
@@ -185,20 +187,21 @@ export function MenuItem({
|
||||
color={
|
||||
isFocused ? colors.selected.paragraph : colors.primary.paragraph
|
||||
}
|
||||
size={AppFontSize.sm}
|
||||
style={{ flexShrink: 1 }}
|
||||
fontFamily={isFocused ? "MEDIUM" : "REGULAR"}
|
||||
fontSize="SM"
|
||||
>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
<Paragraph
|
||||
size={AppFontSize.xxs}
|
||||
fontSize="XS"
|
||||
color={
|
||||
isFocused ? colors.primary.paragraph : colors.secondary.paragraph
|
||||
}
|
||||
style={{
|
||||
marginLeft: DefaultAppStyles.GAP_SMALL
|
||||
marginLeft: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{menuItemCount}
|
||||
|
||||
@@ -24,19 +24,20 @@ import { StoreApi, UseBoundStore } from "zustand";
|
||||
import { useTotalNotes } from "../../hooks/use-db-item";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent,
|
||||
ToastManager
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { TreeItem } from "../../stores/create-notebook-tree-stores";
|
||||
import { SelectionStore } from "../../stores/item-selection-store";
|
||||
import { eOnNotebookUpdated } from "../../utils/events";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
import { IconButton } from "../ui/icon-button";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { AddNotebookSheet } from "../sheets/add-notebook";
|
||||
|
||||
export const NotebookItem = ({
|
||||
index,
|
||||
@@ -94,20 +95,31 @@ export const NotebookItem = ({
|
||||
};
|
||||
}, [item.notebook.id, notebook.id, onItemUpdate]);
|
||||
|
||||
const itemPadding =
|
||||
item.depth === 0 ? undefined : item.depth < 6 ? 15 * item.depth : 15 * 5;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingLeft:
|
||||
item.depth === 0
|
||||
? undefined
|
||||
: item.depth < 6
|
||||
? 15 * item.depth
|
||||
: 15 * 5,
|
||||
paddingLeft: itemPadding,
|
||||
width: "100%",
|
||||
marginTop: 2,
|
||||
opacity: item.disabled ? 0.5 : 1
|
||||
opacity: item.disabled ? 0.5 : 1,
|
||||
paddingBottom: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
{item.depth > 0 ? (
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: 1,
|
||||
backgroundColor: colors.primary.border,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
position: "absolute",
|
||||
left: itemPadding
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Pressable
|
||||
type={isFocused || selected ? "selected" : "transparent"}
|
||||
onLongPress={onLongPress}
|
||||
@@ -155,47 +167,39 @@ export const NotebookItem = ({
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
borderRadius: defaultBorderRadius,
|
||||
paddingRight: DefaultAppStyles.GAP_SMALL
|
||||
borderRadius: Radius.XS,
|
||||
paddingVertical: Spacing.LEVEL_1,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
marginBottom:
|
||||
expanded && item.hasChildren ? Spacing.LEVEL_0 : undefined
|
||||
// borderLeftWidth: item.depth > 0 ? 1 : undefined,
|
||||
// borderLeftColor: colors.primary.border
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1
|
||||
flexShrink: 1,
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
size={AppFontSize.md}
|
||||
color={
|
||||
selected || isFocused ? colors.selected.icon : colors.primary.icon
|
||||
}
|
||||
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
|
||||
onPress={() => {
|
||||
if (item.hasChildren && !disableExpand) {
|
||||
onToggleExpanded?.();
|
||||
} else {
|
||||
onPress?.();
|
||||
{item.depth === 0 ? (
|
||||
<AppIcon
|
||||
size={AppFontSize.md}
|
||||
color={
|
||||
selected || isFocused
|
||||
? colors.selected.icon
|
||||
: colors.primary.icon
|
||||
}
|
||||
}}
|
||||
top={0}
|
||||
left={50}
|
||||
bottom={0}
|
||||
right={40}
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
name={
|
||||
!item.hasChildren || disableExpand
|
||||
? "book-outline"
|
||||
: expanded
|
||||
? "chevron-down"
|
||||
: "chevron-right"
|
||||
}
|
||||
/>
|
||||
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
|
||||
style={{
|
||||
borderRadius: defaultBorderRadius
|
||||
}}
|
||||
iconFamily="notesnook"
|
||||
name={"bookmark"}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Paragraph
|
||||
color={
|
||||
@@ -210,34 +214,30 @@ export const NotebookItem = ({
|
||||
|
||||
<View
|
||||
style={{
|
||||
gap: DefaultAppStyles.GAP_SMALL,
|
||||
gap: Spacing.LEVEL_1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginLeft: DefaultAppStyles.GAP_SMALL
|
||||
marginLeft: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
{selectionEnabled ? (
|
||||
<View
|
||||
style={{
|
||||
width: 25,
|
||||
height: 25,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name={selected ? "checkbox-outline" : "checkbox-blank-outline"}
|
||||
name={selected ? "checkbox" : "box-empty"}
|
||||
iconFamily="notesnook"
|
||||
size={AppFontSize.md}
|
||||
color={selected ? colors.selected.icon : colors.primary.icon}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<Paragraph
|
||||
size={AppFontSize.xxs}
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
<Paragraph fontSize="SM" color={colors.secondary.paragraph}>
|
||||
{totalNotes?.(notebook?.id) || 0}
|
||||
</Paragraph>
|
||||
</>
|
||||
@@ -263,8 +263,81 @@ export const NotebookItem = ({
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{item.hasChildren ? (
|
||||
<IconButton
|
||||
size={12}
|
||||
color={
|
||||
selected || isFocused
|
||||
? colors.selected.icon
|
||||
: colors.primary.icon
|
||||
}
|
||||
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
|
||||
onPress={() => {
|
||||
if (item.hasChildren && !disableExpand) {
|
||||
onToggleExpanded?.();
|
||||
}
|
||||
}}
|
||||
top={0}
|
||||
left={20}
|
||||
bottom={0}
|
||||
right={20}
|
||||
style={{
|
||||
borderRadius: defaultBorderRadius,
|
||||
width: undefined,
|
||||
height: undefined
|
||||
}}
|
||||
iconFamily="notesnook"
|
||||
name={expanded ? "chevron-up" : "chevron-down"}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
</Pressable>
|
||||
{expanded && item.hasChildren && !selectionEnabled ? (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingLeft: (item.depth + 1) * 15
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
height: "100%",
|
||||
width: 1,
|
||||
backgroundColor: colors.primary.border,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
position: "absolute",
|
||||
left: (item.depth + 1) * 15
|
||||
}}
|
||||
/>
|
||||
<Pressable
|
||||
style={{
|
||||
// borderLeftWidth: 1,
|
||||
// borderLeftColor: colors.primary.border,
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_1,
|
||||
justifyContent: "flex-start",
|
||||
paddingVertical: Spacing.LEVEL_1,
|
||||
paddingHorizontal: Spacing.LEVEL_1,
|
||||
alignItems: "center"
|
||||
}}
|
||||
onPress={() => {
|
||||
AddNotebookSheet.present(undefined, item.notebook);
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="plus"
|
||||
size={14}
|
||||
style={{
|
||||
marginTop: -2
|
||||
}}
|
||||
iconFamily="notesnook"
|
||||
/>
|
||||
<Heading fontSize="SM">Create sub-notebook</Heading>
|
||||
</Pressable>{" "}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -70,7 +70,7 @@ export const PinnedSection = React.memo(
|
||||
menuPins.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
icon: item.type === "notebook" ? "notebook-outline" : "pound",
|
||||
icon: item.type === "notebook" ? "bookmark" : "shopping-mode",
|
||||
dataType: item.type,
|
||||
data: item,
|
||||
onPress: onPress,
|
||||
|
||||
@@ -30,6 +30,7 @@ import { Pressable } from "../ui/pressable";
|
||||
import { SvgView } from "../ui/svg";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { useSideBarDraggingStore } from "./dragging-store";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
|
||||
const SettingsIcon = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -77,31 +78,31 @@ export const SideMenuHeader = (props: { rightButtons?: IconButtonProps[] }) => {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.primary.border,
|
||||
paddingBottom: DefaultAppStyles.GAP,
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: DefaultAppStyles.GAP_SMALL,
|
||||
gap: Spacing.LEVEL_1,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: "black",
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 10
|
||||
width: 26,
|
||||
height: 26,
|
||||
borderRadius: Radius.XS,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<SvgView width={28} height={28} src={NOTESNOOK_LOGO_SVG} />
|
||||
<SvgView width={26} height={26} src={NOTESNOOK_LOGO_SVG} />
|
||||
</View>
|
||||
|
||||
<Heading size={AppFontSize.lg}>Notesnook</Heading>
|
||||
<Heading lineHeight="120%" fontSize="XL">
|
||||
Notesnook
|
||||
</Heading>
|
||||
</View>
|
||||
|
||||
<View
|
||||
|
||||
@@ -38,6 +38,7 @@ import { ColorSection } from "./color-section";
|
||||
import { MenuItem } from "./menu-item";
|
||||
import { PinnedSection } from "./pinned-section";
|
||||
import { SideMenuHeader } from "./side-menu-header";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
|
||||
const pro = {
|
||||
title: strings.upgradePlan(),
|
||||
@@ -71,8 +72,7 @@ export function SideMenuHome() {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
gap: DefaultAppStyles.GAP,
|
||||
paddingTop: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingTop: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<SideMenuHeader />
|
||||
@@ -129,7 +129,8 @@ export function SideMenuHome() {
|
||||
</>
|
||||
)}
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop: Spacing.LEVEL_3
|
||||
}}
|
||||
nestedScrollEnabled={false}
|
||||
/>
|
||||
|
||||
@@ -23,9 +23,15 @@ import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { SideMenuHeader } from "./side-menu-header";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
type SideMenuListEmptyProps = {
|
||||
placeholder: string;
|
||||
placeholderTitle: string;
|
||||
placeholderBody: string;
|
||||
placeholderButtonTitle: string;
|
||||
onPressPlaceholderButton: () => void;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
@@ -76,9 +82,39 @@ export const SideMenuListEmpty = (props: SideMenuListEmptyProps) => {
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<Paragraph size={AppFontSize.xs} color={colors.secondary.paragraph}>
|
||||
{props.placeholder}
|
||||
</Paragraph>
|
||||
<View
|
||||
style={{
|
||||
alignItems: "center",
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<Heading size={AppFontSize.md} color={colors.secondary.paragraph}>
|
||||
{props.placeholderTitle}
|
||||
</Heading>
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: "center",
|
||||
maxWidth: "60%"
|
||||
}}
|
||||
fontSize="SM"
|
||||
color={colors.secondary.paragraph}
|
||||
>
|
||||
{props.placeholderBody}
|
||||
</Paragraph>
|
||||
|
||||
<Button
|
||||
title={props.placeholderButtonTitle}
|
||||
onPress={props.onPressPlaceholderButton}
|
||||
fontSize={AppFontSize.sm}
|
||||
style={{
|
||||
marginTop: Spacing.LEVEL_2,
|
||||
paddingVertical: Spacing.LEVEL_2,
|
||||
paddingHorizontal: Spacing.LEVEL_2
|
||||
}}
|
||||
type="accent-outline"
|
||||
icon="plus"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -41,6 +41,9 @@ import {
|
||||
} from "./stores";
|
||||
import { LegendList } from "@legendapp/list";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { AddNotebookSheet } from "../sheets/add-notebook";
|
||||
import { Spacing } from "../../common/design/spacing";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
useSideMenuNotebookSelectionStore.setState({
|
||||
multiSelect: true
|
||||
});
|
||||
@@ -53,7 +56,7 @@ export const SideMenuNotebooks = () => {
|
||||
const [filteredNotebooks, setFilteredNotebooks] = React.useState(notebooks);
|
||||
const searchTimer = React.useRef<NodeJS.Timeout>(undefined);
|
||||
const lastQuery = React.useRef<string>(undefined);
|
||||
const updater = useRelationStore(state => state.updater);
|
||||
const updater = useRelationStore((state) => state.updater);
|
||||
const loadRootNotebooks = React.useCallback(async () => {
|
||||
if (!filteredNotebooks) return;
|
||||
const _notebooks: Notebook[] = [];
|
||||
@@ -81,7 +84,7 @@ export const SideMenuNotebooks = () => {
|
||||
|
||||
useEffect(() => {
|
||||
updateNotebooks();
|
||||
}, [updateNotebooks,updater]);
|
||||
}, [updateNotebooks, updater]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -136,7 +139,12 @@ export const SideMenuNotebooks = () => {
|
||||
>
|
||||
{!notebooks || notebooks.placeholders.length === 0 ? (
|
||||
<SideMenuListEmpty
|
||||
placeholder={strings.emptyPlaceholders("notebook")}
|
||||
placeholderTitle={strings.noNotebooksYet()}
|
||||
placeholderBody={strings.notebooksEmptyBody()}
|
||||
placeholderButtonTitle={strings.createNotebook()}
|
||||
onPressPlaceholderButton={() => {
|
||||
AddNotebookSheet.present();
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
) : (
|
||||
@@ -151,7 +159,8 @@ export const SideMenuNotebooks = () => {
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingTop: Spacing.LEVEL_1,
|
||||
paddingBottom: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<SideMenuHeader />
|
||||
@@ -159,34 +168,44 @@ export const SideMenuNotebooks = () => {
|
||||
}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.primary.background,
|
||||
borderTopColor: colors.primary.border,
|
||||
borderTopWidth: 1,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingHorizontal: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="Filter notebooks..."
|
||||
<View
|
||||
style={{
|
||||
fontFamily: "Inter-Regular",
|
||||
fontSize: AppFontSize.xs,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
borderTopColor: colors.primary.border,
|
||||
borderTopWidth: 1,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
cursorColor={colors.primary.accent}
|
||||
onChangeText={async (value: string) => {
|
||||
searchTimer.current && clearTimeout(searchTimer.current);
|
||||
searchTimer.current = setTimeout(async () => {
|
||||
lastQuery.current = value;
|
||||
updateNotebooks();
|
||||
}, 500);
|
||||
}}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
/>
|
||||
>
|
||||
<TextInput
|
||||
placeholder={strings.filterNotebooks()}
|
||||
style={{
|
||||
fontFamily: "Inter-Regular",
|
||||
fontSize: AppFontSize.xs,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
}}
|
||||
cursorColor={colors.primary.accent}
|
||||
onChangeText={async (value: string) => {
|
||||
searchTimer.current && clearTimeout(searchTimer.current);
|
||||
searchTimer.current = setTimeout(async () => {
|
||||
lastQuery.current = value;
|
||||
updateNotebooks();
|
||||
}, 500);
|
||||
}}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
/>
|
||||
|
||||
<AppIcon name="funnel" size={14} iconFamily="notesnook" />
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
@@ -235,8 +254,7 @@ const NotebookItemWrapper = React.memo(
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop: index === 0 ? DefaultAppStyles.GAP_VERTICAL : 0
|
||||
paddingHorizontal: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<NotebookItem
|
||||
|
||||
@@ -25,10 +25,12 @@ import { TextInput, View } from "react-native";
|
||||
import { DatabaseLogger, db } from "../../common/database";
|
||||
import { useDBItem, useTotalNotes } from "../../hooks/use-db-item";
|
||||
import { TaggedNotes } from "../../screens/notes/tagged";
|
||||
import { presentDialog } from "../dialog/functions";
|
||||
import Navigation from "../../services/navigation";
|
||||
import { ToastManager } from "../../services/event-manager";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useTags } from "../../stores/use-tag-store";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||
import { useTags, useTagStore } from "../../stores/use-tag-store";
|
||||
import { AppFontSize } from "../../utils/size";
|
||||
import { DefaultAppStyles } from "../../utils/styles";
|
||||
import { Properties } from "../properties";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
@@ -39,6 +41,7 @@ import { SideMenuListEmpty } from "./side-menu-list-empty";
|
||||
import { useSideMenuTagsSelectionStore } from "./stores";
|
||||
import { LegendList, LegendListRenderItemProps } from "@legendapp/list";
|
||||
import { useRelationStore } from "../../stores/use-relation-store";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
|
||||
const TagItem = (props: {
|
||||
tags: VirtualizedGrouping<Tag>;
|
||||
@@ -67,11 +70,8 @@ const TagItem = (props: {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
minHeight: 35,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
marginTop:
|
||||
(props.id as number) === 0 ? DefaultAppStyles.GAP_VERTICAL : 2
|
||||
paddingHorizontal: Spacing.LEVEL_3,
|
||||
marginBottom: Spacing.LEVEL_0
|
||||
}}
|
||||
>
|
||||
{item ? (
|
||||
@@ -118,31 +118,24 @@ const TagItem = (props: {
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
borderRadius: defaultBorderRadius,
|
||||
paddingRight: DefaultAppStyles.GAP_SMALL
|
||||
borderRadius: Radius.XS,
|
||||
padding: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1
|
||||
flexShrink: 1,
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
size={AppFontSize.md}
|
||||
color={isFocused ? colors.selected.icon : colors.primary.icon}
|
||||
name="pound"
|
||||
/>
|
||||
</View>
|
||||
<AppIcon
|
||||
size={16}
|
||||
color={isFocused ? colors.selected.icon : colors.primary.icon}
|
||||
name="shopping-mode"
|
||||
iconFamily="notesnook"
|
||||
/>
|
||||
|
||||
<Paragraph
|
||||
color={
|
||||
@@ -175,9 +168,9 @@ const TagItem = (props: {
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{item?.id && totalNotes.totalNotes?.(item?.id) ? (
|
||||
{item?.id && totalNotes.totalNotes?.(item?.id) !== undefined ? (
|
||||
<Paragraph
|
||||
size={AppFontSize.xxs}
|
||||
size={AppFontSize.sm}
|
||||
color={colors.secondary.paragraph}
|
||||
style={{
|
||||
marginLeft: DefaultAppStyles.GAP_SMALL
|
||||
@@ -241,6 +234,32 @@ export const SideMenuTags = () => {
|
||||
setLoading(false);
|
||||
}, [tags]);
|
||||
|
||||
const onPressAddTag = React.useCallback(() => {
|
||||
presentDialog({
|
||||
title: strings.addTag(),
|
||||
// paragraph: strings.addTagDesc(),
|
||||
input: true,
|
||||
inputLabel: "Enter title",
|
||||
inputPlaceholder: "eg. journal",
|
||||
positiveText: strings.add(),
|
||||
positivePress: async (tag) => {
|
||||
if (tag) {
|
||||
await db.tags.add({
|
||||
title: tag
|
||||
});
|
||||
useTagStore.getState().refresh();
|
||||
return true;
|
||||
}
|
||||
ToastManager.show({
|
||||
context: "local",
|
||||
type: "error",
|
||||
message: strings.allFieldsRequired()
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
updateTags();
|
||||
@@ -262,7 +281,10 @@ export const SideMenuTags = () => {
|
||||
>
|
||||
{!tags || tags?.placeholders.length === 0 ? (
|
||||
<SideMenuListEmpty
|
||||
placeholder={strings.emptyPlaceholders("tag")}
|
||||
placeholderTitle={strings.noTagsYet()}
|
||||
placeholderBody={strings.tagsEmptyBody()}
|
||||
placeholderButtonTitle={strings.addTag()}
|
||||
onPressPlaceholderButton={onPressAddTag}
|
||||
isLoading={loading}
|
||||
/>
|
||||
) : (
|
||||
@@ -278,7 +300,8 @@ export const SideMenuTags = () => {
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.primary.background,
|
||||
paddingTop: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingTop: Spacing.LEVEL_1,
|
||||
paddingBottom: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<SideMenuHeader />
|
||||
@@ -288,36 +311,45 @@ export const SideMenuTags = () => {
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingHorizontal: DefaultAppStyles.GAP,
|
||||
backgroundColor: colors.primary.background,
|
||||
borderTopColor: colors.primary.border,
|
||||
borderTopWidth: 1,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL
|
||||
paddingHorizontal: Spacing.LEVEL_3
|
||||
}}
|
||||
>
|
||||
<TextInput
|
||||
placeholder="Filter tags..."
|
||||
<View
|
||||
style={{
|
||||
fontFamily: "Inter-Regular",
|
||||
fontSize: AppFontSize.xs,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
width: "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
borderTopColor: colors.primary.border,
|
||||
borderTopWidth: 1,
|
||||
paddingVertical: DefaultAppStyles.GAP_VERTICAL,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
cursorColor={colors.primary.accent}
|
||||
onChangeText={async (value) => {
|
||||
searchTimer.current && clearTimeout(searchTimer.current);
|
||||
searchTimer.current = setTimeout(async () => {
|
||||
try {
|
||||
lastQuery.current = value;
|
||||
updateTags();
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
}
|
||||
}, 100);
|
||||
}}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
/>
|
||||
>
|
||||
<TextInput
|
||||
placeholder={strings.filterTags()}
|
||||
style={{
|
||||
fontFamily: "Inter-Regular",
|
||||
fontSize: AppFontSize.xs,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0
|
||||
}}
|
||||
cursorColor={colors.primary.accent}
|
||||
onChangeText={async (value) => {
|
||||
searchTimer.current && clearTimeout(searchTimer.current);
|
||||
searchTimer.current = setTimeout(async () => {
|
||||
try {
|
||||
lastQuery.current = value;
|
||||
updateTags();
|
||||
} catch (e) {
|
||||
DatabaseLogger.error(e);
|
||||
}
|
||||
}, 100);
|
||||
}}
|
||||
placeholderTextColor={colors.primary.placeholder}
|
||||
/>
|
||||
|
||||
<AppIcon name="funnel" size={14} iconFamily="notesnook" />
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
|
||||
111
apps/mobile/app/components/side-menu/tab-bar-button.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { Pressable } from "../ui/pressable";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import Animated, {
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withSpring
|
||||
} from "react-native-reanimated";
|
||||
import { Radius, Spacing } from "../../common/design/spacing";
|
||||
import AppIcon from "../ui/AppIcon";
|
||||
|
||||
type TabBarButtonProps = {
|
||||
icon: string;
|
||||
label?: string;
|
||||
onPress: () => void;
|
||||
isActive?: boolean;
|
||||
testID?: string;
|
||||
};
|
||||
|
||||
export const TabBarButton = ({
|
||||
icon,
|
||||
label,
|
||||
onPress,
|
||||
isActive = false,
|
||||
testID
|
||||
}: TabBarButtonProps) => {
|
||||
const { colors } = useThemeColors();
|
||||
const scale = useSharedValue(1);
|
||||
|
||||
const animatedIconStyle = useAnimatedStyle(() => ({
|
||||
transform: [{ scale: scale.value }]
|
||||
}));
|
||||
|
||||
const handlePress = () => {
|
||||
scale.value = withSpring(0.85, {
|
||||
damping: 100,
|
||||
mass: 1,
|
||||
overshootClamping: false
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
scale.value = withSpring(1, {
|
||||
damping: 100,
|
||||
mass: 1,
|
||||
overshootClamping: false
|
||||
});
|
||||
}, 100);
|
||||
|
||||
onPress();
|
||||
};
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
testID={testID}
|
||||
onPress={handlePress}
|
||||
style={{
|
||||
borderRadius: 10,
|
||||
paddingVertical: 2,
|
||||
width: undefined,
|
||||
gap: label ? Spacing.LEVEL_1 : 0,
|
||||
backgroundColor: "transparent",
|
||||
borderWidth: 0
|
||||
}}
|
||||
type={"plain"}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
{
|
||||
backgroundColor: isActive ? colors.primary.shade : undefined,
|
||||
borderRadius: Radius.XS,
|
||||
padding: Spacing.LEVEL_1
|
||||
},
|
||||
animatedIconStyle
|
||||
]}
|
||||
>
|
||||
<AppIcon
|
||||
name={icon}
|
||||
iconFamily="notesnook"
|
||||
color={isActive ? colors.selected.icon : colors.primary.icon}
|
||||
size={16}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
||||
{label && (
|
||||
<Paragraph fontFamily={isActive ? "MEDIUM" : "REGULAR"} fontSize={"XS"}>
|
||||
{label}
|
||||
</Paragraph>
|
||||
)}
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
@@ -22,6 +22,10 @@ import { ColorValue, TextProps } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import EvilIcon from "react-native-vector-icons/EvilIcons";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { createNanoIconSet } from "react-native-nano-icons";
|
||||
import glyphMap from "../../../../fonts/notesnook-icons.glyphmap.json";
|
||||
|
||||
const NotesnookIcon = createNanoIconSet(glyphMap);
|
||||
|
||||
export interface IconProps extends TextProps {
|
||||
/**
|
||||
@@ -43,9 +47,9 @@ export interface IconProps extends TextProps {
|
||||
* Color of the icon
|
||||
*
|
||||
*/
|
||||
color?: ColorValue | number | undefined;
|
||||
color?: ColorValue | ColorValue[] | number | undefined;
|
||||
|
||||
iconFamily?: "evilicons" | "material";
|
||||
iconFamily?: "evilicons" | "material" | "notesnook";
|
||||
}
|
||||
|
||||
export default function AppIcon({
|
||||
@@ -59,6 +63,12 @@ export default function AppIcon({
|
||||
color={colors.primary.icon}
|
||||
{...(props as any)}
|
||||
/>
|
||||
) : iconFamily === "notesnook" ? (
|
||||
<NotesnookIcon
|
||||
size={AppFontSize.md}
|
||||
color={colors.primary.icon}
|
||||
{...(props as any)}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
size={AppFontSize.md}
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
ViewStyle,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
@@ -35,6 +34,7 @@ import Heading from "../typography/heading";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
import { FontFamily } from "../../../common/design/font";
|
||||
import AppIcon, { IconProps } from "../AppIcon";
|
||||
export interface ButtonProps extends PressableProps {
|
||||
height?: number;
|
||||
icon?: string;
|
||||
@@ -57,6 +57,7 @@ export interface ButtonProps extends PressableProps {
|
||||
bold?: boolean;
|
||||
iconColor?: ColorValue;
|
||||
iconStyle?: TextStyle;
|
||||
iconFamily?: IconProps["iconFamily"];
|
||||
proTag?: boolean;
|
||||
allowFontScaling?: boolean;
|
||||
}
|
||||
@@ -84,6 +85,7 @@ export const Button = ({
|
||||
fwdRef,
|
||||
proTag,
|
||||
iconStyle,
|
||||
iconFamily,
|
||||
allowFontScaling = true,
|
||||
...restProps
|
||||
}: ButtonProps) => {
|
||||
@@ -142,8 +144,9 @@ export const Button = ({
|
||||
<ActivityIndicator color={textColor} size={fontSize + 4} />
|
||||
) : null}
|
||||
{icon && !loading && iconPosition === "left" ? (
|
||||
<Icon
|
||||
<AppIcon
|
||||
name={icon}
|
||||
iconFamily={iconFamily}
|
||||
allowFontScaling={allowFontScaling}
|
||||
style={[{ marginRight: 0 }, iconStyle as any]}
|
||||
color={iconColor || buttonType?.text || textColor}
|
||||
@@ -177,8 +180,9 @@ export const Button = ({
|
||||
)}
|
||||
|
||||
{icon && !loading && iconPosition === "right" ? (
|
||||
<Icon
|
||||
<AppIcon
|
||||
name={icon}
|
||||
iconFamily={iconFamily}
|
||||
allowFontScaling
|
||||
style={[iconStyle as any]}
|
||||
color={iconColor || buttonType?.text || textColor}
|
||||
|
||||
@@ -25,10 +25,10 @@ import {
|
||||
TextStyle,
|
||||
useWindowDimensions
|
||||
} from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { hexToRGBA, RGB_Linear_Shade } from "../../../utils/colors";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
import AppIcon, { IconProps } from "../AppIcon";
|
||||
import { Pressable, PressableProps } from "../pressable";
|
||||
export interface IconButtonProps extends PressableProps {
|
||||
name: string;
|
||||
@@ -42,6 +42,8 @@ export interface IconButtonProps extends PressableProps {
|
||||
tooltipText?: string;
|
||||
tooltipPosition?: number;
|
||||
iconStyle?: TextStyle;
|
||||
iconProps?: IconProps;
|
||||
iconFamily?: IconProps["iconFamily"];
|
||||
}
|
||||
|
||||
export const IconButton = ({
|
||||
@@ -59,7 +61,9 @@ export const IconButton = ({
|
||||
tooltipText,
|
||||
type = "plain",
|
||||
fwdRef,
|
||||
iconProps,
|
||||
tooltipPosition = NativeTooltip.POSITIONS.TOP,
|
||||
iconFamily,
|
||||
...restProps
|
||||
}: IconButtonProps) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -100,8 +104,10 @@ export const IconButton = ({
|
||||
...style
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
<AppIcon
|
||||
{...iconProps}
|
||||
name={name}
|
||||
iconFamily={iconFamily}
|
||||
style={iconStyle as any}
|
||||
allowFontScaling
|
||||
color={
|
||||
|
||||
@@ -17,9 +17,12 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { RefObject, useState } from "react";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import phone from "phone";
|
||||
import React, { RefObject, useRef, useState } from "react";
|
||||
import {
|
||||
ColorValue,
|
||||
findNodeHandle,
|
||||
NativeSyntheticEvent,
|
||||
TextInput,
|
||||
TextInputProps,
|
||||
@@ -28,22 +31,17 @@ import {
|
||||
View,
|
||||
ViewStyle
|
||||
} from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import isURL from "validator/lib/isURL";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
import {
|
||||
ERRORS_LIST,
|
||||
validateEmail,
|
||||
validatePass,
|
||||
validateUsername
|
||||
} from "../../../services/validation";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { getElevationStyle } from "../../../utils/elevation";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
|
||||
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
|
||||
import { IconButton } from "../icon-button";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
import phone from "phone";
|
||||
import isURL from "validator/lib/isURL";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
import { useInputError } from "./input-error-context";
|
||||
|
||||
interface InputProps extends TextInputProps {
|
||||
fwdRef?: RefObject<TextInput | null>;
|
||||
@@ -113,6 +111,9 @@ const Input = ({
|
||||
...restProps
|
||||
}: InputProps) => {
|
||||
const { colors, isDark } = useThemeColors();
|
||||
const errorCtx = useInputError();
|
||||
const internalRef = useRef<TextInput>(null);
|
||||
const activeRef = fwdRef ?? internalRef;
|
||||
const [error, setError] = useState(false);
|
||||
const [focus, setFocus] = useState(false);
|
||||
const [secureEntry, setSecureEntry] = useState(true);
|
||||
@@ -121,6 +122,12 @@ const Input = ({
|
||||
SHORT_PASS: false
|
||||
});
|
||||
type ErrorKey = keyof typeof errorList;
|
||||
|
||||
const reportError = (message: string | null) => {
|
||||
if (!errorCtx) return;
|
||||
const nativeId = findNodeHandle(activeRef.current);
|
||||
if (nativeId !== null) errorCtx.setError(nativeId, message);
|
||||
};
|
||||
const color = error
|
||||
? colors.error.border
|
||||
: focus
|
||||
@@ -137,34 +144,30 @@ const Input = ({
|
||||
});
|
||||
return;
|
||||
}
|
||||
let isError:
|
||||
| boolean
|
||||
| string
|
||||
| { SHORT_PASS?: boolean; isValid?: boolean }
|
||||
| undefined = undefined;
|
||||
let isValid: boolean | string | undefined = undefined;
|
||||
|
||||
switch (validationType) {
|
||||
case "password":
|
||||
isError = validatePass(value);
|
||||
isValid = validatePass(value);
|
||||
break;
|
||||
case "email":
|
||||
isError = validateEmail(value);
|
||||
isValid = validateEmail(value);
|
||||
break;
|
||||
case "username":
|
||||
isError = validateUsername(value);
|
||||
isValid = validateUsername(value);
|
||||
break;
|
||||
case "confirmPassword":
|
||||
isError = customValidator && value === customValidator();
|
||||
isValid = customValidator && value === customValidator();
|
||||
break;
|
||||
case "url":
|
||||
isError = isURL(value, { allow_underscores: true });
|
||||
isValid = isURL(value, { allow_underscores: true });
|
||||
break;
|
||||
case "phonenumber": {
|
||||
const result = phone(value, {
|
||||
strictDetection: true,
|
||||
validateMobilePrefix: true
|
||||
});
|
||||
isError = result.isValid;
|
||||
isValid = result.isValid;
|
||||
if (result.isValid) {
|
||||
onChangeText && onChangeText(result.phoneNumber);
|
||||
}
|
||||
@@ -173,23 +176,10 @@ const Input = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (validationType === "password") {
|
||||
let hasError = false;
|
||||
|
||||
const errors = isError as { [name: string]: boolean };
|
||||
Object.keys(errors).forEach((e) => {
|
||||
//ts-ignore
|
||||
if (errors[e] === true) {
|
||||
hasError = true;
|
||||
}
|
||||
});
|
||||
setError(hasError);
|
||||
onErrorCheck && onErrorCheck(hasError);
|
||||
setErrorList(errors as { SHORT_PASS: boolean });
|
||||
} else {
|
||||
setError(!isError);
|
||||
onErrorCheck && onErrorCheck(!isError);
|
||||
}
|
||||
const hasError = !isValid;
|
||||
setError(hasError);
|
||||
onErrorCheck && onErrorCheck(hasError);
|
||||
reportError(hasError ? (errorMessage ?? null) : null);
|
||||
};
|
||||
|
||||
const onChange = (value: string) => {
|
||||
@@ -201,6 +191,7 @@ const Input = ({
|
||||
setErrorList({
|
||||
SHORT_PASS: false
|
||||
});
|
||||
reportError(null);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -221,7 +212,7 @@ const Input = ({
|
||||
const style: ViewStyle = {
|
||||
borderWidth: 1,
|
||||
borderRadius: defaultBorderRadius,
|
||||
borderColor: color,
|
||||
borderColor: error ? colors.static.red : color,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
@@ -277,10 +268,8 @@ const Input = ({
|
||||
|
||||
<TextInput
|
||||
{...restProps}
|
||||
ref={fwdRef}
|
||||
onLayout={(e) => {
|
||||
console.log(e.nativeEvent.layout);
|
||||
}}
|
||||
ref={activeRef}
|
||||
onLayout={restProps.onLayout}
|
||||
editable={!loading && restProps.editable}
|
||||
onChangeText={onChange}
|
||||
onBlur={onBlur}
|
||||
@@ -343,86 +332,9 @@ const Input = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<IconButton
|
||||
name="alert-circle-outline"
|
||||
top={10}
|
||||
bottom={10}
|
||||
onPress={() => {
|
||||
setShowError(!showError);
|
||||
}}
|
||||
size={20}
|
||||
style={{
|
||||
width: 25,
|
||||
marginLeft: 5
|
||||
}}
|
||||
color={colors.error.icon}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{error && showError && errorMessage ? (
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
backgroundColor: colors.secondary.background,
|
||||
paddingVertical: 3,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL / 2,
|
||||
borderRadius: 2.5,
|
||||
...getElevationStyle(2),
|
||||
top: 0
|
||||
}}
|
||||
>
|
||||
<Paragraph
|
||||
size={AppFontSize.xs}
|
||||
style={{
|
||||
textAlign: "right",
|
||||
textAlignVertical: "bottom"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="alert-circle-outline"
|
||||
size={AppFontSize.xs}
|
||||
color={colors.error.icon}
|
||||
/>{" "}
|
||||
{errorMessage}
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{validationType === "password" &&
|
||||
focus &&
|
||||
Object.keys(errorList).filter((k) => errorList[k as ErrorKey] === true)
|
||||
.length !== 0 ? (
|
||||
<View
|
||||
style={{
|
||||
marginTop: -5,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
{Object.keys(ERRORS_LIST).map((error) => (
|
||||
<View
|
||||
key={ERRORS_LIST[error as ErrorKey]}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={errorList[error as ErrorKey] ? "close" : "check"}
|
||||
color={errorList[error as ErrorKey] ? "red" : "green"}
|
||||
/>
|
||||
|
||||
<Paragraph style={{ marginLeft: 5 }} fontSize="XS">
|
||||
{ERRORS_LIST[error as ErrorKey]}
|
||||
</Paragraph>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
120
apps/mobile/app/components/ui/input/input-error-context.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, {
|
||||
createContext,
|
||||
RefObject,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { findNodeHandle, TextInput, View } from "react-native";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
import AppIcon from "../AppIcon";
|
||||
|
||||
interface InputErrorContextType {
|
||||
setError: (nativeId: number, message: string | null) => void;
|
||||
getError: (nativeId: number) => string | null;
|
||||
}
|
||||
|
||||
const InputErrorContext = createContext<InputErrorContextType | null>(null);
|
||||
|
||||
export function InputErrorProvider({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [errors, setErrors] = useState<Map<number, string | null>>(new Map());
|
||||
|
||||
const setError = useCallback((nativeId: number, message: string | null) => {
|
||||
setErrors((prev) => {
|
||||
const next = new Map(prev);
|
||||
if (message === null) {
|
||||
next.delete(nativeId);
|
||||
} else {
|
||||
next.set(nativeId, message);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getError = useCallback(
|
||||
(nativeId: number) => errors.get(nativeId) ?? null,
|
||||
[errors]
|
||||
);
|
||||
|
||||
const value = useMemo(() => ({ setError, getError }), [setError, getError]);
|
||||
|
||||
return (
|
||||
<InputErrorContext.Provider value={value}>
|
||||
{children}
|
||||
</InputErrorContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useInputError() {
|
||||
return useContext(InputErrorContext);
|
||||
}
|
||||
|
||||
interface ErrorContainerProps {
|
||||
inputRef: RefObject<TextInput | null>;
|
||||
}
|
||||
|
||||
export function ErrorContainer({ inputRef }: ErrorContainerProps) {
|
||||
const ctx = useInputError();
|
||||
const { colors } = useThemeColors();
|
||||
const nativeIdRef = useRef<number | null>(null);
|
||||
|
||||
if (!ctx) return null;
|
||||
|
||||
// Resolve the native ID lazily — the ref's current may not be set on first
|
||||
// render but will be by the time the input mounts and registers an error.
|
||||
const nativeId =
|
||||
nativeIdRef.current ??
|
||||
(inputRef.current
|
||||
? (nativeIdRef.current = findNodeHandle(inputRef.current))
|
||||
: null);
|
||||
|
||||
const message = nativeId !== null ? ctx.getError(nativeId) : null;
|
||||
|
||||
if (!message) return null;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
gap: Spacing.LEVEL_1
|
||||
}}
|
||||
>
|
||||
<AppIcon
|
||||
name="warning-circle"
|
||||
color={colors.static.red}
|
||||
iconFamily="notesnook"
|
||||
size={16}
|
||||
/>
|
||||
<Paragraph color={colors.static.red} fontSize="SM">
|
||||
{message}
|
||||
</Paragraph>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -50,6 +50,8 @@ export interface PressableProps extends RNPressableProps {
|
||||
|
||||
type ButtonTypes =
|
||||
| "plain"
|
||||
| "plain-outline"
|
||||
| "accent-background"
|
||||
| "transparent"
|
||||
| "accent"
|
||||
| "shade"
|
||||
@@ -61,7 +63,8 @@ type ButtonTypes =
|
||||
| "error"
|
||||
| "errorShade"
|
||||
| "warn"
|
||||
| "selected";
|
||||
| "selected"
|
||||
| "accent-outline";
|
||||
|
||||
type ButtonVariant = {
|
||||
primary: string;
|
||||
@@ -92,6 +95,14 @@ const buttonTypes = (
|
||||
isDark
|
||||
)
|
||||
},
|
||||
"plain-outline": {
|
||||
primary: "transparent",
|
||||
text: colors.primary.paragraph,
|
||||
selected: colors.primary.hover,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.primary.border,
|
||||
borderSelectedColor: colors.primary.border
|
||||
},
|
||||
transparent: {
|
||||
primary: "transparent",
|
||||
text: colors.primary.accent,
|
||||
@@ -167,6 +178,20 @@ const buttonTypes = (
|
||||
false
|
||||
)
|
||||
},
|
||||
"accent-background": {
|
||||
primary: colors.primary.background,
|
||||
text: colors.primary.accent,
|
||||
selected: colors.primary.background,
|
||||
borderWidth: 0
|
||||
},
|
||||
"accent-outline": {
|
||||
primary: "transparent",
|
||||
text: colors.primary.accent,
|
||||
selected: accent || colors.primary.accent,
|
||||
borderWidth: 1,
|
||||
borderColor: accent || colors.primary.accent,
|
||||
borderSelectedColor: accent || colors.primary.accent
|
||||
},
|
||||
inverted: {
|
||||
primary: colors.primary.background,
|
||||
text: colors.primary.accent,
|
||||
|
||||
@@ -19,13 +19,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { isReminderActive } from "@notesnook/core";
|
||||
import React from "react";
|
||||
import { ViewStyle } from "react-native";
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
|
||||
import { AppFontSize } from "../../../utils/size";
|
||||
import { Button, ButtonProps } from "../button";
|
||||
import { getFormattedReminderTime } from "@notesnook/common";
|
||||
import { Reminder } from "@notesnook/core";
|
||||
import { DefaultAppStyles } from "../../../utils/styles";
|
||||
import { Radius } from "../../../common/design/spacing";
|
||||
|
||||
export const ReminderTime = ({
|
||||
checkIsActive = true,
|
||||
@@ -55,8 +55,9 @@ export const ReminderTime = ({
|
||||
title={time}
|
||||
key={reminder.id}
|
||||
icon="bell"
|
||||
iconFamily="notesnook"
|
||||
fontSize={AppFontSize.xs}
|
||||
iconSize={AppFontSize.sm}
|
||||
iconSize={12}
|
||||
type="secondary"
|
||||
buttonType={
|
||||
isTodayOrTomorrow
|
||||
@@ -70,7 +71,7 @@ export const ReminderTime = ({
|
||||
}}
|
||||
style={{
|
||||
height: "auto",
|
||||
borderRadius: defaultBorderRadius,
|
||||
borderRadius: Radius.XXS,
|
||||
borderColor: colors.primary.border,
|
||||
paddingHorizontal: DefaultAppStyles.GAP_SMALL,
|
||||
...(style as ViewStyle)
|
||||
|
||||
@@ -17,41 +17,23 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { IconButton, IconButtonProps } from "../ui/icon-button";
|
||||
import { View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
|
||||
export const RightMenus = ({
|
||||
rightButton
|
||||
}: {
|
||||
rightButton?: IconButtonProps;
|
||||
}) => {
|
||||
const LineSeparator = ({ padding }: { padding: keyof typeof Spacing }) => {
|
||||
const { colors } = useThemeColors();
|
||||
|
||||
return (
|
||||
<View style={styles.rightBtnContainer}>
|
||||
{rightButton ? (
|
||||
<IconButton {...rightButton} color={colors.primary.icon} />
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 1,
|
||||
backgroundColor: colors.primary.border
|
||||
// marginVertical: Spacing[padding]
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
rightBtnContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
},
|
||||
rightBtn: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}
|
||||
});
|
||||
export default LineSeparator;
|
||||
@@ -27,6 +27,7 @@ import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { getContainerBorder } from "../../../utils/colors";
|
||||
import { NotesnookModule } from "../../../utils/notesnook-module";
|
||||
import { Toast } from "../../toast";
|
||||
import { Spacing } from "../../../common/design/spacing";
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -68,8 +69,9 @@ const SheetWrapper = ({
|
||||
width: largeTablet || smallTablet ? width : "100%",
|
||||
backgroundColor: colors.primary.background,
|
||||
zIndex: 10,
|
||||
borderTopRightRadius: 15,
|
||||
borderTopLeftRadius: 15,
|
||||
borderTopRightRadius: 35,
|
||||
paddingTop: Spacing.LEVEL_4,
|
||||
borderTopLeftRadius: 35,
|
||||
alignSelf: "center",
|
||||
borderBottomRightRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
|
||||
@@ -24,7 +24,8 @@ import { AppFontSize } from "../../../utils/size";
|
||||
import {
|
||||
FontFamily,
|
||||
FontSizes,
|
||||
getLineHeight
|
||||
getLineHeight,
|
||||
LineHeightVariants
|
||||
} from "../../../common/design/font";
|
||||
|
||||
interface HeadingProps extends TextProps {
|
||||
@@ -36,7 +37,7 @@ interface HeadingProps extends TextProps {
|
||||
extraBold?: boolean;
|
||||
fontSize?: keyof typeof FontSizes;
|
||||
fontFamily?: keyof typeof FontFamily;
|
||||
lineHeight?: "100%" | "150%";
|
||||
lineHeight?: LineHeightVariants;
|
||||
}
|
||||
|
||||
const extraBoldStyle = {
|
||||
@@ -68,7 +69,7 @@ const Heading = ({
|
||||
color: color || colors.primary.heading,
|
||||
lineHeight:
|
||||
fontSize && lineHeight
|
||||
? getLineHeight(fontSize, lineHeight === "100%" ? 1 : 2)
|
||||
? getLineHeight(fontSize, lineHeight)
|
||||
: undefined
|
||||
},
|
||||
fontFamily
|
||||
|
||||
@@ -24,7 +24,8 @@ import { AppFontSize } from "../../../utils/size";
|
||||
import {
|
||||
FontFamily,
|
||||
FontSizes,
|
||||
getLineHeight
|
||||
getLineHeight,
|
||||
LineHeightVariants
|
||||
} from "../../../common/design/font";
|
||||
|
||||
interface ParagraphProps extends TextProps {
|
||||
@@ -35,7 +36,7 @@ interface ParagraphProps extends TextProps {
|
||||
size?: number;
|
||||
fontSize?: keyof typeof FontSizes;
|
||||
fontFamily?: keyof typeof FontFamily;
|
||||
lineHeight?: "100%" | "150%";
|
||||
lineHeight?: LineHeightVariants;
|
||||
}
|
||||
const Paragraph = ({
|
||||
color,
|
||||
@@ -43,7 +44,7 @@ const Paragraph = ({
|
||||
style,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
lineHeight = "100%",
|
||||
lineHeight,
|
||||
...restProps
|
||||
}: ParagraphProps) => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -59,7 +60,7 @@ const Paragraph = ({
|
||||
fontFamily: fontFamily ? FontFamily[fontFamily] : FontFamily.REGULAR,
|
||||
lineHeight:
|
||||
fontSize && lineHeight
|
||||
? getLineHeight(fontSize, lineHeight === "100%" ? 1 : 2)
|
||||
? getLineHeight(fontSize, lineHeight)
|
||||
: undefined
|
||||
},
|
||||
style
|
||||
|
||||
@@ -30,6 +30,7 @@ import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNotes } from "../../stores/use-notes-store";
|
||||
import { openEditor } from "../notes/common";
|
||||
import LineSeparator from "../../components/ui/seperator/line-separator";
|
||||
|
||||
export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
const [notes, loading] = useNotes();
|
||||
@@ -66,6 +67,8 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
onPressDefaultRightButton={openEditor}
|
||||
/>
|
||||
|
||||
<LineSeparator padding="LEVEL_3" />
|
||||
|
||||
<DelayLayout wait={loading}>
|
||||
<List
|
||||
data={notes}
|
||||
|
||||
@@ -32,18 +32,12 @@ export const ERRORS_LIST = {
|
||||
SHORT_PASS: strings.passTooShort()
|
||||
};
|
||||
|
||||
export function validatePass(password) {
|
||||
let errors = {
|
||||
SHORT_PASS: false
|
||||
};
|
||||
|
||||
if (password?.length < 8) {
|
||||
errors.SHORT_PASS = true;
|
||||
export function validatePass(password, length = 8) {
|
||||
if (password?.length < length) {
|
||||
return true;
|
||||
} else {
|
||||
errors.SHORT_PASS = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function validateUsername(username) {
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ItemType } from "@notesnook/core";
|
||||
import { Item, ItemType } from "@notesnook/core";
|
||||
import { create } from "zustand";
|
||||
|
||||
export interface SelectionStore {
|
||||
@@ -62,3 +62,20 @@ export const useSelectionStore = create<SelectionStore>((set, get) => ({
|
||||
set({ selectionMode: undefined, selectedItemsList: [] });
|
||||
}
|
||||
}));
|
||||
|
||||
export function selectItem(item: Item) {
|
||||
if (useSelectionStore.getState().selectionMode === item.type) {
|
||||
const { selectionMode, clearSelection, setSelectedItem } =
|
||||
useSelectionStore.getState();
|
||||
|
||||
if (selectionMode === item.type) {
|
||||
setSelectedItem(item.id);
|
||||
}
|
||||
|
||||
if (useSelectionStore.getState().selectedItemsList.length === 0) {
|
||||
clearSelection();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ export const STORE_LINK =
|
||||
export const GROUP = {
|
||||
default: "default",
|
||||
none: "none",
|
||||
abc: "abc",
|
||||
year: "year",
|
||||
month: "month",
|
||||
week: "week",
|
||||
month: "month"
|
||||
abc: "abc"
|
||||
};
|
||||
|
||||
export const SORT = {
|
||||
|
||||
@@ -37,28 +37,16 @@ export const MenuItemsList: SideMenuItem[] = [
|
||||
dataType: "note",
|
||||
id: "Notes",
|
||||
title: "Notes",
|
||||
icon: "note-outline",
|
||||
icon: "note",
|
||||
type: "side-menu-item"
|
||||
},
|
||||
// {
|
||||
// dataType: "notebook",
|
||||
// id: "Notebooks",
|
||||
// title: "Notebooks",
|
||||
// icon: "book-outline"
|
||||
// },
|
||||
{
|
||||
dataType: "note",
|
||||
id: "Favorites",
|
||||
title: "Favorites",
|
||||
icon: "star-outline",
|
||||
icon: "star",
|
||||
type: "side-menu-item"
|
||||
},
|
||||
// {
|
||||
// dataType: "tag",
|
||||
// id: "Tags",
|
||||
// title: "Tags",
|
||||
// icon: "pound"
|
||||
// },
|
||||
{
|
||||
dataType: "reminder",
|
||||
id: "Reminders",
|
||||
@@ -70,7 +58,7 @@ export const MenuItemsList: SideMenuItem[] = [
|
||||
dataType: "monograph",
|
||||
id: "Monographs",
|
||||
title: "Monographs",
|
||||
icon: "text-box-multiple-outline",
|
||||
icon: "book-open",
|
||||
onPress: () => {
|
||||
Navigation.closeDrawer();
|
||||
Monographs.navigate();
|
||||
@@ -88,7 +76,7 @@ export const MenuItemsList: SideMenuItem[] = [
|
||||
dataType: "note",
|
||||
id: "Trash",
|
||||
title: "Trash",
|
||||
icon: "delete-outline",
|
||||
icon: "trash",
|
||||
type: "side-menu-item"
|
||||
}
|
||||
];
|
||||
|
||||
1
apps/mobile/fonts/notesnook-icons.glyphmap.json
Normal file
@@ -0,0 +1 @@
|
||||
{"m":{"f":"notesnook-icons","u":1024,"z":1020,"s":59648,"h":"83a1ac81fdaaf076cb52c3ad793c668b4be2ce48e94af989b74c69660096843b"},"i":{"archive":[1024,[[59648,"#666666"]]],"bell":[1024,[[59649,"#666666"]]],"book-open":[1024,[[59650,"#666666"]]],"bookmark":[1024,[[59651,"#666666"]]],"box-empty":[1024,[[59652,"#B0B0B1"]]],"calendar":[1024,[[59653,"rgba(102,102,102,0.8)"]]],"chat":[1024,[[59654,"white"]]],"checkbox":[1024,[[59655,"#008836"],[59656,"white"]]],"checks":[1024,[[59657,"#181818"]]],"chevron-down":[1024,[[59658,"#181818"]]],"chevron-up":[1024,[[59659,"#181818"]]],"close":[1024,[[59660,"#666666"]]],"dark-mode-outline":[1024,[[59661,"currentColor"]]],"dots-three":[1024,[[59662,"#202020"]]],"drive-file-move":[1088,[[59663,"#666666"]]],"envelope-simple":[1024,[[59664,"white"]]],"funnel":[1024,[[59665,"#666666"]]],"home":[1024,[[59666,"#181818"]]],"link":[560,[[59667,"#666666"]]],"lock-simple":[1024,[[59668,"white"]]],"menu":[1024,[[59669,"#181818"]]],"note":[1024,[[59670,"#181818"]]],"pin":[939,[[59671,"#666666"]]],"plus":[1024,[[59672,"#666666"]]],"search":[1024,[[59673,"#181818"]]],"shield-check":[1024,[[59674,"#008836"]]],"shopping-mode":[1024,[[59675,"#666666"]]],"sliders":[1024,[[59676,"#181818"]]],"sort-ascending":[1024,[[59677,"#666666"]]],"sort-descending":[1024,[[59678,"currentColor"]]],"star-filled":[1024,[[59679,"#E5C131"]]],"star":[1024,[[59680,"#666666"]]],"sun":[1024,[[59681,"#666666"]]],"trash":[1024,[[59682,"#666666"]]],"view-list":[1024,[[59683,"#181818"]]],"warning-circle":[1024,[[59684,"#BB3431"]]]}}
|
||||
BIN
apps/mobile/fonts/notesnook-icons.ttf
Normal file
@@ -1,43 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Add to Notes</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Add to Notes</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>svgs.ttf</string>
|
||||
<string>notesnook-icons.ttf</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -386,6 +386,7 @@
|
||||
240525450DF9ABA0F332B52E /* [CP] Copy Pods Resources */,
|
||||
65A7F34B255687E600699170 /* Embed App Extensions */,
|
||||
48C834D962D612F18A0388B3 /* [CP] Embed Pods Frameworks */,
|
||||
5C222D5F8D0742BFAE9C4245 /* Copy nanoicons fonts */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -723,6 +724,20 @@
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
5C222D5F8D0742BFAE9C4245 /* Copy nanoicons fonts */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy nanoicons fonts";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\n NANOICONS_DIR=\"${PROJECT_DIR}/nanoicons-fonts\"\n if [ -d \"$NANOICONS_DIR\" ]; then\n cp \"$NANOICONS_DIR\"/*.ttf \"${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\" 2>/dev/null || true\n fi\n ";
|
||||
};
|
||||
658D06A625A7446E008C70C0 /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<string>Inter-Bold.ttf</string>
|
||||
<string>MaterialCommunityIcons.ttf</string>
|
||||
<string>EvilIcons.ttf</string>
|
||||
<string>notesnook-icons.ttf</string>
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
|
||||
@@ -2113,6 +2113,34 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-nano-icons (0.1.8):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
- fmt
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly
|
||||
- RCT-Folly/Fabric
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-jsi
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-renderercss
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-netinfo (11.4.1):
|
||||
- React-Core
|
||||
- react-native-notification-sounds (0.5.5):
|
||||
@@ -3591,6 +3619,7 @@ DEPENDENCIES:
|
||||
- react-native-in-app-review (from `../node_modules/react-native-in-app-review`)
|
||||
- "react-native-keep-awake (from `../node_modules/@sayem314/react-native-keep-awake`)"
|
||||
- react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
|
||||
- react-native-nano-icons (from `../node_modules/react-native-nano-icons`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-notification-sounds (from `../node_modules/react-native-notification-sounds`)
|
||||
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
|
||||
@@ -3801,6 +3830,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/@sayem314/react-native-keep-awake"
|
||||
react-native-mmkv-storage:
|
||||
:path: "../node_modules/react-native-mmkv-storage"
|
||||
react-native-nano-icons:
|
||||
:path: "../node_modules/react-native-nano-icons"
|
||||
react-native-netinfo:
|
||||
:path: "../node_modules/@react-native-community/netinfo"
|
||||
react-native-notification-sounds:
|
||||
@@ -4016,6 +4047,7 @@ SPEC CHECKSUMS:
|
||||
react-native-in-app-review: 1516ba69d60d58053b7eb3aaaf8d2a5a74af8b57
|
||||
react-native-keep-awake: a351e6f67006b47f316ae2b17ee8ee69386167f4
|
||||
react-native-mmkv-storage: e84980084e371a9c2230bbde071536a7e1539406
|
||||
react-native-nano-icons: 118c99a62175c3f4737ff21e85252c90158816fb
|
||||
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
|
||||
react-native-notification-sounds: ce106d58df0dd384bccbd2e84fb53accab7cc068
|
||||
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
|
||||
|
||||
BIN
apps/mobile/ios/nanoicons-fonts/notesnook-icons.ttf
Normal file
1109
apps/mobile/package-lock.json
generated
@@ -119,6 +119,7 @@
|
||||
"react-native-material-menu": "^2.0.0",
|
||||
"react-native-mmkv-storage": "^12.0.1",
|
||||
"react-native-modal-datetime-picker": "14.0.0",
|
||||
"react-native-nano-icons": "^0.1.8",
|
||||
"react-native-navigation-bar-color": "2.0.2",
|
||||
"react-native-nitro-cloud-uploader": "^1.0.9",
|
||||
"react-native-nitro-modules": "^0.32.0",
|
||||
|
||||
11
packages/icons/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@notesnook/icons",
|
||||
"version": "2.1.3",
|
||||
"main": "./svgs",
|
||||
"exports": {
|
||||
".": "./svgs",
|
||||
"./*": "./svgs/*"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"sideEffects": false
|
||||
}
|
||||
5
packages/icons/svgs/archive.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 5.33331V14H2V5.33331" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.3334 2H0.666748V5.33333H15.3334V2Z" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.66675 8H9.33341" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 454 B |
4
packages/icons/svgs/bell.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 5.33331C12 4.27245 11.5786 3.25503 10.8284 2.50489C10.0783 1.75474 9.06087 1.33331 8 1.33331C6.93913 1.33331 5.92172 1.75474 5.17157 2.50489C4.42143 3.25503 4 4.27245 4 5.33331C4 9.99998 2 11.3333 2 11.3333H14C14 11.3333 12 9.99998 12 5.33331Z" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9.15335 14C9.03614 14.2021 8.86791 14.3698 8.6655 14.4864C8.46309 14.6029 8.2336 14.6643 8.00001 14.6643C7.76643 14.6643 7.53694 14.6029 7.33453 14.4864C7.13212 14.3698 6.96389 14.2021 6.84668 14" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 740 B |
4
packages/icons/svgs/book-open.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.33325 2H5.33325C6.0405 2 6.71877 2.28095 7.21887 2.78105C7.71897 3.28115 7.99992 3.95942 7.99992 4.66667V14C7.99992 13.4696 7.7892 12.9609 7.41413 12.5858C7.03906 12.2107 6.53035 12 5.99992 12H1.33325V2Z" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.6667 2H10.6667C9.95942 2 9.28115 2.28095 8.78105 2.78105C8.28095 3.28115 8 3.95942 8 4.66667V14C8 13.4696 8.21071 12.9609 8.58579 12.5858C8.96086 12.2107 9.46957 12 10 12H14.6667V2Z" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 688 B |
3
packages/icons/svgs/bookmark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.6665 1.33331C3.56986 1.33331 2.6665 2.23667 2.6665 3.33331V12.6666C2.6665 13.7633 3.56986 14.6666 4.6665 14.6666H13.3332V13.3333H4.6665C4.28982 13.3333 3.99984 13.0433 3.99984 12.6666C3.99984 12.29 4.28982 12 4.6665 12H13.3332V10.6666V1.33331H10.6665H6.6665H4.6665ZM4.6665 2.66665H6.6665V8.08201L7.71598 7.34633L8.6665 6.67836L10.6665 8.08201V2.66665H11.9998V10.6666H4.6665C4.43194 10.6666 4.20976 10.7159 3.99984 10.7916V3.33331C3.99984 2.95663 4.28982 2.66665 4.6665 2.66665ZM7.99984 2.66665H9.33317V5.51821L8.6665 5.05076L7.99984 5.51821V2.66665Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 683 B |
3
packages/icons/svgs/box-empty.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 0.5H12C13.933 0.5 15.5 2.067 15.5 4V12C15.5 13.933 13.933 15.5 12 15.5H4C2.067 15.5 0.5 13.933 0.5 12V4C0.5 2.067 2.067 0.5 4 0.5Z" stroke="#B0B0B1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 266 B |
10
packages/icons/svgs/calendar.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1253_14453)">
|
||||
<path d="M10 1.5H9.5V0.5H8.5V1.5H3.5V0.5H2.5V1.5H2C1.45 1.5 1 1.95 1 2.5V10.5C1 11.05 1.45 11.5 2 11.5H10C10.55 11.5 11 11.05 11 10.5V2.5C11 1.95 10.55 1.5 10 1.5ZM10 10.5H2V5H10V10.5ZM10 4H2V2.5H10V4Z" fill="#666666" fill-opacity="0.8"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1253_14453">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 490 B |
3
packages/icons/svgs/chat.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.5 3H2.50002C2.2348 3 1.98045 3.10536 1.79291 3.29289C1.60537 3.48043 1.50002 3.73478 1.50002 4V14C1.49886 14.1907 1.55281 14.3777 1.65537 14.5384C1.75793 14.6992 1.90473 14.8269 2.07814 14.9062C2.21029 14.9678 2.35425 14.9998 2.50002 15C2.73477 14.9994 2.96174 14.9157 3.14064 14.7638L3.14627 14.7594L5.18752 13H13.5C13.7652 13 14.0196 12.8946 14.2071 12.7071C14.3947 12.5196 14.5 12.2652 14.5 12V4C14.5 3.73478 14.3947 3.48043 14.2071 3.29289C14.0196 3.10536 13.7652 3 13.5 3ZM13.5 12H5.00002C4.87996 12.0001 4.76394 12.0433 4.67314 12.1219L2.50002 14V4H13.5V12Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 696 B |
4
packages/icons/svgs/checkbox.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" rx="4" fill="#008836"/>
|
||||
<path d="M6.69059 11.6351C6.49451 11.8354 6.17216 11.8354 5.97608 11.6351L3.34252 8.94556C3.1522 8.75118 3.1522 8.44031 3.34252 8.24593L3.80941 7.76911C4.00549 7.56886 4.32784 7.56886 4.52392 7.76911L5.97608 9.25217C6.17216 9.45242 6.49451 9.45242 6.69059 9.25217L11.4761 4.36486C11.6722 4.16461 11.9945 4.16461 12.1906 4.36486L12.6575 4.84168C12.8478 5.03605 12.8478 5.34693 12.6575 5.5413L6.69059 11.6351Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 580 B |
3
packages/icons/svgs/checks.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.00586 4.44977C9.07802 4.45044 9.14939 4.46551 9.21582 4.49371C9.28229 4.52196 9.34294 4.56329 9.39355 4.61481C9.44391 4.66617 9.48375 4.72684 9.51074 4.79352C9.53776 4.8604 9.55137 4.93233 9.55078 5.00446C9.55014 5.07672 9.53508 5.14888 9.50684 5.21539C9.47867 5.28159 9.43799 5.34169 9.38672 5.39215L9.37207 5.4068H9.37109L3.78613 10.8922C3.68334 10.9933 3.54457 11.0503 3.40039 11.0504C3.25609 11.0504 3.11752 10.9933 3.01465 10.8922L0.614258 8.53571C0.56277 8.48506 0.522337 8.42446 0.494141 8.35797C0.465942 8.29147 0.450802 8.22024 0.450195 8.14801C0.449594 8.07579 0.463157 8.00403 0.490234 7.93707C0.51731 7.87018 0.556827 7.80885 0.607422 7.75739C0.658014 7.70594 0.718741 7.66547 0.785156 7.63727C0.851556 7.60911 0.922999 7.59397 0.995117 7.59332C1.06726 7.59272 1.13916 7.60635 1.20605 7.63336C1.27302 7.66044 1.33424 7.70088 1.38574 7.75153L3.39941 9.72809L8.61523 4.60699C8.71931 4.50492 8.86007 4.44848 9.00586 4.44977ZM15.0049 4.44977C15.0771 4.45041 15.1484 4.46549 15.2148 4.49371C15.2814 4.52198 15.342 4.56322 15.3926 4.61481C15.4431 4.66627 15.4837 4.72667 15.5107 4.79352C15.5378 4.86056 15.5505 4.93315 15.5498 5.00543C15.5491 5.07757 15.5341 5.14902 15.5059 5.21539C15.4775 5.28177 15.4363 5.34164 15.3848 5.39215L9.78516 10.8922C9.68228 10.9933 9.54371 11.0504 9.39941 11.0504C9.25513 11.0503 9.11654 10.9933 9.01367 10.8922L7.4873 9.39215C7.38328 9.29003 7.32371 9.15119 7.32227 9.00543C7.32091 8.85963 7.37741 8.7189 7.47949 8.61481C7.58169 8.51069 7.7213 8.45112 7.86719 8.44977C8.01301 8.44842 8.15372 8.50487 8.25781 8.60699L9.39941 9.72809L14.6143 4.60699C14.6657 4.5564 14.727 4.51687 14.7939 4.48981C14.8609 4.46272 14.9326 4.44914 15.0049 4.44977Z" fill="#181818" stroke="#181818" stroke-width="0.1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
10
packages/icons/svgs/chevron-down.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1223_13942)">
|
||||
<path d="M0.7 5.1C0.313401 4.7134 0.313402 4.0866 0.7 3.7C1.0866 3.3134 1.7134 3.3134 2.1 3.7L5.29289 6.89289C5.68342 7.28342 6.31658 7.28342 6.70711 6.89289L9.9 3.7C10.2866 3.3134 10.9134 3.3134 11.3 3.7C11.6866 4.0866 11.6866 4.7134 11.3 5.1L6.70711 9.69289C6.31658 10.0834 5.68342 10.0834 5.29289 9.69289L0.7 5.1Z" fill="#181818"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1223_13942">
|
||||
<rect width="12" height="12" fill="white" transform="translate(12) rotate(90)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 623 B |
10
packages/icons/svgs/chevron-up.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1223_13904)">
|
||||
<path d="M0.7 6.9C0.313401 7.2866 0.313402 7.9134 0.7 8.3C1.0866 8.6866 1.7134 8.6866 2.1 8.3L5.29289 5.10711C5.68342 4.71658 6.31658 4.71658 6.70711 5.10711L9.9 8.3C10.2866 8.6866 10.9134 8.6866 11.3 8.3C11.6866 7.9134 11.6866 7.2866 11.3 6.9L6.70711 2.30711C6.31658 1.91658 5.68342 1.91658 5.29289 2.30711L0.7 6.9Z" fill="#181818"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1223_13904">
|
||||
<rect width="12" height="12" fill="white" transform="matrix(-4.37114e-08 -1 -1 4.37114e-08 12 12)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 643 B |
4
packages/icons/svgs/close.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 4L4 12" stroke="#666666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 4L12 12" stroke="#666666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 315 B |
1
packages/icons/svgs/dark-mode-outline.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols Light by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="currentColor" d="M12.058 20q-3.333 0-5.667-2.334T4.058 12q0-2.47 1.413-4.535q1.414-2.067 4.01-2.973q.306-.107.536-.056t.381.199t.192.38q.04.233-.063.489q-.194.477-.282.966t-.087 1.03q0 2.667 1.866 4.533t4.534 1.867q.698 0 1.277-.148q.58-.148.988-.24q.218-.04.399.01t.289.176q.118.125.16.308q.04.183-.048.417q-.715 2.45-2.803 4.013T12.058 20m0-1q2.2 0 3.95-1.213t2.55-3.162q-.5.125-1 .2t-1 .075q-3.075 0-5.238-2.162T9.158 7.5q0-.5.075-1t.2-1q-1.95.8-3.163 2.55T5.058 12q0 2.9 2.05 4.95t4.95 2.05m-.25-6.75"/></svg>
|
||||
|
After Width: | Height: | Size: 729 B |
3
packages/icons/svgs/dots-three.svg
Executable file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.1875 9.9375C11.1875 10.1229 11.1325 10.3042 11.0295 10.4583C10.9265 10.6125 10.7801 10.7327 10.6088 10.8036C10.4375 10.8746 10.249 10.8932 10.0671 10.857C9.88525 10.8208 9.7182 10.7315 9.58709 10.6004C9.45598 10.4693 9.36669 10.3023 9.33051 10.1204C9.29434 9.93854 9.31291 9.75004 9.38386 9.57873C9.45482 9.40743 9.57498 9.26101 9.72915 9.158C9.88332 9.05498 10.0646 9 10.25 9C10.4986 9 10.7371 9.09877 10.9129 9.27459C11.0887 9.4504 11.1875 9.68886 11.1875 9.9375ZM15.5625 9C15.3771 9 15.1958 9.05498 15.0417 9.158C14.8875 9.26101 14.7673 9.40743 14.6964 9.57873C14.6254 9.75004 14.6068 9.93854 14.643 10.1204C14.6792 10.3023 14.7685 10.4693 14.8996 10.6004C15.0307 10.7315 15.1977 10.8208 15.3796 10.857C15.5615 10.8932 15.75 10.8746 15.9213 10.8036C16.0926 10.7327 16.239 10.6125 16.342 10.4583C16.445 10.3042 16.5 10.1229 16.5 9.9375C16.5 9.68886 16.4012 9.4504 16.2254 9.27459C16.0496 9.09877 15.8111 9 15.5625 9ZM4.9375 9C4.75208 9 4.57082 9.05498 4.41665 9.158C4.26248 9.26101 4.14232 9.40743 4.07136 9.57873C4.00041 9.75004 3.98184 9.93854 4.01801 10.1204C4.05419 10.3023 4.14348 10.4693 4.27459 10.6004C4.4057 10.7315 4.57275 10.8208 4.7546 10.857C4.93646 10.8932 5.12496 10.8746 5.29627 10.8036C5.46757 10.7327 5.61399 10.6125 5.717 10.4583C5.82002 10.3042 5.875 10.1229 5.875 9.9375C5.875 9.68886 5.77623 9.4504 5.60041 9.27459C5.4246 9.09877 5.18614 9 4.9375 9Z" fill="#202020"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
3
packages/icons/svgs/drive-file-move.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.48213 11.8435L11.3332 9.04349L8.48213 6.24349L7.49046 7.21741L8.6415 8.34784H5.6665V9.73915H8.6415L7.49046 10.8696L8.48213 11.8435ZM2.83317 13.9131C2.44359 13.9131 2.1102 13.7769 1.833 13.5047C1.55534 13.232 1.4165 12.9044 1.4165 12.5218V4.17393C1.4165 3.79132 1.55534 3.4639 1.833 3.19167C2.1102 2.91897 2.44359 2.78262 2.83317 2.78262H7.08317L8.49984 4.17393H14.1665C14.5561 4.17393 14.8897 4.31028 15.1674 4.58297C15.4446 4.8552 15.5832 5.18262 15.5832 5.56523V12.5218C15.5832 12.9044 15.4446 13.232 15.1674 13.5047C14.8897 13.7769 14.5561 13.9131 14.1665 13.9131H2.83317ZM2.83317 4.17393V12.5218H14.1665V5.56523H7.91546L6.4988 4.17393H2.83317Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 781 B |
11
packages/icons/svgs/edit-pencil.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1249_6859)">
|
||||
<path d="M2 14H4.5L11.8733 6.62671L9.37333 4.12671L2 11.5V14ZM3.33333 12.0534L9.37333 6.01338L9.98667 6.62671L3.94667 12.6667H3.33333V12.0534Z" fill="#181818"/>
|
||||
<path d="M12.2467 2.19329C11.9867 1.93329 11.5667 1.93329 11.3067 2.19329L10.0867 3.41329L12.5867 5.91329L13.8067 4.69329C14.0667 4.43329 14.0667 4.01329 13.8067 3.75329L12.2467 2.19329Z" fill="#181818"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1249_6859">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 615 B |
3
packages/icons/svgs/envelope-simple.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 3H2C1.86739 3 1.74021 3.05268 1.64645 3.14645C1.55268 3.24021 1.5 3.36739 1.5 3.5V12C1.5 12.2652 1.60536 12.5196 1.79289 12.7071C1.98043 12.8946 2.23478 13 2.5 13H13.5C13.7652 13 14.0196 12.8946 14.2071 12.7071C14.3946 12.5196 14.5 12.2652 14.5 12V3.5C14.5 3.36739 14.4473 3.24021 14.3536 3.14645C14.2598 3.05268 14.1326 3 14 3ZM12.7144 4L8 8.32187L3.28562 4H12.7144ZM13.5 12H2.5V4.63688L7.66187 9.36875C7.75412 9.45343 7.87478 9.50041 8 9.50041C8.12522 9.50041 8.24588 9.45343 8.33813 9.36875L13.5 4.63688V12Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 643 B |
3
packages/icons/svgs/funnel.svg
Executable file
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.6112 2.70869C12.5438 2.55311 12.4322 2.42075 12.2903 2.32809C12.1483 2.23543 11.9823 2.18654 11.8127 2.18751H2.18773C2.01838 2.18785 1.85277 2.23732 1.71098 2.32992C1.56919 2.42253 1.45732 2.55429 1.38895 2.70922C1.32057 2.86416 1.29862 3.0356 1.32577 3.20276C1.35291 3.36992 1.42798 3.52562 1.54187 3.65095L1.54625 3.65587L5.25023 7.61087V11.8125C5.25019 11.9709 5.29313 12.1263 5.37448 12.2622C5.45582 12.398 5.57251 12.5093 5.71211 12.5841C5.85171 12.6588 6.00899 12.6943 6.16717 12.6867C6.32535 12.6791 6.47851 12.6287 6.61031 12.541L8.36031 11.3739C8.48027 11.294 8.57864 11.1857 8.64666 11.0586C8.71469 10.9315 8.75026 10.7896 8.75023 10.6455V7.61087L12.4548 3.65587L12.4591 3.65095C12.5742 3.52619 12.65 3.37029 12.6771 3.20272C12.7041 3.03514 12.6812 2.86332 12.6112 2.70869ZM7.99445 7.14111C7.91876 7.22135 7.87618 7.32722 7.87523 7.43751V10.6455L6.12523 11.8125V7.43751C6.12526 7.32642 6.08303 7.21947 6.0071 7.13837L2.18773 3.06251H11.8127L7.99445 7.14111Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
4
packages/icons/svgs/home.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 5.99998L8 1.33331L14 5.99998V13.3333C14 13.6869 13.8595 14.0261 13.6095 14.2761C13.3594 14.5262 13.0203 14.6666 12.6667 14.6666H3.33333C2.97971 14.6666 2.64057 14.5262 2.39052 14.2761C2.14048 14.0261 2 13.6869 2 13.3333V5.99998Z" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6 14.6667V8H10V14.6667" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 551 B |
11
packages/icons/svgs/link.svg
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg width="6" height="11" viewBox="0 0 6 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1253_14423)">
|
||||
<path d="M0.8125 3.9375L0.8125 2.625C0.8125 2.33773 0.869081 2.05328 0.979014 1.78788C1.08895 1.52248 1.25008 1.28133 1.4532 1.0782C1.65633 0.875077 1.89748 0.713946 2.16288 0.604014C2.42828 0.494082 2.71273 0.437501 3 0.437501C3.28727 0.437501 3.57172 0.494082 3.83712 0.604014C4.10252 0.713946 4.34367 0.875077 4.5468 1.0782C4.74992 1.28133 4.91105 1.52248 5.02099 1.78788C5.13092 2.05328 5.1875 2.33773 5.1875 2.625L5.1875 3.9375M5.1875 6.5625L5.1875 7.875C5.1875 8.16227 5.13092 8.44672 5.02099 8.71212C4.91105 8.97752 4.74992 9.21867 4.5468 9.4218C4.13656 9.83203 3.58016 10.0625 3 10.0625C2.41984 10.0625 1.86344 9.83203 1.4532 9.4218C1.04297 9.01156 0.8125 8.45516 0.8125 7.875L0.8125 6.5625" stroke="#666666" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 7L3 3.5" stroke="#666666" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1253_14423">
|
||||
<rect width="10.5" height="6" fill="white" transform="translate(4.5897e-07 10.5) rotate(-90)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
3
packages/icons/svgs/lock-simple.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 5H11V3.5C11 2.70435 10.6839 1.94129 10.1213 1.37868C9.55871 0.81607 8.79565 0.5 8 0.5C7.20435 0.5 6.44129 0.81607 5.87868 1.37868C5.31607 1.94129 5 2.70435 5 3.5V5H3C2.73478 5 2.48043 5.10536 2.29289 5.29289C2.10536 5.48043 2 5.73478 2 6V13C2 13.2652 2.10536 13.5196 2.29289 13.7071C2.48043 13.8946 2.73478 14 3 14H13C13.2652 14 13.5196 13.8946 13.7071 13.7071C13.8946 13.5196 14 13.2652 14 13V6C14 5.73478 13.8946 5.48043 13.7071 5.29289C13.5196 5.10536 13.2652 5 13 5ZM6 3.5C6 2.96957 6.21071 2.46086 6.58579 2.08579C6.96086 1.71071 7.46957 1.5 8 1.5C8.53043 1.5 9.03914 1.71071 9.41421 2.08579C9.78929 2.46086 10 2.96957 10 3.5V5H6V3.5ZM13 13H3V6H13V13Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 789 B |
10
packages/icons/svgs/menu.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1253_14461)">
|
||||
<path d="M2.5 15H17.5V13.3333H2.5V15ZM2.5 10.8333H17.5V9.16667H2.5V10.8333ZM2.5 5V6.66667H17.5V5H2.5Z" fill="#181818"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1253_14461">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 371 B |
3
packages/icons/svgs/note.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3538 5.14625L9.85375 1.64625C9.80728 1.59983 9.75212 1.56303 9.69143 1.53793C9.63073 1.51284 9.56568 1.49995 9.5 1.5H3.5C3.23478 1.5 2.98043 1.60536 2.79289 1.79289C2.60536 1.98043 2.5 2.23478 2.5 2.5V13.5C2.5 13.7652 2.60536 14.0196 2.79289 14.2071C2.98043 14.3946 3.23478 14.5 3.5 14.5H12.5C12.7652 14.5 13.0196 14.3946 13.2071 14.2071C13.3946 14.0196 13.5 13.7652 13.5 13.5V5.5C13.5001 5.43432 13.4872 5.36927 13.4621 5.30858C13.437 5.24788 13.4002 5.19272 13.3538 5.14625ZM10 3.20688L11.7931 5H10V3.20688ZM12.5 13.5H3.5V2.5H9V5.5C9 5.63261 9.05268 5.75979 9.14645 5.85355C9.24022 5.94732 9.36739 6 9.5 6H12.5V13.5ZM10.5 8.5C10.5 8.63261 10.4473 8.75979 10.3536 8.85356C10.2598 8.94732 10.1326 9 10 9H6C5.86739 9 5.74022 8.94732 5.64645 8.85356C5.55268 8.75979 5.5 8.63261 5.5 8.5C5.5 8.36739 5.55268 8.24022 5.64645 8.14645C5.74022 8.05268 5.86739 8 6 8H10C10.1326 8 10.2598 8.05268 10.3536 8.14645C10.4473 8.24022 10.5 8.36739 10.5 8.5ZM10.5 10.5C10.5 10.6326 10.4473 10.7598 10.3536 10.8536C10.2598 10.9473 10.1326 11 10 11H6C5.86739 11 5.74022 10.9473 5.64645 10.8536C5.55268 10.7598 5.5 10.6326 5.5 10.5C5.5 10.3674 5.55268 10.2402 5.64645 10.1464C5.74022 10.0527 5.86739 10 6 10H10C10.1326 10 10.2598 10.0527 10.3536 10.1464C10.4473 10.2402 10.5 10.3674 10.5 10.5Z" fill="#181818"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
10
packages/icons/svgs/pin.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg width="11" height="12" viewBox="0 0 11 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1253_14268)">
|
||||
<path d="M10.7527 3.50183L7.54313 0.251121C7.46455 0.171507 7.37127 0.108353 7.26859 0.0652648C7.16592 0.0221771 7.05587 0 6.94474 0C6.8336 0 6.72356 0.0221771 6.62089 0.0652648C6.51821 0.108353 6.42492 0.171507 6.34635 0.251121L3.51015 3.13326C2.9464 2.95433 1.65919 2.73844 0.315917 3.83719C0.223329 3.91261 0.147457 4.00693 0.0932409 4.11402C0.0390243 4.22111 0.00767788 4.33856 0.00124234 4.45874C-0.00519321 4.57891 0.0134263 4.69911 0.0558887 4.81151C0.0983512 4.92392 0.163705 5.026 0.247696 5.11112L2.80308 7.69862L0.547022 9.98237C0.467636 10.0628 0.423037 10.1719 0.423037 10.2856C0.423037 10.3993 0.467636 10.5084 0.547022 10.5888C0.626409 10.6692 0.73408 10.7144 0.846349 10.7144C0.958619 10.7144 1.06629 10.6692 1.14568 10.5888L3.40015 8.30344L5.95395 10.8904C6.03245 10.9703 6.12574 11.0337 6.22847 11.077C6.33119 11.1203 6.44134 11.1426 6.5526 11.1427C6.5727 11.1427 6.59226 11.1427 6.61236 11.1427C6.73298 11.1343 6.85039 11.0996 6.9566 11.0411C7.06282 10.9826 7.15536 10.9016 7.22794 10.8036C8.26659 9.40541 8.16664 8.26862 7.92548 7.58933L10.7532 4.71416C10.8318 4.63453 10.8941 4.54 10.9366 4.43597C10.9791 4.33195 11.0009 4.22046 11.0009 4.10788C11.0008 3.99531 10.9789 3.88384 10.9363 3.77985C10.8937 3.67587 10.8313 3.5814 10.7527 3.50183ZM10.154 4.10826L7.12534 7.18648C7.06279 7.25007 7.02154 7.33196 7.0074 7.42058C6.99326 7.50921 7.00695 7.60009 7.04654 7.68041C7.54683 8.69451 6.95135 9.74773 6.5526 10.285L0.846349 4.50416C1.4852 3.98237 2.09654 3.84469 2.56404 3.84469C2.86069 3.84059 3.15488 3.89954 3.42765 4.01773C3.50722 4.05807 3.59733 4.07197 3.68516 4.05745C3.77298 4.04292 3.85405 4.00072 3.91683 3.93683L6.945 0.857013L10.154 4.10773V4.10826Z" fill="#666666"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1253_14268">
|
||||
<rect width="11" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
3
packages/icons/svgs/plus.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.25 8C14.25 8.19891 14.171 8.38968 14.0303 8.53033C13.8897 8.67098 13.6989 8.75 13.5 8.75H8.75V13.5C8.75 13.6989 8.67098 13.8897 8.53033 14.0303C8.38968 14.171 8.19891 14.25 8 14.25C7.80109 14.25 7.61032 14.171 7.46967 14.0303C7.32902 13.8897 7.25 13.6989 7.25 13.5V8.75H2.5C2.30109 8.75 2.11032 8.67098 1.96967 8.53033C1.82902 8.38968 1.75 8.19891 1.75 8C1.75 7.80109 1.82902 7.61032 1.96967 7.46967C2.11032 7.32902 2.30109 7.25 2.5 7.25H7.25V2.5C7.25 2.30109 7.32902 2.11032 7.46967 1.96967C7.61032 1.82902 7.80109 1.75 8 1.75C8.19891 1.75 8.38968 1.82902 8.53033 1.96967C8.67098 2.11032 8.75 2.30109 8.75 2.5V7.25H13.5C13.6989 7.25 13.8897 7.32902 14.0303 7.46967C14.171 7.61032 14.25 7.80109 14.25 8Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 838 B |
10
packages/icons/svgs/search.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1253_14465)">
|
||||
<path d="M12.9167 11.6667H12.2583L12.025 11.4417C12.8417 10.4917 13.3333 9.25833 13.3333 7.91667C13.3333 4.925 10.9083 2.5 7.91667 2.5C4.925 2.5 2.5 4.925 2.5 7.91667C2.5 10.9083 4.925 13.3333 7.91667 13.3333C9.25833 13.3333 10.4917 12.8417 11.4417 12.025L11.6667 12.2583V12.9167L15.8333 17.075L17.075 15.8333L12.9167 11.6667ZM7.91667 11.6667C5.84167 11.6667 4.16667 9.99167 4.16667 7.91667C4.16667 5.84167 5.84167 4.16667 7.91667 4.16667C9.99167 4.16667 11.6667 5.84167 11.6667 7.91667C11.6667 9.99167 9.99167 11.6667 7.91667 11.6667Z" fill="#181818"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1253_14465">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 805 B |
3
packages/icons/svgs/shield-check.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.5 3.75H4.5C4.10218 3.75 3.72064 3.90804 3.43934 4.18934C3.15804 4.47064 3 4.85218 3 5.25V10.5C3 15.4425 5.3925 18.4378 7.39969 20.0803C9.56156 21.8484 11.7122 22.4484 11.8059 22.4738C11.9348 22.5088 12.0708 22.5088 12.1997 22.4738C12.2934 22.4484 14.4413 21.8484 16.6059 20.0803C18.6075 18.4378 21 15.4425 21 10.5V5.25C21 4.85218 20.842 4.47064 20.5607 4.18934C20.2794 3.90804 19.8978 3.75 19.5 3.75ZM16.2825 10.2806L11.0325 15.5306C10.9628 15.6004 10.8801 15.6557 10.7891 15.6934C10.698 15.7312 10.6004 15.7506 10.5019 15.7506C10.4033 15.7506 10.3057 15.7312 10.2147 15.6934C10.1236 15.6557 10.0409 15.6004 9.97125 15.5306L7.72125 13.2806C7.58052 13.1399 7.50146 12.949 7.50146 12.75C7.50146 12.551 7.58052 12.3601 7.72125 12.2194C7.86198 12.0786 8.05285 11.9996 8.25187 11.9996C8.4509 11.9996 8.64177 12.0786 8.7825 12.2194L10.5 13.9397L15.2194 9.21937C15.2891 9.14969 15.3718 9.09442 15.4628 9.0567C15.5539 9.01899 15.6515 8.99958 15.75 8.99958C15.8485 8.99958 15.9461 9.01899 16.0372 9.0567C16.1282 9.09442 16.2109 9.14969 16.2806 9.21937C16.3503 9.28906 16.4056 9.37178 16.4433 9.46283C16.481 9.55387 16.5004 9.65145 16.5004 9.75C16.5004 9.84855 16.481 9.94613 16.4433 10.0372C16.4056 10.1282 16.3503 10.2109 16.2806 10.2806H16.2825Z" fill="#008836"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
packages/icons/svgs/shopping-mode.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.38325 14.2667C8.11658 14.5333 7.79992 14.6667 7.43325 14.6667C7.06659 14.6667 6.74992 14.5333 6.48325 14.2667L1.71659 9.50001C1.46103 9.24445 1.33325 8.93057 1.33325 8.55834C1.33325 8.18612 1.46103 7.87223 1.71659 7.61668L7.58325 1.73334C7.70547 1.61112 7.84992 1.5139 8.01658 1.44168C8.18325 1.36945 8.36103 1.33334 8.54992 1.33334H13.3166C13.6833 1.33334 13.9971 1.4639 14.2583 1.72501C14.5194 1.98612 14.6499 2.30001 14.6499 2.66668V7.43334C14.6499 7.62223 14.6138 7.80001 14.5416 7.96668C14.4694 8.13334 14.3721 8.27779 14.2499 8.40001L8.38325 14.2667ZM11.6499 5.33334C11.9277 5.33334 12.1638 5.23612 12.3583 5.04168C12.5527 4.84723 12.6499 4.61112 12.6499 4.33334C12.6499 4.05557 12.5527 3.81945 12.3583 3.62501C12.1638 3.43057 11.9277 3.33334 11.6499 3.33334C11.3721 3.33334 11.136 3.43057 10.9416 3.62501C10.7471 3.81945 10.6499 4.05557 10.6499 4.33334C10.6499 4.61112 10.7471 4.84723 10.9416 5.04168C11.136 5.23612 11.3721 5.33334 11.6499 5.33334ZM7.43325 13.3333L13.3166 7.43334V2.66668H8.54992L2.66659 8.56668L7.43325 13.3333Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
packages/icons/svgs/sliders.svg
Executable file
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66699 13.9999V9.33325" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.66699 6.66667V2" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 14V8" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 5.33333V2" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.333 14.0001V10.6667" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.333 8V2" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M0.666992 9.33325H4.66699" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6 5.33325H10" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.333 10.6667H15.333" stroke="#181818" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
3
packages/icons/svgs/sort-ascending.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 8C8 8.13261 7.94732 8.25979 7.85355 8.35355C7.75979 8.44732 7.63261 8.5 7.5 8.5H3C2.86739 8.5 2.74021 8.44732 2.64645 8.35355C2.55268 8.25979 2.5 8.13261 2.5 8C2.5 7.86739 2.55268 7.74021 2.64645 7.64645C2.74021 7.55268 2.86739 7.5 3 7.5H7.5C7.63261 7.5 7.75979 7.55268 7.85355 7.64645C7.94732 7.74021 8 7.86739 8 8ZM3 4.5H11.5C11.6326 4.5 11.7598 4.44732 11.8536 4.35355C11.9473 4.25979 12 4.13261 12 4C12 3.86739 11.9473 3.74021 11.8536 3.64645C11.7598 3.55268 11.6326 3.5 11.5 3.5H3C2.86739 3.5 2.74021 3.55268 2.64645 3.64645C2.55268 3.74021 2.5 3.86739 2.5 4C2.5 4.13261 2.55268 4.25979 2.64645 4.35355C2.74021 4.44732 2.86739 4.5 3 4.5ZM6.5 11.5H3C2.86739 11.5 2.74021 11.5527 2.64645 11.6464C2.55268 11.7402 2.5 11.8674 2.5 12C2.5 12.1326 2.55268 12.2598 2.64645 12.3536C2.74021 12.4473 2.86739 12.5 3 12.5H6.5C6.63261 12.5 6.75979 12.4473 6.85355 12.3536C6.94732 12.2598 7 12.1326 7 12C7 11.8674 6.94732 11.7402 6.85355 11.6464C6.75979 11.5527 6.63261 11.5 6.5 11.5ZM14.3538 10.1462C14.3073 10.0998 14.2522 10.0629 14.1915 10.0377C14.1308 10.0126 14.0657 9.99961 14 9.99961C13.9343 9.99961 13.8692 10.0126 13.8085 10.0377C13.7478 10.0629 13.6927 10.0998 13.6462 10.1462L12 11.7931V7C12 6.86739 11.9473 6.74021 11.8536 6.64645C11.7598 6.55268 11.6326 6.5 11.5 6.5C11.3674 6.5 11.2402 6.55268 11.1464 6.64645C11.0527 6.74021 11 6.86739 11 7V11.7931L9.35375 10.1462C9.25993 10.0524 9.13268 9.99972 9 9.99972C8.86732 9.99972 8.74007 10.0524 8.64625 10.1462C8.55243 10.2401 8.49972 10.3673 8.49972 10.5C8.49972 10.6327 8.55243 10.7599 8.64625 10.8538L11.1462 13.3538C11.1927 13.4002 11.2478 13.4371 11.3085 13.4623C11.3692 13.4874 11.4343 13.5004 11.5 13.5004C11.5657 13.5004 11.6308 13.4874 11.6915 13.4623C11.7522 13.4371 11.8073 13.4002 11.8538 13.3538L14.3538 10.8538C14.4002 10.8073 14.4371 10.7522 14.4623 10.6915C14.4874 10.6308 14.5004 10.5657 14.5004 10.5C14.5004 10.4343 14.4874 10.3692 14.4623 10.3085C14.4371 10.2478 14.4002 10.1927 14.3538 10.1462Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
packages/icons/svgs/sort-descending.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><!-- Icon from Phosphor by Phosphor Icons - https://github.com/phosphor-icons/core/blob/main/LICENSE --><path fill="currentColor" d="M40 128a8 8 0 0 1 8-8h72a8 8 0 0 1 0 16H48a8 8 0 0 1-8-8m8-56h56a8 8 0 0 0 0-16H48a8 8 0 0 0 0 16m136 112H48a8 8 0 0 0 0 16h136a8 8 0 0 0 0-16m45.66-101.66l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 11.32 11.32L176 67.31V144a8 8 0 0 0 16 0V67.31l26.34 26.35a8 8 0 0 0 11.32-11.32"/></svg>
|
||||
|
After Width: | Height: | Size: 505 B |
3
packages/icons/svgs/square-out.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 6.5C14 6.63261 13.9473 6.75979 13.8536 6.85355C13.7598 6.94732 13.6326 7 13.5 7C13.3674 7 13.2402 6.94732 13.1464 6.85355C13.0527 6.75979 13 6.63261 13 6.5V3.7075L8.85437 7.85375C8.76055 7.94757 8.63331 8.00028 8.50062 8.00028C8.36794 8.00028 8.2407 7.94757 8.14688 7.85375C8.05305 7.75993 8.00035 7.63268 8.00035 7.5C8.00035 7.36732 8.05305 7.24007 8.14688 7.14625L12.2925 3H9.5C9.36739 3 9.24021 2.94732 9.14645 2.85355C9.05268 2.75979 9 2.63261 9 2.5C9 2.36739 9.05268 2.24021 9.14645 2.14645C9.24021 2.05268 9.36739 2 9.5 2H13.5C13.6326 2 13.7598 2.05268 13.8536 2.14645C13.9473 2.24021 14 2.36739 14 2.5V6.5ZM11.5 8C11.3674 8 11.2402 8.05268 11.1464 8.14645C11.0527 8.24021 11 8.36739 11 8.5V13H3V5H7.5C7.63261 5 7.75979 4.94732 7.85355 4.85355C7.94732 4.75979 8 4.63261 8 4.5C8 4.36739 7.94732 4.24021 7.85355 4.14645C7.75979 4.05268 7.63261 4 7.5 4H3C2.73478 4 2.48043 4.10536 2.29289 4.29289C2.10536 4.48043 2 4.73478 2 5V13C2 13.2652 2.10536 13.5196 2.29289 13.7071C2.48043 13.8946 2.73478 14 3 14H11C11.2652 14 11.5196 13.8946 11.7071 13.7071C11.8946 13.5196 12 13.2652 12 13V8.5C12 8.36739 11.9473 8.24021 11.8536 8.14645C11.7598 8.05268 11.6326 8 11.5 8Z" fill="#181818"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
10
packages/icons/svgs/star-filled.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1253_14370)">
|
||||
<path d="M5.74163 9.31794C5.90053 9.22204 6.09947 9.22204 6.25837 9.31794L8.6935 10.7877C9.07216 11.0162 9.53928 10.6767 9.4388 10.246L8.79256 7.4759C8.7504 7.29515 8.81182 7.10592 8.9521 6.98439L11.1043 5.11994C11.4386 4.83034 11.2599 4.28121 10.8192 4.24382L7.98643 4.00349C7.80164 3.98781 7.64071 3.87114 7.56834 3.70039L6.46036 1.08618C6.28807 0.67967 5.71194 0.67967 5.53964 1.08618L4.43166 3.70039C4.35929 3.87114 4.19836 3.98781 4.01357 4.00349L1.18082 4.24382C0.740103 4.28121 0.561398 4.83034 0.895697 5.11994L3.0479 6.98439C3.18818 7.10592 3.2496 7.29515 3.20744 7.4759L2.56121 10.246C2.46073 10.6767 2.92784 11.0162 3.3065 10.7877L5.74163 9.31794Z" fill="#E5C131"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1253_14370">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 928 B |
3
packages/icons/svgs/star.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.99992 1.33331L10.0599 5.50665L14.6666 6.17998L11.3333 9.42665L12.1199 14.0133L7.99992 11.8466L3.87992 14.0133L4.66659 9.42665L1.33325 6.17998L5.93992 5.50665L7.99992 1.33331Z" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
18
packages/icons/svgs/sun.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1174_4412)">
|
||||
<path d="M5.71445 8.09522C7.02941 8.09522 8.0954 7.02923 8.0954 5.71427C8.0954 4.3993 7.02941 3.33331 5.71445 3.33331C4.39948 3.33331 3.3335 4.3993 3.3335 5.71427C3.3335 7.02923 4.39948 8.09522 5.71445 8.09522Z" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.71436 0.476196V1.42858" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.71436 10V10.9524" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.00977 2.00952L2.68596 2.68571" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.74268 8.74286L9.41887 9.41905" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M0.476074 5.71429H1.42846" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10 5.71429H10.9524" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.00977 9.41905L2.68596 8.74286" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.74268 2.68571L9.41887 2.00952" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1174_4412">
|
||||
<rect width="11.4286" height="11.4286" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
4
packages/icons/svgs/trash.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 4H3.33333H14" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.6668 4.00001V13.3333C12.6668 13.687 12.5264 14.0261 12.2763 14.2762C12.0263 14.5262 11.6871 14.6667 11.3335 14.6667H4.66683C4.31321 14.6667 3.97407 14.5262 3.72402 14.2762C3.47397 14.0261 3.3335 13.687 3.3335 13.3333V4.00001M5.3335 4.00001V2.66668C5.3335 2.31305 5.47397 1.97392 5.72402 1.72387C5.97407 1.47382 6.31321 1.33334 6.66683 1.33334H9.3335C9.68712 1.33334 10.0263 1.47382 10.2763 1.72387C10.5264 1.97392 10.6668 2.31305 10.6668 2.66668V4.00001" stroke="#666666" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 769 B |