@postxl/generators 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/frontend-core/frontend.generator.d.ts +0 -58
- package/dist/frontend-core/frontend.generator.js +6 -172
- package/dist/frontend-core/frontend.generator.js.map +1 -1
- package/dist/frontend-core/template/README.md +1 -1
- package/dist/frontend-core/template/src/components/admin/table-filter.tsx +1 -5
- package/dist/frontend-core/template/src/components/ui/color-mode-toggle/color-mode-toggle.tsx +10 -4
- package/dist/frontend-core/template/src/pages/dashboard/dashboard.page.tsx +2 -3
- package/dist/frontend-core/template/src/pages/error/default-error.page.tsx +1 -1
- package/dist/frontend-core/template/src/pages/error/not-found-error.page.tsx +1 -1
- package/dist/frontend-core/template/src/styles/styles.css +13 -1
- package/dist/frontend-core/template/tsconfig.json +2 -0
- package/dist/frontend-core/types/component.d.ts +1 -1
- package/dist/frontend-forms/generators/discriminated-union/fields.generator.js +4 -6
- package/dist/frontend-forms/generators/discriminated-union/fields.generator.js.map +1 -1
- package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js +1 -1
- package/dist/frontend-forms/generators/discriminated-union/inputs.generator.js.map +1 -1
- package/dist/frontend-forms/generators/enum/inputs.generator.js +1 -1
- package/dist/frontend-forms/generators/enum/inputs.generator.js.map +1 -1
- package/dist/frontend-forms/generators/model/forms.generator.js +8 -12
- package/dist/frontend-forms/generators/model/forms.generator.js.map +1 -1
- package/dist/frontend-forms/generators/model/inputs.generator.js +2 -6
- package/dist/frontend-forms/generators/model/inputs.generator.js.map +1 -1
- package/dist/frontend-forms/template/src/components/ui/field/field.tsx +1 -4
- package/dist/frontend-tables/generators/model-table.generator.js +1 -5
- package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
- package/package.json +3 -2
- package/dist/frontend-core/template/src/components/ui/accordion/accordion.stories.tsx +0 -47
- package/dist/frontend-core/template/src/components/ui/accordion/accordion.tsx +0 -52
- package/dist/frontend-core/template/src/components/ui/admin-sidebar/admin-sidebar.tsx +0 -195
- package/dist/frontend-core/template/src/components/ui/alert/alert.stories.tsx +0 -61
- package/dist/frontend-core/template/src/components/ui/alert/alert.tsx +0 -45
- package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.stories.tsx +0 -52
- package/dist/frontend-core/template/src/components/ui/alert-dialog/alert-dialog.tsx +0 -105
- package/dist/frontend-core/template/src/components/ui/avatar/avatar.stories.tsx +0 -30
- package/dist/frontend-core/template/src/components/ui/avatar/avatar.tsx +0 -39
- package/dist/frontend-core/template/src/components/ui/badge/badge.stories.tsx +0 -78
- package/dist/frontend-core/template/src/components/ui/badge/badge.tsx +0 -48
- package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.stories.tsx +0 -67
- package/dist/frontend-core/template/src/components/ui/breadcrumb/breadcrumb.tsx +0 -85
- package/dist/frontend-core/template/src/components/ui/button/button.stories.tsx +0 -150
- package/dist/frontend-core/template/src/components/ui/button/button.tsx +0 -68
- package/dist/frontend-core/template/src/components/ui/calendar/calendar.stories.tsx +0 -160
- package/dist/frontend-core/template/src/components/ui/calendar/calendar.tsx +0 -293
- package/dist/frontend-core/template/src/components/ui/card/card.stories.tsx +0 -77
- package/dist/frontend-core/template/src/components/ui/card/card.tsx +0 -45
- package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.stories.tsx +0 -29
- package/dist/frontend-core/template/src/components/ui/card-hover/card-hover.tsx +0 -28
- package/dist/frontend-core/template/src/components/ui/carousel/carousel.stories.tsx +0 -154
- package/dist/frontend-core/template/src/components/ui/carousel/carousel.tsx +0 -227
- package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.stories.tsx +0 -106
- package/dist/frontend-core/template/src/components/ui/checkbox/checkbox.tsx +0 -88
- package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.stories.tsx +0 -90
- package/dist/frontend-core/template/src/components/ui/checkbox/shadcn-checkbox.tsx +0 -54
- package/dist/frontend-core/template/src/components/ui/collapse/collapse.stories.tsx +0 -52
- package/dist/frontend-core/template/src/components/ui/collapse/collapse.tsx +0 -9
- package/dist/frontend-core/template/src/components/ui/combobox/combobox.stories.tsx +0 -207
- package/dist/frontend-core/template/src/components/ui/combobox/combobox.tsx +0 -79
- package/dist/frontend-core/template/src/components/ui/command/command.stories.tsx +0 -186
- package/dist/frontend-core/template/src/components/ui/command/command.tsx +0 -165
- package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.stories.tsx +0 -160
- package/dist/frontend-core/template/src/components/ui/command-palette/command-palette.tsx +0 -134
- package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.stories.tsx +0 -198
- package/dist/frontend-core/template/src/components/ui/content-frame/content-frame.tsx +0 -100
- package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.stories.tsx +0 -78
- package/dist/frontend-core/template/src/components/ui/context-menu/context-menu.tsx +0 -179
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/cell-variant-types.ts +0 -11
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/checkbox-cell.tsx +0 -116
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/date-cell.tsx +0 -157
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/gantt-cell.tsx +0 -82
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/long-text-cell.tsx +0 -180
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/multi-select-cell.tsx +0 -280
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/number-cell.tsx +0 -169
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/react-node-cell.tsx +0 -33
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/select-cell.tsx +0 -175
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/short-text-cell.tsx +0 -138
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timeline.tsx +0 -92
- package/dist/frontend-core/template/src/components/ui/data-grid/cell-variants/utils/gantt-timerange-picker.tsx +0 -330
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell-wrapper.tsx +0 -212
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-cell.tsx +0 -157
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-column-header.tsx +0 -340
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-context-menu.tsx +0 -271
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-row.tsx +0 -123
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-search.tsx +0 -211
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-types.ts +0 -159
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-utils.ts +0 -67
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid-view-menu.tsx +0 -360
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.stories.tsx +0 -780
- package/dist/frontend-core/template/src/components/ui/data-grid/data-grid.tsx +0 -217
- package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-callback-ref.ts +0 -22
- package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-data-grid.tsx +0 -1892
- package/dist/frontend-core/template/src/components/ui/data-grid/hooks/use-debounced-callback.ts +0 -19
- package/dist/frontend-core/template/src/components/ui/data-grid/styles.css +0 -3
- package/dist/frontend-core/template/src/components/ui/data-table/context-menu-simple.tsx +0 -141
- package/dist/frontend-core/template/src/components/ui/data-table/data-table.stories.tsx +0 -146
- package/dist/frontend-core/template/src/components/ui/data-table/data-table.tsx +0 -447
- package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-array-cell-renderer.tsx +0 -77
- package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-cell-renderer.tsx +0 -56
- package/dist/frontend-core/template/src/components/ui/data-table/renderers/favorite-cell-renderer.tsx +0 -68
- package/dist/frontend-core/template/src/components/ui/data-table/renderers/links-cell-renderer.tsx +0 -205
- package/dist/frontend-core/template/src/components/ui/data-table/utils/columns.ts +0 -351
- package/dist/frontend-core/template/src/components/ui/data-table/utils/data-table.utils.ts +0 -49
- package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.stories.tsx +0 -149
- package/dist/frontend-core/template/src/components/ui/date-picker/date-picker.tsx +0 -30
- package/dist/frontend-core/template/src/components/ui/dialog/dialog.stories.tsx +0 -80
- package/dist/frontend-core/template/src/components/ui/dialog/dialog.tsx +0 -134
- package/dist/frontend-core/template/src/components/ui/drawer/drawer.stories.tsx +0 -104
- package/dist/frontend-core/template/src/components/ui/drawer/drawer.tsx +0 -87
- package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.stories.tsx +0 -168
- package/dist/frontend-core/template/src/components/ui/dropdown-menu/dropdown-menu.tsx +0 -225
- package/dist/frontend-core/template/src/components/ui/input/input.stories.tsx +0 -141
- package/dist/frontend-core/template/src/components/ui/input/input.tsx +0 -47
- package/dist/frontend-core/template/src/components/ui/label/label.stories.tsx +0 -41
- package/dist/frontend-core/template/src/components/ui/label/label.tsx +0 -20
- package/dist/frontend-core/template/src/components/ui/loader/loader.stories.tsx +0 -45
- package/dist/frontend-core/template/src/components/ui/loader/loader.tsx +0 -17
- package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.stories.tsx +0 -114
- package/dist/frontend-core/template/src/components/ui/mark-value-renderer/mark-value-renderer.tsx +0 -48
- package/dist/frontend-core/template/src/components/ui/menubar/menu.stories.tsx +0 -134
- package/dist/frontend-core/template/src/components/ui/menubar/menubar.tsx +0 -208
- package/dist/frontend-core/template/src/components/ui/modal/modal.stories.tsx +0 -297
- package/dist/frontend-core/template/src/components/ui/modal/modal.tsx +0 -80
- package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.stories.tsx +0 -213
- package/dist/frontend-core/template/src/components/ui/navigation-menu/navigation-menu.tsx +0 -142
- package/dist/frontend-core/template/src/components/ui/pagination/pagination.stories.tsx +0 -49
- package/dist/frontend-core/template/src/components/ui/pagination/pagination.tsx +0 -84
- package/dist/frontend-core/template/src/components/ui/popover/popover.stories.tsx +0 -82
- package/dist/frontend-core/template/src/components/ui/popover/popover.tsx +0 -55
- package/dist/frontend-core/template/src/components/ui/progress/progress.stories.tsx +0 -80
- package/dist/frontend-core/template/src/components/ui/progress/progress.tsx +0 -17
- package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.stories.tsx +0 -154
- package/dist/frontend-core/template/src/components/ui/radio-group/radio-group.tsx +0 -68
- package/dist/frontend-core/template/src/components/ui/resizable/resizable.stories.tsx +0 -73
- package/dist/frontend-core/template/src/components/ui/resizable/resizeable.tsx +0 -38
- package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.stories.tsx +0 -55
- package/dist/frontend-core/template/src/components/ui/scroll-area/scroll-area.tsx +0 -39
- package/dist/frontend-core/template/src/components/ui/select/select.stories.tsx +0 -297
- package/dist/frontend-core/template/src/components/ui/select/select.tsx +0 -227
- package/dist/frontend-core/template/src/components/ui/separator/separator.tsx +0 -21
- package/dist/frontend-core/template/src/components/ui/separator/seperator.stories.tsx +0 -25
- package/dist/frontend-core/template/src/components/ui/sheet/sheet.stories.tsx +0 -45
- package/dist/frontend-core/template/src/components/ui/sheet/sheet.tsx +0 -107
- package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.stories.tsx +0 -26
- package/dist/frontend-core/template/src/components/ui/skeleton/skeleton.tsx +0 -7
- package/dist/frontend-core/template/src/components/ui/slider/slider.stories.tsx +0 -101
- package/dist/frontend-core/template/src/components/ui/slider/slider.tsx +0 -98
- package/dist/frontend-core/template/src/components/ui/spinner/spinner.stories.tsx +0 -19
- package/dist/frontend-core/template/src/components/ui/spinner/spinner.tsx +0 -21
- package/dist/frontend-core/template/src/components/ui/switch/switch.stories.tsx +0 -33
- package/dist/frontend-core/template/src/components/ui/switch/switch.tsx +0 -28
- package/dist/frontend-core/template/src/components/ui/tabs/tabs.stories.tsx +0 -215
- package/dist/frontend-core/template/src/components/ui/tabs/tabs.tsx +0 -70
- package/dist/frontend-core/template/src/components/ui/textarea/textarea.stories.tsx +0 -138
- package/dist/frontend-core/template/src/components/ui/textarea/textarea.tsx +0 -40
- package/dist/frontend-core/template/src/components/ui/toast/toast.mdx +0 -31
- package/dist/frontend-core/template/src/components/ui/toast/toast.stories.tsx +0 -89
- package/dist/frontend-core/template/src/components/ui/toggle/toggle.stories.tsx +0 -65
- package/dist/frontend-core/template/src/components/ui/toggle/toggle.tsx +0 -38
- package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.stories.tsx +0 -85
- package/dist/frontend-core/template/src/components/ui/toggle-group/toggle-group.tsx +0 -54
- package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.stories.tsx +0 -29
- package/dist/frontend-core/template/src/components/ui/tooltip/tooltip.tsx +0 -29
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
import '@glideapps/glide-data-grid/dist/index.css'
|
|
2
|
-
|
|
3
|
-
import { type Theme as ContextTheme, useTheme } from '@context-providers/theme-context-provider'
|
|
4
|
-
import {
|
|
5
|
-
type CellClickedEventArgs,
|
|
6
|
-
DataEditor,
|
|
7
|
-
type DataEditorProps,
|
|
8
|
-
type DataEditorRef,
|
|
9
|
-
type GridCell,
|
|
10
|
-
GridCellKind,
|
|
11
|
-
type GridColumn,
|
|
12
|
-
type GridMouseEventArgs,
|
|
13
|
-
type Item,
|
|
14
|
-
type LoadingCell,
|
|
15
|
-
type Rectangle,
|
|
16
|
-
Theme,
|
|
17
|
-
} from '@glideapps/glide-data-grid'
|
|
18
|
-
import { ParentSize } from '@visx/responsive'
|
|
19
|
-
|
|
20
|
-
import { type Dispatch, ReactNode, type SetStateAction, useCallback, useEffect, useRef, useState } from 'react'
|
|
21
|
-
|
|
22
|
-
import { Loader } from '@components/ui/loader/loader'
|
|
23
|
-
|
|
24
|
-
import { countryArrayDataTableRenderer } from './renderers/country-array-cell-renderer'
|
|
25
|
-
import { countryDataTableRenderer } from './renderers/country-cell-renderer'
|
|
26
|
-
import { favoriteDataTableRenderer } from './renderers/favorite-cell-renderer'
|
|
27
|
-
import { linksTableRenderer } from './renderers/links-cell-renderer'
|
|
28
|
-
import { getTableTheme, isHoverableRenderer } from './utils/data-table.utils'
|
|
29
|
-
import type { ContextMenuAction, ContextMenuConfig } from './context-menu-simple'
|
|
30
|
-
|
|
31
|
-
export type TableSort<FieldName extends string> = {
|
|
32
|
-
field: FieldName
|
|
33
|
-
order: 'asc' | 'desc' | null
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type DataTableProps<T extends object, ColumnId extends string> = {
|
|
37
|
-
data: T[]
|
|
38
|
-
columns: DataTableColumn<T, ColumnId>[]
|
|
39
|
-
totalRows?: number
|
|
40
|
-
onEndReached?: () => void
|
|
41
|
-
sortHandler?: (columnId: ColumnId, sort: 'asc' | 'desc' | null) => void
|
|
42
|
-
freezeColumns?: number
|
|
43
|
-
currentSort?: TableSort<ColumnId>
|
|
44
|
-
isLoading?: boolean
|
|
45
|
-
isLoadingLabel?: string
|
|
46
|
-
hasHeaderGroup?: boolean
|
|
47
|
-
footer?: ReactNode
|
|
48
|
-
/**
|
|
49
|
-
* E2E test_id to identify the button.
|
|
50
|
-
*/
|
|
51
|
-
__e2e_test_id__?: string
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Callback function to be called when the user right-clicks on a cell.
|
|
55
|
-
* This function should be used to show and position the context menu.
|
|
56
|
-
*/
|
|
57
|
-
setContextMenu?: Dispatch<SetStateAction<ContextMenuConfig>>
|
|
58
|
-
} & Omit<DataEditorProps, 'columns' | 'rows' | 'getCellContent'>
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* We take the `GridColumn` type from the `@glideapps/glide-data-grid` so the
|
|
62
|
-
* user can use all of its props and extend it to include the `toCell`,
|
|
63
|
-
* `onFilter`, and `onSort` functions.
|
|
64
|
-
*/
|
|
65
|
-
export type DataTableColumn<Row extends object, ColumnId extends string> = GridColumn & {
|
|
66
|
-
id: ColumnId
|
|
67
|
-
|
|
68
|
-
tooltip?: string
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* We use this to render the cell content to the column. This then uses the
|
|
72
|
-
* `GridCell` type to render the content/data.
|
|
73
|
-
*/
|
|
74
|
-
toCell: (item: Row) => GridCell
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The function that performs an action when the user clicks on a cell in this column.
|
|
78
|
-
*/
|
|
79
|
-
onClick?: (row: Row, event: CellClickedEventArgs) => void
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* The function that returns context actions for a given row. Will be called when a cell in this column is right-clicked.
|
|
83
|
-
*/
|
|
84
|
-
onContextMenu?: OnContextMenuCallback<Row>
|
|
85
|
-
|
|
86
|
-
sortComparator: (row1: Row, row2: Row, direction: 'asc' | 'desc' | null) => number
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export type OnContextMenuCallback<Row> = (row: Row, event: CellClickedEventArgs) => ContextMenuAction[]
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* We take the `GridColumn` type from the `@glideapps/glide-data-grid` so the
|
|
93
|
-
* user can use all of its props and extend it to include the `toCell`,
|
|
94
|
-
* `onFilter`, and `onSort` functions.
|
|
95
|
-
*/
|
|
96
|
-
export type DataGridColumn<Row extends object, ColumnId extends string> = GridColumn & {
|
|
97
|
-
id: ColumnId
|
|
98
|
-
/**
|
|
99
|
-
* We use this to render the cell content to the column. This then uses the
|
|
100
|
-
* `GridCell` type to render the content/data.
|
|
101
|
-
*/
|
|
102
|
-
toCell: (item: Row) => GridCell
|
|
103
|
-
|
|
104
|
-
onClick?: (rowData: Row, event: CellClickedEventArgs) => void
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
type ManagedDataTableColumn<Row extends object, ColumnId extends string> = DataTableColumn<Row, ColumnId> & {
|
|
108
|
-
activeSort: 'asc' | 'desc' | null
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function DataTable<Row extends object, ColumnId extends string = string>({
|
|
112
|
-
data,
|
|
113
|
-
columns,
|
|
114
|
-
onEndReached,
|
|
115
|
-
sortHandler,
|
|
116
|
-
freezeColumns,
|
|
117
|
-
currentSort,
|
|
118
|
-
__e2e_test_id__,
|
|
119
|
-
isLoading,
|
|
120
|
-
isLoadingLabel = 'Loading...',
|
|
121
|
-
hasHeaderGroup,
|
|
122
|
-
footer,
|
|
123
|
-
setContextMenu,
|
|
124
|
-
...delegated
|
|
125
|
-
}: DataTableProps<Row, ColumnId>) {
|
|
126
|
-
const sortOrder = currentSort?.order === 'asc' ? 'sortAscending' : 'sortDescending'
|
|
127
|
-
const [managedColumns, setManagedColumns] = useState<ManagedDataTableColumn<Row, ColumnId>[]>([])
|
|
128
|
-
const { brand, colorMode } = useTheme()
|
|
129
|
-
const theme: ContextTheme = `${brand} ${colorMode}`
|
|
130
|
-
const [tableTheme, setTableTheme] = useState<Partial<Theme> | undefined>()
|
|
131
|
-
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
const observer = new MutationObserver(() => {
|
|
134
|
-
setTableTheme(getTableTheme(theme))
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['style', 'class'] })
|
|
138
|
-
|
|
139
|
-
return () => observer.disconnect()
|
|
140
|
-
}, [theme])
|
|
141
|
-
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
setManagedColumns(
|
|
144
|
-
columns.map((col) => ({
|
|
145
|
-
...col,
|
|
146
|
-
activeSort: currentSort?.field === col.id ? currentSort.order : null,
|
|
147
|
-
isDraggable: true,
|
|
148
|
-
icon: currentSort?.field === col.id ? sortOrder : undefined,
|
|
149
|
-
})),
|
|
150
|
-
)
|
|
151
|
-
}, [columns, currentSort, sortOrder])
|
|
152
|
-
|
|
153
|
-
const [visibleRegion, setVisibleRegion] = useState<Rectangle>({
|
|
154
|
-
x: 0,
|
|
155
|
-
y: 0,
|
|
156
|
-
width: 0,
|
|
157
|
-
height: 0,
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
// const dataEditorTheme: Partial<Theme> = useMemo(() => getTableTheme(), [theme === 'dark'])
|
|
161
|
-
|
|
162
|
-
useEffect(() => {
|
|
163
|
-
if (data.length > 1 && visibleRegion.y + visibleRegion.height > data.length - 20) {
|
|
164
|
-
onEndReached?.()
|
|
165
|
-
}
|
|
166
|
-
}, [visibleRegion, onEndReached, data.length])
|
|
167
|
-
|
|
168
|
-
const getRowContent = useCallback(
|
|
169
|
-
(row: number) => {
|
|
170
|
-
return data[row]
|
|
171
|
-
},
|
|
172
|
-
[data],
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
const getCellContent = useCallback(
|
|
176
|
-
([col, row]: Item) => {
|
|
177
|
-
const rowData = getRowContent(row)
|
|
178
|
-
|
|
179
|
-
if (!rowData) {
|
|
180
|
-
return { kind: GridCellKind.Loading, allowOverlay: true } satisfies LoadingCell
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const toCell = managedColumns[col].toCell
|
|
184
|
-
|
|
185
|
-
return toCell(rowData)
|
|
186
|
-
},
|
|
187
|
-
[getRowContent, managedColumns],
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
const dataEditorRef = useRef<DataEditorRef>(null)
|
|
191
|
-
const tableWrapperRef = useRef<HTMLDivElement>(null)
|
|
192
|
-
|
|
193
|
-
const onRightClick = useCallback(
|
|
194
|
-
([col, row]: Item, event: CellClickedEventArgs) => {
|
|
195
|
-
if (!dataEditorRef?.current) {
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
const cellBounds: Rectangle | undefined = dataEditorRef.current.getBounds(col, row)
|
|
199
|
-
if (!cellBounds) {
|
|
200
|
-
return
|
|
201
|
-
}
|
|
202
|
-
const position = {
|
|
203
|
-
x: cellBounds.x + event.localEventX,
|
|
204
|
-
y: cellBounds.y + event.localEventY,
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Uncomment to get coordinates during test creation
|
|
208
|
-
// console.log('absolute', position)
|
|
209
|
-
// console.log('relative', { x: event.localEventX, y: event.localEventY })
|
|
210
|
-
|
|
211
|
-
if (!setContextMenu) {
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
const rowData = getRowContent(row)
|
|
215
|
-
if (!rowData) {
|
|
216
|
-
return { kind: GridCellKind.Loading, allowOverlay: true } satisfies LoadingCell
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const onContextMenuFn = managedColumns[col].onContextMenu
|
|
220
|
-
if (!onContextMenuFn) {
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const actions = onContextMenuFn(rowData, event)
|
|
225
|
-
if (!actions) {
|
|
226
|
-
return
|
|
227
|
-
}
|
|
228
|
-
event.preventDefault()
|
|
229
|
-
|
|
230
|
-
setContextMenu({
|
|
231
|
-
position,
|
|
232
|
-
actions,
|
|
233
|
-
})
|
|
234
|
-
},
|
|
235
|
-
[dataEditorRef, setContextMenu, getRowContent, managedColumns],
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
const [hoveredCellData, setHoveredCellData] = useState<{
|
|
239
|
-
x: number
|
|
240
|
-
y: number
|
|
241
|
-
data: any
|
|
242
|
-
hoverOverlay?: (data: any) => React.ReactNode
|
|
243
|
-
} | null>(null)
|
|
244
|
-
|
|
245
|
-
const [hoveredHeader, setHoveredHeader] = useState<{
|
|
246
|
-
columnId: number
|
|
247
|
-
x: number
|
|
248
|
-
y: number
|
|
249
|
-
} | null>(null)
|
|
250
|
-
const onItemHovered = useCallback(
|
|
251
|
-
(args: GridMouseEventArgs) => {
|
|
252
|
-
const tableBounds = tableWrapperRef.current?.getBoundingClientRect()
|
|
253
|
-
|
|
254
|
-
if (args.kind === 'cell') {
|
|
255
|
-
setHoveredHeader(null)
|
|
256
|
-
const [col, row] = args.location
|
|
257
|
-
const cell = getCellContent([col, row])
|
|
258
|
-
if (!cell || cell.kind !== GridCellKind.Custom) {
|
|
259
|
-
setHoveredCellData(null)
|
|
260
|
-
return
|
|
261
|
-
}
|
|
262
|
-
const x = args.bounds.x - (tableBounds?.x ?? 0) + 10
|
|
263
|
-
const y = args.bounds.y - (tableBounds?.y ?? 0) + args.bounds.height
|
|
264
|
-
const hoverableRenderers = [countryArrayDataTableRenderer]
|
|
265
|
-
const renderer = hoverableRenderers.find((r) => r.isMatch(cell))
|
|
266
|
-
const data = cell.data
|
|
267
|
-
const hoverOverlay = isHoverableRenderer(renderer) ? renderer.hoverOverlay : undefined
|
|
268
|
-
|
|
269
|
-
setHoveredCellData({ x, y, data, hoverOverlay })
|
|
270
|
-
return
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (args.kind === 'header') {
|
|
274
|
-
setHoveredCellData(null)
|
|
275
|
-
const [columnId] = args.location
|
|
276
|
-
const x = args.bounds.x - (tableBounds?.x ?? 0) + 10
|
|
277
|
-
const y = args.bounds.height
|
|
278
|
-
|
|
279
|
-
setHoveredHeader({ columnId, x, y: y + (hasHeaderGroup ? 20 : 0) })
|
|
280
|
-
return
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
setHoveredCellData(null)
|
|
284
|
-
setHoveredHeader(null)
|
|
285
|
-
},
|
|
286
|
-
[getCellContent, hasHeaderGroup],
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
const onHeaderMenuClick = useCallback(
|
|
290
|
-
(colIndex: number) => {
|
|
291
|
-
if (!sortHandler) {
|
|
292
|
-
// If sorting is not supported, do nothing on header click
|
|
293
|
-
return
|
|
294
|
-
}
|
|
295
|
-
setManagedColumns((cols) =>
|
|
296
|
-
cols.map((col, i) => {
|
|
297
|
-
if (i !== colIndex) {
|
|
298
|
-
return { ...col, activeSort: null, icon: undefined }
|
|
299
|
-
}
|
|
300
|
-
let newSort: 'asc' | 'desc' | null
|
|
301
|
-
let icon: 'sortAscending' | 'sortDescending' | undefined = undefined
|
|
302
|
-
|
|
303
|
-
switch (col.activeSort) {
|
|
304
|
-
case null:
|
|
305
|
-
newSort = 'asc'
|
|
306
|
-
icon = 'sortAscending'
|
|
307
|
-
break
|
|
308
|
-
case 'asc':
|
|
309
|
-
newSort = 'desc'
|
|
310
|
-
icon = 'sortDescending'
|
|
311
|
-
break
|
|
312
|
-
case 'desc':
|
|
313
|
-
newSort = null
|
|
314
|
-
icon = undefined
|
|
315
|
-
break
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
console.log(`Column ${col.id} sort state: ${col.activeSort} -> ${newSort}`)
|
|
319
|
-
|
|
320
|
-
if (sortHandler) {
|
|
321
|
-
sortHandler(col.id, newSort)
|
|
322
|
-
}
|
|
323
|
-
return { ...col, activeSort: newSort, icon }
|
|
324
|
-
}),
|
|
325
|
-
)
|
|
326
|
-
},
|
|
327
|
-
[sortHandler],
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
const onCellClicked = useCallback(
|
|
331
|
-
([col, row]: Item, event: CellClickedEventArgs) => {
|
|
332
|
-
const rowData = getRowContent(row)
|
|
333
|
-
const columnId = managedColumns[col].id
|
|
334
|
-
const columnDefinition = managedColumns.find((col) => col.id === columnId)!
|
|
335
|
-
if (columnDefinition.onClick) {
|
|
336
|
-
columnDefinition.onClick(rowData, event)
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
|
-
[getRowContent, managedColumns],
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
const onColumnResize = useCallback(
|
|
343
|
-
({ id }: { id?: string }, width: number) => {
|
|
344
|
-
setManagedColumns((oldColumns) =>
|
|
345
|
-
oldColumns.map((oldColumn) => (oldColumn.id === id ? { ...oldColumn, width } : oldColumn)),
|
|
346
|
-
)
|
|
347
|
-
},
|
|
348
|
-
[setManagedColumns],
|
|
349
|
-
)
|
|
350
|
-
return (
|
|
351
|
-
<ParentSize className="flex h-full min-h-[inherit] relative">
|
|
352
|
-
{({ height }) => (
|
|
353
|
-
<div
|
|
354
|
-
className={`h-[${height}px] w-full flex flex-col relative`}
|
|
355
|
-
ref={tableWrapperRef}
|
|
356
|
-
data-test-id={__e2e_test_id__}
|
|
357
|
-
>
|
|
358
|
-
<DataEditor
|
|
359
|
-
ref={dataEditorRef}
|
|
360
|
-
freezeColumns={freezeColumns ?? 0}
|
|
361
|
-
customRenderers={[
|
|
362
|
-
linksTableRenderer,
|
|
363
|
-
countryDataTableRenderer,
|
|
364
|
-
countryArrayDataTableRenderer,
|
|
365
|
-
favoriteDataTableRenderer,
|
|
366
|
-
]}
|
|
367
|
-
columns={managedColumns}
|
|
368
|
-
getCellContent={getCellContent}
|
|
369
|
-
rows={data.length}
|
|
370
|
-
onHeaderClicked={onHeaderMenuClick}
|
|
371
|
-
onCellActivated={(cell) => console.log('cell activated', cell)}
|
|
372
|
-
onItemHovered={onItemHovered}
|
|
373
|
-
onHeaderMenuClick={onHeaderMenuClick}
|
|
374
|
-
onColumnResize={onColumnResize}
|
|
375
|
-
onCellClicked={onCellClicked}
|
|
376
|
-
verticalBorder={false}
|
|
377
|
-
headerIcons={{ sortAscending, sortDescending, noSort }}
|
|
378
|
-
{...delegated}
|
|
379
|
-
onCellContextMenu={onRightClick}
|
|
380
|
-
theme={tableTheme}
|
|
381
|
-
onVisibleRegionChanged={setVisibleRegion}
|
|
382
|
-
rowHeight={22}
|
|
383
|
-
headerHeight={22}
|
|
384
|
-
smoothScrollX
|
|
385
|
-
smoothScrollY
|
|
386
|
-
/>
|
|
387
|
-
|
|
388
|
-
{hoveredHeader && managedColumns[hoveredHeader.columnId]?.tooltip && (
|
|
389
|
-
<div
|
|
390
|
-
style={
|
|
391
|
-
{
|
|
392
|
-
'--header-x': `${hoveredHeader.x}px`,
|
|
393
|
-
'--header-y': `${hoveredHeader.y}px`,
|
|
394
|
-
} as React.CSSProperties
|
|
395
|
-
}
|
|
396
|
-
className={
|
|
397
|
-
'absolute left-(--header-x) top-(--header-y) bg-background p-2 m-0.5 border border-(--discreet-border) shadow-md max-w-[400px]'
|
|
398
|
-
}
|
|
399
|
-
dangerouslySetInnerHTML={{ __html: managedColumns[hoveredHeader.columnId].tooltip ?? '' }}
|
|
400
|
-
/>
|
|
401
|
-
)}
|
|
402
|
-
|
|
403
|
-
{hoveredCellData?.hoverOverlay && (
|
|
404
|
-
<div
|
|
405
|
-
style={
|
|
406
|
-
{
|
|
407
|
-
'--header-x': `${hoveredCellData.x}px`,
|
|
408
|
-
'--header-y': `${hoveredCellData.y}px`,
|
|
409
|
-
} as React.CSSProperties
|
|
410
|
-
}
|
|
411
|
-
className={
|
|
412
|
-
'absolute left-(--header-x) top-(--header-y) bg-background p-2 m-0.5 border border-(--discreet-border) shadow-md max-w-[400px]'
|
|
413
|
-
}
|
|
414
|
-
>
|
|
415
|
-
{hoveredCellData.hoverOverlay(hoveredCellData.data)}
|
|
416
|
-
</div>
|
|
417
|
-
)}
|
|
418
|
-
|
|
419
|
-
{footer ? (
|
|
420
|
-
<div className="flex absolute bottom-0 w-full items-center px-1 py-4 font-bold bg-background text-muted-foreground">
|
|
421
|
-
{footer}
|
|
422
|
-
</div>
|
|
423
|
-
) : null}
|
|
424
|
-
|
|
425
|
-
{isLoading && <Loader label={isLoadingLabel} />}
|
|
426
|
-
</div>
|
|
427
|
-
)}
|
|
428
|
-
</ParentSize>
|
|
429
|
-
)
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// CUSTOM ICONS ----------------------------------------------------------------
|
|
433
|
-
|
|
434
|
-
const noSort = () => `
|
|
435
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
|
436
|
-
<path transform="scale(0.75)" stroke-linecap="round" stroke-linejoin="round" d="M3 7.5 7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5" />
|
|
437
|
-
</svg>
|
|
438
|
-
`
|
|
439
|
-
|
|
440
|
-
const sortAscending = () => `
|
|
441
|
-
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 9H11L7.5 4.5L4 9Z" fill="currentColor"></path></svg>
|
|
442
|
-
|
|
443
|
-
`
|
|
444
|
-
|
|
445
|
-
const sortDescending = () => `
|
|
446
|
-
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6H11L7.5 10.5L4 6Z" fill="currentColor"></path></svg>
|
|
447
|
-
`
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { CustomCell, CustomRenderer } from '@glideapps/glide-data-grid'
|
|
2
|
-
import { GridCellKind } from '@glideapps/glide-data-grid'
|
|
3
|
-
|
|
4
|
-
import { ReactNode } from 'react'
|
|
5
|
-
|
|
6
|
-
import { APP_CONFIG } from '@lib/config'
|
|
7
|
-
|
|
8
|
-
export type HoverableRenderer<T extends CustomCell = CustomCell> = {
|
|
9
|
-
hoverOverlay?: (cellData: T['data']) => ReactNode
|
|
10
|
-
} & CustomRenderer<T>
|
|
11
|
-
|
|
12
|
-
export type CountryArrayCell = CustomCell<{
|
|
13
|
-
kind: 'country-array-cell'
|
|
14
|
-
countries: { id: string; label: string; hoverLabel?: string }[]
|
|
15
|
-
}>
|
|
16
|
-
|
|
17
|
-
const countryImageCache: Record<string, HTMLImageElement> = {}
|
|
18
|
-
|
|
19
|
-
export const countryArrayDataTableRenderer: HoverableRenderer<CountryArrayCell> = {
|
|
20
|
-
kind: GridCellKind.Custom,
|
|
21
|
-
isMatch: (cell): cell is CountryArrayCell => (cell.data as { kind?: string }).kind === 'country-array-cell',
|
|
22
|
-
draw: ({ ctx, rect, theme }, { data: { countries } }) => {
|
|
23
|
-
const flagHeight = rect.height - theme.cellVerticalPadding * 2
|
|
24
|
-
const flagWidth = flagHeight * 1.5 // flags are 3:2 aspect ratio
|
|
25
|
-
|
|
26
|
-
let x = rect.x + theme.cellHorizontalPadding
|
|
27
|
-
const y = rect.y + theme.cellVerticalPadding
|
|
28
|
-
|
|
29
|
-
for (const { id, label } of countries) {
|
|
30
|
-
const isUnknown = !id || id === ''
|
|
31
|
-
|
|
32
|
-
let image: HTMLImageElement | undefined
|
|
33
|
-
|
|
34
|
-
if (!isUnknown) {
|
|
35
|
-
if (!countryImageCache[id]) {
|
|
36
|
-
countryImageCache[id] = new Image()
|
|
37
|
-
countryImageCache[id].src = `/flags/${id}.svg`
|
|
38
|
-
countryImageCache[id].onload = () => {
|
|
39
|
-
ctx.canvas.dispatchEvent(new Event('invalidate'))
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
image = countryImageCache[id]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const circlepath = new Path2D()
|
|
46
|
-
circlepath.roundRect(x, y, flagWidth, flagHeight, 2)
|
|
47
|
-
ctx.save()
|
|
48
|
-
ctx.clip(circlepath)
|
|
49
|
-
|
|
50
|
-
if (isUnknown || !image || !image.complete || image.naturalHeight === 0 || APP_CONFIG.useTestAssets) {
|
|
51
|
-
// Draw grey rectangle for unknown country
|
|
52
|
-
ctx.fillStyle = '#cccccc'
|
|
53
|
-
ctx.fillRect(x, y, flagWidth, flagHeight)
|
|
54
|
-
} else {
|
|
55
|
-
ctx.drawImage(image, x, y, flagWidth, flagHeight)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
ctx.restore()
|
|
59
|
-
|
|
60
|
-
ctx.save()
|
|
61
|
-
ctx.font = `${theme.baseFontFull} ${theme.fontFamily}`
|
|
62
|
-
ctx.fillStyle = theme.textDark
|
|
63
|
-
ctx.fillText(label, x + flagWidth + 5, y + flagHeight / 2)
|
|
64
|
-
ctx.restore()
|
|
65
|
-
|
|
66
|
-
const labelWidth = ctx.measureText(label).width
|
|
67
|
-
x += flagWidth + 5 + labelWidth + 10
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return true
|
|
71
|
-
},
|
|
72
|
-
needsHover: true,
|
|
73
|
-
hoverOverlay: (data) => {
|
|
74
|
-
const { countries } = data
|
|
75
|
-
return <>{countries.map((c) => c.hoverLabel || c.label).join(', ')}</>
|
|
76
|
-
},
|
|
77
|
-
}
|
package/dist/frontend-core/template/src/components/ui/data-table/renderers/country-cell-renderer.tsx
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { CustomCell, CustomRenderer } from '@glideapps/glide-data-grid'
|
|
2
|
-
import { GridCellKind } from '@glideapps/glide-data-grid'
|
|
3
|
-
|
|
4
|
-
import { APP_CONFIG } from '@lib/config'
|
|
5
|
-
|
|
6
|
-
export type CountryCell = CustomCell<{ kind: 'country'; name: string; code: string }>
|
|
7
|
-
|
|
8
|
-
const countryImageCache: Record<string, HTMLImageElement> = {}
|
|
9
|
-
|
|
10
|
-
export const countryDataTableRenderer: CustomRenderer<CountryCell> = {
|
|
11
|
-
kind: GridCellKind.Custom,
|
|
12
|
-
isMatch: (cell): cell is CountryCell => (cell.data as { kind?: string }).kind === 'country',
|
|
13
|
-
draw: ({ ctx, rect, theme }, { data: { name, code } }) => {
|
|
14
|
-
if (!code || code === '') {
|
|
15
|
-
return false
|
|
16
|
-
}
|
|
17
|
-
const flagHeight = rect.height - theme.cellVerticalPadding * 2
|
|
18
|
-
const flagWidth = flagHeight * 1.5 // flags are 3:2 aspect ratio
|
|
19
|
-
|
|
20
|
-
const top = rect.y + theme.cellVerticalPadding
|
|
21
|
-
const left = rect.x + theme.cellHorizontalPadding
|
|
22
|
-
|
|
23
|
-
if (!countryImageCache[code]) {
|
|
24
|
-
countryImageCache[code] = new Image()
|
|
25
|
-
countryImageCache[code].src = `/flags/${code}.svg`
|
|
26
|
-
countryImageCache[code].onload = () => {
|
|
27
|
-
ctx.canvas.dispatchEvent(new Event('invalidate'))
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const image = countryImageCache[code]
|
|
32
|
-
|
|
33
|
-
if (image.complete && image.naturalHeight !== 0 && !APP_CONFIG.useTestAssets) {
|
|
34
|
-
const circlepath = new Path2D()
|
|
35
|
-
circlepath.roundRect(left, top, flagWidth, flagHeight, 2)
|
|
36
|
-
|
|
37
|
-
ctx.save()
|
|
38
|
-
ctx.clip(circlepath)
|
|
39
|
-
ctx.drawImage(image, left, top, flagWidth, flagHeight)
|
|
40
|
-
ctx.restore()
|
|
41
|
-
} else {
|
|
42
|
-
ctx.save()
|
|
43
|
-
ctx.fillStyle = theme.textLight
|
|
44
|
-
ctx.fillRect(left, top, flagWidth, flagHeight)
|
|
45
|
-
ctx.restore()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
ctx.save()
|
|
49
|
-
ctx.font = `${theme.baseFontFull} ${theme.fontFamily}`
|
|
50
|
-
ctx.fillStyle = theme.textDark
|
|
51
|
-
ctx.fillText(name, left + flagWidth + 5, top + flagHeight / 2)
|
|
52
|
-
ctx.restore()
|
|
53
|
-
|
|
54
|
-
return true
|
|
55
|
-
},
|
|
56
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import type { CustomCell, CustomRenderer } from '@glideapps/glide-data-grid'
|
|
2
|
-
import { GridCellKind } from '@glideapps/glide-data-grid'
|
|
3
|
-
|
|
4
|
-
export type FavoriteCell = CustomCell<{ kind: 'favorite'; isFavorite: boolean; isSelected?: boolean }>
|
|
5
|
-
|
|
6
|
-
export const favoriteDataTableRenderer: CustomRenderer<FavoriteCell> = {
|
|
7
|
-
kind: GridCellKind.Custom,
|
|
8
|
-
isMatch: (cell): cell is FavoriteCell => (cell.data as { kind?: string }).kind === 'favorite',
|
|
9
|
-
draw: ({ ctx, rect, theme }, { data: { isFavorite, isSelected } }) => {
|
|
10
|
-
const size = Math.min(rect.width, rect.height) - theme.cellVerticalPadding * 2
|
|
11
|
-
const centerX = rect.x + rect.width / 2
|
|
12
|
-
const centerY = rect.y + rect.height / 2
|
|
13
|
-
const scale = size / 24 // Lucide icons are typically designed on a 24x24 grid
|
|
14
|
-
|
|
15
|
-
// Save the current context state
|
|
16
|
-
ctx.save()
|
|
17
|
-
|
|
18
|
-
// Center and scale the coordinate system
|
|
19
|
-
ctx.translate(centerX, centerY)
|
|
20
|
-
ctx.scale(scale, scale)
|
|
21
|
-
|
|
22
|
-
// Draw the star path following Lucide.dev's star shape
|
|
23
|
-
ctx.beginPath()
|
|
24
|
-
|
|
25
|
-
// These points create a star path similar to Lucide's design
|
|
26
|
-
// Star points in a 24x24 grid centered at 0,0
|
|
27
|
-
const points = [
|
|
28
|
-
[0, -10], // Top point
|
|
29
|
-
[2.9, -3.1], // Upper right connecting point
|
|
30
|
-
[10, -3.1], // Right point
|
|
31
|
-
[3.8, 1.2], // Lower right connecting point
|
|
32
|
-
[6.2, 8.2], // Lower right point
|
|
33
|
-
[0, 4.4], // Bottom connecting point
|
|
34
|
-
[-6.2, 8.2], // Lower left point
|
|
35
|
-
[-3.8, 1.2], // Lower left connecting point
|
|
36
|
-
[-10, -3.1], // Left point
|
|
37
|
-
[-2.9, -3.1], // Upper left connecting point
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
// Draw the star path
|
|
41
|
-
ctx.moveTo(points[0][0], points[0][1])
|
|
42
|
-
for (let i = 1; i < points.length; i++) {
|
|
43
|
-
ctx.lineTo(points[i][0], points[i][1])
|
|
44
|
-
}
|
|
45
|
-
ctx.closePath()
|
|
46
|
-
|
|
47
|
-
// Fill or just stroke based on favourite state
|
|
48
|
-
if (isFavorite) {
|
|
49
|
-
// Lucide uses a yellow/gold fill when active
|
|
50
|
-
ctx.fillStyle = '#FFD700' // Gold color for filled star
|
|
51
|
-
ctx.fill()
|
|
52
|
-
ctx.lineWidth = 2
|
|
53
|
-
ctx.strokeStyle = isSelected ? '#fb4b28' : '#D4AF37'
|
|
54
|
-
} else {
|
|
55
|
-
// When not favorited, only draw the outline
|
|
56
|
-
ctx.lineWidth = isSelected ? 2 : 1 // Thinner line for cleaner look
|
|
57
|
-
ctx.strokeStyle = isSelected ? '#fb4b28' : theme.textMedium
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Always draw the stroke
|
|
61
|
-
ctx.stroke()
|
|
62
|
-
|
|
63
|
-
// Restore the context state
|
|
64
|
-
ctx.restore()
|
|
65
|
-
|
|
66
|
-
return true
|
|
67
|
-
},
|
|
68
|
-
}
|