Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/lib/actions/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,16 @@ export enum Click {
WebsiteOpenClick = 'click_open_website',
CopyPromptStarterKitClick = 'click_copy_prompt_starter_kit',
OpenInCursorClick = 'click_open_in_cursor',
OpenInLovableClick = 'click_open_in_lovable'
OpenInLovableClick = 'click_open_in_lovable',
RowCopyUrl = 'click_row_copy_url',
RowCopyJson = 'click_row_copy_json',
RowCopySnippet = 'click_row_copy_snippet',
RowContextMenuOpen = 'click_row_context_menu_open',
RowUpdate = 'click_row_update',
RowDuplicate = 'click_row_duplicate',
RowDelete = 'click_row_delete',
RowPermissions = 'click_row_permissions',
RowActivity = 'click_row_activity'
}

export enum Submit {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<script lang="ts">
import { createContextMenu, melt } from '@melt-ui/svelte';
import { Card, ActionMenu, Divider } from '@appwrite.io/pink-svelte';
import type { ComponentType } from 'svelte';
import {
IconPencil,
IconDuplicate,
IconKey,
IconChartBar,
IconLink,
IconTrash,
IconClipboardCopy,
IconCode,
IconChevronRight
} from '@appwrite.io/pink-icons-svelte';
import type { RowCellAction } from './sheetOptions.svelte';
import { trackEvent, Click } from '$lib/actions/analytics';

export let onSelect: (action: RowCellAction) => void;

const {
elements: { menu, trigger, item },
builders: { createSubmenu }
} = createContextMenu({
positioning: {
placement: 'right-start',
gutter: 2,
sameWidth: false
},
onOpenChange: ({ next }) => {
if (next) {
trackEvent(Click.RowContextMenuOpen);
}
return next;
}
});

const {
elements: { subMenu: copySubMenu, subTrigger: copySubTrigger }
} = createSubmenu({
positioning: {
placement: 'right-start',
gutter: 8
},
arrowSize: 0
});

const menuItems: {
label?: string;
icon?: ComponentType;
action?: RowCellAction;
subMenu?: string;
divider?: boolean;
danger?: boolean;
}[] = [
{ label: 'Update row', icon: IconPencil, action: 'update' },
{ label: 'Duplicate row', icon: IconDuplicate, action: 'duplicate-row' },
{ divider: true },
{ label: 'Manage permissions', icon: IconKey, action: 'permissions' },
{ label: 'View activity', icon: IconChartBar, action: 'activity' },
{ divider: true },
{ label: 'Copy', icon: IconDuplicate, subMenu: 'copy' },
{ divider: true },
{ label: 'Delete', icon: IconTrash, action: 'delete', danger: true }
];

const copyMenuItems: { label: string; icon: ComponentType; action: RowCellAction }[] = [
{ label: 'URL', icon: IconLink, action: 'copy-url' },
{ label: 'JSON', icon: IconClipboardCopy, action: 'copy-json' },
{ label: 'Code snippet', icon: IconCode, action: 'copy-snippet' }
];
</script>

<div use:melt={$trigger} style="display: contents;">
<slot />
</div>

<div class="menu" use:melt={$menu}>
<Card.Base padding="none">
<div class="action-menu-root">
<ActionMenu.Root>
{#each menuItems as menuItem}
{#if menuItem.divider}
<div style:padding-block="0.25rem">
<Divider />
</div>
{:else if menuItem.subMenu === 'copy'}
<!-- Copy Submenu Trigger -->
<div use:melt={$copySubTrigger} class="sub-trigger">
<ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={menuItem.icon}
trailingIcon={IconChevronRight}>
{menuItem.label}
</ActionMenu.Item.Button>
</ActionMenu.Root>
Comment on lines +89 to +96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how ActionMenu.Root is typically used in the codebase
rg -n -B2 -A10 'ActionMenu\.Root' --type=svelte src/routes/ | head -100

Repository: appwrite/console

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for ActionMenu.Root usage in .svelte files
rg -n -B3 -A8 'ActionMenu\.Root' src/routes/ --glob='*.svelte' | head -150

Repository: appwrite/console

Length of output: 18237


🏁 Script executed:

#!/bin/bash
# Get the full context of the rowContextMenu.svelte file
cat -n src/routes/\(console\)/project-\[region\]-\[project\]/databases/database-\[database\]/table-\[table\]/rowContextMenu.svelte | sed -n '74,154p'

Repository: appwrite/console

Length of output: 3576


Simplify ActionMenu.Root wrapping in copy submenu items (lines 120-124).

Each item in the copyMenuItems loop has its own ActionMenu.Root, which is atypical. The standard pattern across the codebase is one ActionMenu.Root wrapping multiple items. Move the ActionMenu.Root outside the loop:

Current structure (incorrect)
{`#each` copyMenuItems as copyItem}
    <div use:melt={$item} on:m-click={() => onSelect(copyItem.action)}>
        <ActionMenu.Root>
            <ActionMenu.Item.Button leadingIcon={copyItem.icon}>
                {copyItem.label}
            </ActionMenu.Item.Button>
        </ActionMenu.Root>
    </div>
{/each}

Recommended: One ActionMenu.Root should wrap all items in the loop, consistent with how the main menu items are structured (lines 81-111).

🤖 Prompt for AI Agents
In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/table-[table]/rowContextMenu.svelte
around lines 89 - 96, The copy submenu incorrectly instantiates an
ActionMenu.Root for each copyMenuItems entry; wrap a single ActionMenu.Root
(instead of one per item) around the {`#each` copyMenuItems as copyItem} loop so
all ActionMenu.Item.Button elements live under the same ActionMenu.Root; keep
the existing use:melt={$copySubTrigger} and per-item on:m-click={() =>
onSelect(copyItem.action)} handlers inside each ActionMenu.Item.Button and
ensure you remove the inner ActionMenu.Roots so only the outer ActionMenu.Root
encloses the loop.

</div>
{:else}
<div
use:melt={$item}
style:display="contents"
on:m-click={() => onSelect(menuItem.action as RowCellAction)}>
<ActionMenu.Item.Button
leadingIcon={menuItem.icon}
status={menuItem.danger ? 'danger' : undefined}>
{menuItem.label}
</ActionMenu.Item.Button>
</div>
{/if}
{/each}
</ActionMenu.Root>
</div>

<!-- Copy Submenu (outside ActionMenu.Root, inside Card.Base) -->
<div class="menu submenu" use:melt={$copySubMenu}>
<Card.Base padding="none">
<div class="action-menu-root">
{#each copyMenuItems as copyItem}
<div use:melt={$item} on:m-click={() => onSelect(copyItem.action)}>
<ActionMenu.Root>
<ActionMenu.Item.Button leadingIcon={copyItem.icon}>
{copyItem.label}
</ActionMenu.Item.Button>
</ActionMenu.Root>
</div>
{/each}
</div>
</Card.Base>
</div>
</Card.Base>
</div>

<style>
.menu {
z-index: 50;
min-width: 220px;
outline: none;
border-radius: var(--border-radius-m);
box-shadow: var(--shadow-large);
}

.submenu {
margin-inline: -4px;
margin-block: -4px;
}

.action-menu-root {
margin-inline-start: calc(var(--space-2) * -1);

& :global(:first-child) {
overflow: visible;
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
| 'activity'
| 'copy-url'
| 'copy-json'
// | 'copy-snippet'
| 'copy-snippet'
| 'delete';
</script>

Expand Down
Loading
Loading