Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
c1708df7
Commit
c1708df7
authored
Jul 08, 2025
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: use select/dropdown instead of popover
parent
d32924ec
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
57 additions
and
97 deletions
+57
-97
HomeSidebar.tsx
web/src/components/HomeSidebar/HomeSidebar.tsx
+1
-1
MarkdownMenu.tsx
web/src/components/MemoEditor/ActionButton/MarkdownMenu.tsx
+23
-31
VisibilitySelector.tsx
...components/MemoEditor/ActionButton/VisibilitySelector.tsx
+14
-46
index.tsx
web/src/components/MemoEditor/index.tsx
+1
-1
SearchBar.tsx
web/src/components/SearchBar.tsx
+3
-3
UserBanner.tsx
web/src/components/UserBanner.tsx
+10
-10
select.tsx
web/src/components/ui/select.tsx
+2
-2
en.json
web/src/locales/en.json
+1
-1
id.json
web/src/locales/id.json
+2
-2
No files found.
web/src/components/HomeSidebar/HomeSidebar.tsx
View file @
c1708df7
...
@@ -40,7 +40,7 @@ const HomeSidebar = observer((props: Props) => {
...
@@ -40,7 +40,7 @@ const HomeSidebar = observer((props: Props) => {
return
(
return
(
<
aside
<
aside
className=
{
cn
(
className=
{
cn
(
"relative w-full h-full overflow-auto flex flex-col justify-start items-start bg-
sidebar
text-sidebar-foreground"
,
"relative w-full h-full overflow-auto flex flex-col justify-start items-start bg-
background
text-sidebar-foreground"
,
props
.
className
,
props
.
className
,
)
}
)
}
>
>
...
...
web/src/components/MemoEditor/ActionButton/MarkdownMenu.tsx
View file @
c1708df7
import
{
CheckSquareIcon
,
Code2Icon
,
SquareSlashIcon
}
from
"lucide-react"
;
import
{
CheckSquareIcon
,
Code2Icon
,
SquareSlashIcon
}
from
"lucide-react"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
Popover
,
PopoverContent
,
PopoverTrigger
}
from
"../../ui/popover
"
;
import
{
DropdownMenu
,
DropdownMenuContent
,
DropdownMenuItem
,
DropdownMenuTrigger
}
from
"../../ui/dropdown-menu
"
;
import
{
EditorRefActions
}
from
"../Editor"
;
import
{
EditorRefActions
}
from
"../Editor"
;
interface
Props
{
interface
Props
{
...
@@ -60,29 +60,22 @@ const MarkdownMenu = (props: Props) => {
...
@@ -60,29 +60,22 @@ const MarkdownMenu = (props: Props) => {
};
};
return
(
return
(
<
Popover
>
<
DropdownMenu
>
<
Popover
Trigger
asChild
>
<
DropdownMenu
Trigger
asChild
>
<
Button
variant=
"ghost"
size=
"icon"
>
<
Button
variant=
"ghost"
size=
"icon"
>
<
SquareSlashIcon
className=
"size-5"
/>
<
SquareSlashIcon
className=
"size-5"
/>
</
Button
>
</
Button
>
</
PopoverTrigger
>
</
DropdownMenuTrigger
>
<
PopoverContent
align=
"start"
className=
"text-sm p-1"
>
<
DropdownMenuContent
align=
"start"
>
<
div
className=
"flex flex-col text-sm gap-0.5"
>
<
DropdownMenuItem
onClick=
{
handleCodeBlockClick
}
>
<
button
<
Code2Icon
className=
"w-4 h-auto text-muted-foreground"
/>
onClick=
{
handleCodeBlockClick
}
{
t
(
"markdown.code-block"
)
}
className=
"flex items-center gap-2 px-2 py-1 text-left text-foreground hover:bg-background outline-none rounded"
</
DropdownMenuItem
>
>
<
DropdownMenuItem
onClick=
{
handleCheckboxClick
}
>
<
Code2Icon
className=
"w-4 h-auto"
/>
<
CheckSquareIcon
className=
"w-4 h-auto text-muted-foreground"
/>
<
span
>
{
t
(
"markdown.code-block"
)
}
</
span
>
{
t
(
"markdown.checkbox"
)
}
</
button
>
</
DropdownMenuItem
>
<
button
<
div
className=
"px-2 -mt-1"
>
onClick=
{
handleCheckboxClick
}
className=
"flex items-center gap-2 px-2 py-1 text-left text-foreground hover:bg-background outline-none rounded"
>
<
CheckSquareIcon
className=
"w-4 h-auto"
/>
<
span
>
{
t
(
"markdown.checkbox"
)
}
</
span
>
</
button
>
<
div
className=
"pl-2"
>
<
a
<
a
className=
"text-xs text-primary hover:underline"
className=
"text-xs text-primary hover:underline"
href=
"https://www.usememos.com/docs/getting-started/content-syntax"
href=
"https://www.usememos.com/docs/getting-started/content-syntax"
...
@@ -92,9 +85,8 @@ const MarkdownMenu = (props: Props) => {
...
@@ -92,9 +85,8 @@ const MarkdownMenu = (props: Props) => {
{
t
(
"markdown.content-syntax"
)
}
{
t
(
"markdown.content-syntax"
)
}
</
a
>
</
a
>
</
div
>
</
div
>
</
div
>
</
DropdownMenuContent
>
</
PopoverContent
>
</
DropdownMenu
>
</
Popover
>
);
);
};
};
...
...
web/src/components/MemoEditor/ActionButton/VisibilitySelector.tsx
View file @
c1708df7
import
{
ChevronDownIcon
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
VisibilityIcon
from
"@/components/VisibilityIcon"
;
import
VisibilityIcon
from
"@/components/VisibilityIcon"
;
import
{
Popover
,
PopoverContent
,
PopoverTrigger
}
from
"@/components/ui/popover"
;
import
{
Select
,
SelectContent
,
SelectItem
,
SelectTrigger
,
SelectValue
}
from
"@/components/ui/select"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
@@ -10,13 +7,11 @@ interface Props {
...
@@ -10,13 +7,11 @@ interface Props {
value
:
Visibility
;
value
:
Visibility
;
onChange
:
(
visibility
:
Visibility
)
=>
void
;
onChange
:
(
visibility
:
Visibility
)
=>
void
;
onOpenChange
?:
(
open
:
boolean
)
=>
void
;
onOpenChange
?:
(
open
:
boolean
)
=>
void
;
className
?:
string
;
}
}
const
VisibilitySelector
=
(
props
:
Props
)
=>
{
const
VisibilitySelector
=
(
props
:
Props
)
=>
{
const
{
value
,
onChange
}
=
props
;
const
{
value
,
onChange
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
[
open
,
setOpen
]
=
useState
(
false
);
const
visibilityOptions
=
[
const
visibilityOptions
=
[
{
value
:
Visibility
.
PRIVATE
,
label
:
t
(
"memo.visibility.private"
)
},
{
value
:
Visibility
.
PRIVATE
,
label
:
t
(
"memo.visibility.private"
)
},
...
@@ -24,53 +19,26 @@ const VisibilitySelector = (props: Props) => {
...
@@ -24,53 +19,26 @@ const VisibilitySelector = (props: Props) => {
{
value
:
Visibility
.
PUBLIC
,
label
:
t
(
"memo.visibility.public"
)
},
{
value
:
Visibility
.
PUBLIC
,
label
:
t
(
"memo.visibility.public"
)
},
];
];
const
currentOption
=
visibilityOptions
.
find
((
option
)
=>
option
.
value
===
value
);
const
handleSelect
=
(
visibility
:
Visibility
)
=>
{
onChange
(
visibility
);
handleOpenChange
(
false
);
};
const
handleOpenChange
=
(
open
:
boolean
)
=>
{
const
handleOpenChange
=
(
open
:
boolean
)
=>
{
setOpen
(
open
);
if
(
props
.
onOpenChange
)
{
if
(
props
.
onOpenChange
)
{
props
.
onOpenChange
(
open
);
props
.
onOpenChange
(
open
);
}
}
};
};
return
(
return
(
<
Popover
open=
{
open
}
onOpenChange=
{
handleOpenChange
}
>
<
Select
value=
{
value
.
toString
()
}
onValueChange=
{
onChange
}
onOpenChange=
{
handleOpenChange
}
>
<
PopoverTrigger
asChild
>
<
SelectTrigger
size=
"xs"
className=
"!bg-background"
>
<
button
<
SelectValue
/>
className=
{
cn
(
</
SelectTrigger
>
`flex items-center justify-center gap-1 px-0.5 text-xs rounded hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 transition-colors`
,
<
SelectContent
align=
"end"
>
props
.
className
,
)
}
type=
"button"
>
<
VisibilityIcon
className=
"w-3 h-3"
visibility=
{
value
}
/>
<
span
>
{
currentOption
?.
label
}
</
span
>
<
ChevronDownIcon
className=
"w-3 h-3 opacity-60"
/>
</
button
>
</
PopoverTrigger
>
<
PopoverContent
className=
"p-1!"
align=
"end"
sideOffset=
{
2
}
alignOffset=
{
-
4
}
>
<
div
className=
"flex flex-col gap-0.5"
>
{
visibilityOptions
.
map
((
option
)
=>
(
{
visibilityOptions
.
map
((
option
)
=>
(
<
button
<
SelectItem
key=
{
option
.
value
}
value=
{
option
.
value
.
toString
()
}
>
key=
{
option
.
value
}
<
VisibilityIcon
className=
"size-3.5"
visibility=
{
option
.
value
}
/>
onClick=
{
()
=>
handleSelect
(
option
.
value
)
}
{
option
.
label
}
className=
{
cn
(
</
SelectItem
>
`flex items-center gap-1 px-1 py-1 text-xs text-left hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 rounded transition-colors`
,
option
.
value
===
value
?
"bg-muted"
:
""
,
)
}
>
<
VisibilityIcon
className=
"w-3 h-3"
visibility=
{
option
.
value
}
/>
<
span
>
{
option
.
label
}
</
span
>
</
button
>
))
}
))
}
</
div
>
</
SelectContent
>
</
PopoverContent
>
</
Select
>
</
Popover
>
);
);
};
};
...
...
web/src/components/MemoEditor/index.tsx
View file @
c1708df7
...
@@ -530,7 +530,7 @@ const MemoEditor = observer((props: Props) => {
...
@@ -530,7 +530,7 @@ const MemoEditor = observer((props: Props) => {
</
div
>
</
div
>
<
div
<
div
className=
{
cn
(
className=
{
cn
(
"absolute right-1 top-1
opacity-60
"
,
"absolute right-1 top-1"
,
"flex flex-row justify-end items-center gap-1"
,
"flex flex-row justify-end items-center gap-1"
,
"invisible group-focus-within:visible group-hover:visible hover:visible focus-within:visible"
,
"invisible group-focus-within:visible group-hover:visible hover:visible focus-within:visible"
,
(
isVisibilitySelectorOpen
||
memoName
)
&&
"visible"
,
(
isVisibilitySelectorOpen
||
memoName
)
&&
"visible"
,
...
...
web/src/components/SearchBar.tsx
View file @
c1708df7
...
@@ -33,15 +33,15 @@ const SearchBar = observer(() => {
...
@@ -33,15 +33,15 @@ const SearchBar = observer(() => {
return
(
return
(
<
div
className=
"relative w-full h-auto flex flex-row justify-start items-center"
>
<
div
className=
"relative w-full h-auto flex flex-row justify-start items-center"
>
<
SearchIcon
className=
"absolute left-2 w-4 h-auto opacity-40 text-
muted
-foreground"
/>
<
SearchIcon
className=
"absolute left-2 w-4 h-auto opacity-40 text-
sidebar
-foreground"
/>
<
input
<
input
className=
{
cn
(
"w-full text-
muted-foreground leading-6 bg-muted
border border-border text-sm rounded-lg p-1 pl-8 outline-0"
)
}
className=
{
cn
(
"w-full text-
sidebar-foreground leading-6 bg-sidebar
border border-border text-sm rounded-lg p-1 pl-8 outline-0"
)
}
placeholder=
{
t
(
"memo.search-placeholder"
)
}
placeholder=
{
t
(
"memo.search-placeholder"
)
}
value=
{
queryText
}
value=
{
queryText
}
onChange=
{
onTextChange
}
onChange=
{
onTextChange
}
onKeyDown=
{
onKeyDown
}
onKeyDown=
{
onKeyDown
}
/>
/>
<
MemoDisplaySettingMenu
className=
"absolute right-2 top-2 text-
muted
-foreground"
/>
<
MemoDisplaySettingMenu
className=
"absolute right-2 top-2 text-
sidebar
-foreground"
/>
</
div
>
</
div
>
);
);
});
});
...
...
web/src/components/UserBanner.tsx
View file @
c1708df7
...
@@ -41,24 +41,24 @@ const UserBanner = (props: Props) => {
...
@@ -41,24 +41,24 @@ const UserBanner = (props: Props) => {
</
DropdownMenuTrigger
>
</
DropdownMenuTrigger
>
<
DropdownMenuContent
align=
"start"
>
<
DropdownMenuContent
align=
"start"
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
`/u/${encodeURIComponent(currentUser.username)}`
)
}
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
`/u/${encodeURIComponent(currentUser.username)}`
)
}
>
<
SquareUserIcon
className=
"
w-4 h-auto
text-muted-foreground"
/>
<
SquareUserIcon
className=
"
size-4
text-muted-foreground"
/>
<
span
className=
"truncate"
>
{
t
(
"common.profile"
)
}
</
span
>
{
t
(
"common.profile"
)
}
</
DropdownMenuItem
>
</
DropdownMenuItem
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
ARCHIVED
)
}
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
ARCHIVED
)
}
>
<
ArchiveIcon
className=
"
w-4 h-auto
text-muted-foreground"
/>
<
ArchiveIcon
className=
"
size-4
text-muted-foreground"
/>
<
span
className=
"truncate"
>
{
t
(
"common.archived"
)
}
</
span
>
{
t
(
"common.archived"
)
}
</
DropdownMenuItem
>
</
DropdownMenuItem
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
INBOX
)
}
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
INBOX
)
}
>
<
BellIcon
className=
"
w-4 h-auto
text-muted-foreground"
/>
<
BellIcon
className=
"
size-4
text-muted-foreground"
/>
<
span
className=
"truncate"
>
{
t
(
"common.inbox"
)
}
</
span
>
{
t
(
"common.inbox"
)
}
</
DropdownMenuItem
>
</
DropdownMenuItem
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
SETTING
)
}
>
<
DropdownMenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
SETTING
)
}
>
<
SettingsIcon
className=
"
w-4 h-auto
text-muted-foreground"
/>
<
SettingsIcon
className=
"
size-4
text-muted-foreground"
/>
<
span
className=
"truncate"
>
{
t
(
"common.settings"
)
}
</
span
>
{
t
(
"common.settings"
)
}
</
DropdownMenuItem
>
</
DropdownMenuItem
>
<
DropdownMenuItem
onClick=
{
handleSignOut
}
>
<
DropdownMenuItem
onClick=
{
handleSignOut
}
>
<
LogOutIcon
className=
"
w-4 h-auto
text-muted-foreground"
/>
<
LogOutIcon
className=
"
size-4
text-muted-foreground"
/>
<
span
className=
"truncate"
>
{
t
(
"common.sign-out"
)
}
</
span
>
{
t
(
"common.sign-out"
)
}
</
DropdownMenuItem
>
</
DropdownMenuItem
>
</
DropdownMenuContent
>
</
DropdownMenuContent
>
</
DropdownMenu
>
</
DropdownMenu
>
...
...
web/src/components/ui/select.tsx
View file @
c1708df7
...
@@ -21,14 +21,14 @@ function SelectTrigger({
...
@@ -21,14 +21,14 @@ function SelectTrigger({
children
,
children
,
...
props
...
props
}:
React
.
ComponentProps
<
typeof
SelectPrimitive
.
Trigger
>
&
{
}:
React
.
ComponentProps
<
typeof
SelectPrimitive
.
Trigger
>
&
{
size
?:
"sm"
|
"default"
;
size
?:
"
xs"
|
"
sm"
|
"default"
;
})
{
})
{
return
(
return
(
<
SelectPrimitive
.
Trigger
<
SelectPrimitive
.
Trigger
data
-
slot=
"select-trigger"
data
-
slot=
"select-trigger"
data
-
size=
{
size
}
data
-
size=
{
size
}
className=
{
cn
(
className=
{
cn
(
"border-border data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between
gap-2 rounded-md border bg-transparent px-2 py-1 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=sm]:h-7
*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
,
"border-border data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between
rounded-md border bg-transparent text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=default]:gap-2 data-[size=default]:px-2 data-[size=default]:py-1 data-[size=sm]:h-7 data-[size=sm]:gap-2 data-[size=sm]:px-2 data-[size=sm]:py-1 data-[size=xs]:h-6 data-[size=xs]:gap-1 data-[size=xs]:px-1 data-[size=xs]:py-0.5 data-[size=xs]:text-xs
*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
,
className
,
className
,
)
}
)
}
{
...
props
}
{
...
props
}
...
...
web/src/locales/en.json
View file @
c1708df7
...
@@ -162,7 +162,7 @@
...
@@ -162,7 +162,7 @@
"visibility"
:
{
"visibility"
:
{
"disabled"
:
"Public memos are disabled"
,
"disabled"
:
"Public memos are disabled"
,
"private"
:
"Private"
,
"private"
:
"Private"
,
"protected"
:
"
Workspace
"
,
"protected"
:
"
Protected
"
,
"public"
:
"Public"
"public"
:
"Public"
},
},
"list"
:
"List"
,
"list"
:
"List"
,
...
...
web/src/locales/id.json
View file @
c1708df7
...
@@ -122,7 +122,7 @@
...
@@ -122,7 +122,7 @@
"visibility"
:
{
"visibility"
:
{
"disabled"
:
"Memo publik dinonaktifkan"
,
"disabled"
:
"Memo publik dinonaktifkan"
,
"private"
:
"Pribadi"
,
"private"
:
"Pribadi"
,
"protected"
:
"
Workspace
"
,
"protected"
:
"
Protected
"
,
"public"
:
"Publik"
"public"
:
"Publik"
}
}
},
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment