@anymux/ui-kit 0.1.0 → 0.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/{calendar-DSlrbHoj.js → calendar-DQKfYSQS.js} +48 -45
- package/dist/calendar-DQKfYSQS.js.map +1 -0
- package/dist/calendar.d.ts +1 -1
- package/dist/calendar.js +1 -1
- package/dist/{contacts-DQXTZzHc.js → contacts-By9Wg3kn.js} +35 -33
- package/dist/contacts-By9Wg3kn.js.map +1 -0
- package/dist/contacts.d.ts +1 -1
- package/dist/contacts.js +1 -1
- package/dist/{file-browser-m5atC3kF.js → file-browser-CkhNwADU.js} +61 -133
- package/dist/file-browser-CkhNwADU.js.map +1 -0
- package/dist/file-browser.d.ts +6 -6
- package/dist/file-browser.js +4 -4
- package/dist/{git-B55e6LL-.js → git-m4lboTfx.js} +29 -29
- package/dist/git-m4lboTfx.js.map +1 -0
- package/dist/git.js +1 -1
- package/dist/{iconMap-V4B8P-Uh.js → iconMap-DDpe35ek.js} +5 -5
- package/dist/iconMap-DDpe35ek.js.map +1 -0
- package/dist/icons.js +1 -1
- package/dist/{index-Bryv_GCG.d.ts → index-BP4IYXiF.d.ts} +46 -53
- package/dist/index-BP4IYXiF.d.ts.map +1 -0
- package/dist/{index-kHr9udZD.d.ts → index-BkIh8oov.d.ts} +17 -17
- package/dist/{index-kHr9udZD.d.ts.map → index-BkIh8oov.d.ts.map} +1 -1
- package/dist/{index-DSu19mq0.d.ts → index-D3Ob3aXg.d.ts} +9 -9
- package/dist/{index-DSu19mq0.d.ts.map → index-D3Ob3aXg.d.ts.map} +1 -1
- package/dist/{index-Ml_SgiKa.d.ts → index-DGoLQBX6.d.ts} +18 -42
- package/dist/index-DGoLQBX6.d.ts.map +1 -0
- package/dist/index-DnJaZr08.d.ts +67 -0
- package/dist/index-DnJaZr08.d.ts.map +1 -0
- package/dist/{index-DmsyeHFr.d.ts → index-Pty-N7-g.d.ts} +5 -5
- package/dist/{index-DmsyeHFr.d.ts.map → index-Pty-N7-g.d.ts.map} +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +10 -10
- package/dist/layout-BYsc16hD.js +183 -0
- package/dist/layout-BYsc16hD.js.map +1 -0
- package/dist/layout.d.ts +2 -2
- package/dist/layout.js +2 -2
- package/dist/{list-CxfT6hix.js → list-DAq-b6RR.js} +49 -63
- package/dist/list-DAq-b6RR.js.map +1 -0
- package/dist/list.d.ts +2 -2
- package/dist/list.js +4 -3
- package/dist/{media-DZ292aKK.js → media-DuczOGsk.js} +32 -31
- package/dist/media-DuczOGsk.js.map +1 -0
- package/dist/media.js +1 -1
- package/dist/{tree-Dd9Z0Aso.js → tree-B9VQcKBp.js} +2 -2
- package/dist/{tree-Dd9Z0Aso.js.map → tree-B9VQcKBp.js.map} +1 -1
- package/dist/tree.d.ts +1 -1
- package/dist/tree.js +2 -2
- package/package.json +2 -2
- package/src/calendar/AgendaView.tsx +2 -2
- package/src/calendar/CalendarBrowser.tsx +11 -11
- package/src/calendar/CalendarSidebar.tsx +10 -10
- package/src/calendar/DayView.tsx +5 -5
- package/src/calendar/EventCard.tsx +3 -3
- package/src/calendar/MonthView.tsx +6 -6
- package/src/calendar/WeekView.tsx +10 -10
- package/src/contacts/ContactBrowser.tsx +8 -8
- package/src/contacts/ContactCard.tsx +4 -4
- package/src/contacts/ContactDetail.tsx +10 -10
- package/src/contacts/ContactGroupSidebar.tsx +6 -6
- package/src/contacts/ContactList.tsx +3 -3
- package/src/file-browser/components/FileBrowser.tsx +3 -2
- package/src/file-browser/components/FileBrowserContent.tsx +1 -1
- package/src/file-browser/examples/BasicUsage.tsx +2 -2
- package/src/file-browser/index.ts +1 -1
- package/src/file-browser/providers/FileSystemProvider.ts +1 -1
- package/src/git/BranchList.tsx +12 -12
- package/src/git/CommitList.tsx +11 -11
- package/src/git/DiffViewer.tsx +11 -11
- package/src/icons/iconMap.ts +4 -4
- package/src/layout/index.ts +6 -2
- package/src/layout/models/ResponsiveLayoutModel.ts +116 -0
- package/src/list/components/ListItem.tsx +1 -1
- package/src/list/index.ts +1 -1
- package/src/media/AlbumSidebar.tsx +4 -4
- package/src/media/MediaBrowser.tsx +11 -11
- package/src/media/MediaGrid.tsx +3 -3
- package/src/media/MediaList.tsx +6 -6
- package/src/media/MediaPreview.tsx +2 -2
- package/src/media/MediaTimeline.tsx +3 -3
- package/src/{file-browser/components/shared → shared}/ErrorBoundary.tsx +3 -3
- package/dist/calendar-DSlrbHoj.js.map +0 -1
- package/dist/contacts-DQXTZzHc.js.map +0 -1
- package/dist/file-browser-m5atC3kF.js.map +0 -1
- package/dist/git-B55e6LL-.js.map +0 -1
- package/dist/iconMap-V4B8P-Uh.js.map +0 -1
- package/dist/index-Bryv_GCG.d.ts.map +0 -1
- package/dist/index-DzfY1Tok.d.ts +0 -32
- package/dist/index-DzfY1Tok.d.ts.map +0 -1
- package/dist/index-Ml_SgiKa.d.ts.map +0 -1
- package/dist/layout-Ca_4r8ka.js +0 -89
- package/dist/layout-Ca_4r8ka.js.map +0 -1
- package/dist/list-CxfT6hix.js.map +0 -1
- package/dist/media-DZ292aKK.js.map +0 -1
- package/src/list/components/shared/ErrorBoundary.tsx +0 -123
package/dist/tree.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CheckboxState, DEFAULT_SELECTION_THEME$1 as DEFAULT_SELECTION_THEME, FocusIndicatorStyle, SelectionColorScheme, SelectionIntensity, SimpleTreeProvider$1 as SimpleTreeProvider, TestTreeProvider$1 as TestTreeProvider, Tree$1 as Tree, TreeCheckbox$1 as TreeCheckbox, TreeContextMenu$1 as TreeContextMenu, TreeContextMenuEvent, TreeContextMenuItem, TreeContextMenuProps, TreeDragDropInfo, TreeLoadOptions, TreeLoadResult, TreeModel$1 as TreeModel, TreeNodeData, TreeNodeList$1 as TreeNodeList, TreeNodeListProps, TreeNodeRenderer, TreeProps, TreeProvider, TreeSelectionInfo, TreeSelectionTheme, TreeTable$1 as TreeTable, TreeTableColumn, TreeTableExportOptions, TreeTableProps, TreeTableSort, getCustomColorVariables$1 as getCustomColorVariables, getSelectionClasses$1 as getSelectionClasses, logger$1 as logger } from "./index-
|
|
1
|
+
import { CheckboxState, DEFAULT_SELECTION_THEME$1 as DEFAULT_SELECTION_THEME, FocusIndicatorStyle, SelectionColorScheme, SelectionIntensity, SimpleTreeProvider$1 as SimpleTreeProvider, TestTreeProvider$1 as TestTreeProvider, Tree$1 as Tree, TreeCheckbox$1 as TreeCheckbox, TreeContextMenu$1 as TreeContextMenu, TreeContextMenuEvent, TreeContextMenuItem, TreeContextMenuProps, TreeDragDropInfo, TreeLoadOptions, TreeLoadResult, TreeModel$1 as TreeModel, TreeNodeData, TreeNodeList$1 as TreeNodeList, TreeNodeListProps, TreeNodeRenderer, TreeProps, TreeProvider, TreeSelectionInfo, TreeSelectionTheme, TreeTable$1 as TreeTable, TreeTableColumn, TreeTableExportOptions, TreeTableProps, TreeTableSort, getCustomColorVariables$1 as getCustomColorVariables, getSelectionClasses$1 as getSelectionClasses, logger$1 as logger } from "./index-BkIh8oov.js";
|
|
2
2
|
export { CheckboxState, DEFAULT_SELECTION_THEME, FocusIndicatorStyle, SelectionColorScheme, SelectionIntensity, SimpleTreeProvider, TestTreeProvider, Tree, TreeCheckbox, TreeContextMenu, TreeContextMenuEvent, TreeContextMenuItem, TreeContextMenuProps, TreeDragDropInfo, TreeLoadOptions, TreeLoadResult, TreeModel, TreeNodeData, TreeNodeList, TreeNodeListProps, TreeNodeRenderer, TreeProps, TreeProvider, TreeSelectionInfo, TreeSelectionTheme, TreeTable, TreeTableColumn, TreeTableExportOptions, TreeTableProps, TreeTableSort, getCustomColorVariables, getSelectionClasses, logger };
|
package/dist/tree.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./utils-B4fdKKsy.js";
|
|
2
|
-
import { DEFAULT_SELECTION_THEME, SimpleTreeProvider, TestTreeProvider, Tree, TreeCheckbox, TreeContextMenu, TreeModel, TreeNodeList, TreeTable, getCustomColorVariables, getSelectionClasses, logger } from "./tree-
|
|
3
|
-
import "./iconMap-
|
|
2
|
+
import { DEFAULT_SELECTION_THEME, SimpleTreeProvider, TestTreeProvider, Tree, TreeCheckbox, TreeContextMenu, TreeModel, TreeNodeList, TreeTable, getCustomColorVariables, getSelectionClasses, logger } from "./tree-B9VQcKBp.js";
|
|
3
|
+
import "./iconMap-DDpe35ek.js";
|
|
4
4
|
import "./FileBrowserContext-B6jixa2j.js";
|
|
5
5
|
|
|
6
6
|
export { DEFAULT_SELECTION_THEME, SimpleTreeProvider, TestTreeProvider, Tree, TreeCheckbox, TreeContextMenu, TreeModel, TreeNodeList, TreeTable, getCustomColorVariables, getSelectionClasses, logger };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anymux/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Composite UI components for AnyMux — file browser, media, calendar, contacts, git",
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
"squarify": "^1.1.0",
|
|
85
85
|
"tailwind-merge": "^2.2.1",
|
|
86
86
|
"transformation-matrix": "^3.1.0",
|
|
87
|
-
"@anymux/file-system": "0.1.0",
|
|
88
87
|
"@anymux/ui": "0.1.0"
|
|
89
88
|
},
|
|
90
89
|
"peerDependencies": {
|
|
90
|
+
"@anymux/file-system": ">=0.1.0",
|
|
91
91
|
"mobx": "^6.13.5",
|
|
92
92
|
"mobx-react-lite": "^4.0.7",
|
|
93
93
|
"react": "^19.0.0",
|
|
@@ -15,13 +15,13 @@ export const AgendaView = observer<AgendaViewProps>(({ model, className = '' })
|
|
|
15
15
|
return (
|
|
16
16
|
<div className={`overflow-y-auto p-4 space-y-4 ${className}`}>
|
|
17
17
|
{sortedEntries.length === 0 && (
|
|
18
|
-
<div className="flex items-center justify-center h-32 text-
|
|
18
|
+
<div className="flex items-center justify-center h-32 text-muted-foreground text-sm">No upcoming events</div>
|
|
19
19
|
)}
|
|
20
20
|
{sortedEntries.map(([dateKey, events]) => {
|
|
21
21
|
const date = new Date(dateKey + 'T00:00:00');
|
|
22
22
|
return (
|
|
23
23
|
<div key={dateKey}>
|
|
24
|
-
<h3 className="text-sm font-semibold text-
|
|
24
|
+
<h3 className="text-sm font-semibold text-foreground mb-2 sticky top-0 bg-background/90 backdrop-blur-sm py-1">
|
|
25
25
|
{date.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' })}
|
|
26
26
|
</h3>
|
|
27
27
|
<div className="space-y-1.5">
|
|
@@ -26,35 +26,35 @@ export const CalendarBrowser = observer<CalendarBrowserProps>(({ model, classNam
|
|
|
26
26
|
useEffect(() => { model.loadEvents(); }, [model]);
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
<div className={`flex h-full bg-
|
|
30
|
-
{showSidebar && <CalendarSidebar model={model} />}
|
|
29
|
+
<div className={`flex h-full bg-background rounded-xl border border-border overflow-hidden ${className}`}>
|
|
30
|
+
{showSidebar && <CalendarSidebar model={model} className="hidden md:block" />}
|
|
31
31
|
|
|
32
32
|
<div className="flex-1 flex flex-col min-w-0">
|
|
33
33
|
{/* Toolbar */}
|
|
34
|
-
<div className="flex items-center gap-2 px-
|
|
35
|
-
<button onClick={() => model.today()} className="text-sm px-3 py-1.5 border border-
|
|
34
|
+
<div className="flex items-center gap-1.5 sm:gap-2 px-3 py-2 border-b border-border flex-wrap sm:flex-nowrap">
|
|
35
|
+
<button onClick={() => model.today()} className="text-sm px-3 py-1.5 border border-border rounded-lg hover:bg-muted">
|
|
36
36
|
Today
|
|
37
37
|
</button>
|
|
38
|
-
<button onClick={() => model.navigateBack()} className="p-1.5 hover:bg-
|
|
38
|
+
<button onClick={() => model.navigateBack()} className="p-1.5 hover:bg-muted rounded-lg">
|
|
39
39
|
<ChevronLeft size={16} />
|
|
40
40
|
</button>
|
|
41
|
-
<button onClick={() => model.navigateForward()} className="p-1.5 hover:bg-
|
|
41
|
+
<button onClick={() => model.navigateForward()} className="p-1.5 hover:bg-muted rounded-lg">
|
|
42
42
|
<ChevronRight size={16} />
|
|
43
43
|
</button>
|
|
44
|
-
<h2 className="text-sm font-medium text-
|
|
44
|
+
<h2 className="text-sm font-medium text-foreground ml-2 truncate">
|
|
45
45
|
{model.currentDate.toLocaleDateString(undefined, {
|
|
46
46
|
month: 'long',
|
|
47
47
|
year: 'numeric',
|
|
48
48
|
...(model.viewMode === 'day' ? { day: 'numeric', weekday: 'long' } : {})
|
|
49
49
|
})}
|
|
50
50
|
</h2>
|
|
51
|
-
<div className="ml-auto flex items-center border border-
|
|
51
|
+
<div className="ml-auto flex items-center border border-border rounded-lg overflow-hidden">
|
|
52
52
|
{(Object.keys(VIEW_LABELS) as CalendarViewMode[]).map(mode => (
|
|
53
53
|
<button
|
|
54
54
|
key={mode}
|
|
55
55
|
onClick={() => model.setViewMode(mode)}
|
|
56
|
-
className={`px-3 py-1.5 text-xs font-medium ${
|
|
57
|
-
model.viewMode === mode ? 'bg-
|
|
56
|
+
className={`px-2 sm:px-3 py-1.5 text-xs font-medium ${
|
|
57
|
+
model.viewMode === mode ? 'bg-primary/10 text-primary' : 'text-muted-foreground hover:bg-muted'
|
|
58
58
|
}`}
|
|
59
59
|
>
|
|
60
60
|
{VIEW_LABELS[mode]}
|
|
@@ -67,7 +67,7 @@ export const CalendarBrowser = observer<CalendarBrowserProps>(({ model, classNam
|
|
|
67
67
|
<div className="flex-1 overflow-hidden">
|
|
68
68
|
{model.loading ? (
|
|
69
69
|
<div className="flex items-center justify-center h-64">
|
|
70
|
-
<Loader2 size={24} className="animate-spin text-
|
|
70
|
+
<Loader2 size={24} className="animate-spin text-muted-foreground" />
|
|
71
71
|
</div>
|
|
72
72
|
) : model.error ? (
|
|
73
73
|
<BrowserError
|
|
@@ -30,23 +30,23 @@ export const CalendarSidebar = observer<CalendarSidebarProps>(({ model, classNam
|
|
|
30
30
|
day === d.getDate();
|
|
31
31
|
|
|
32
32
|
return (
|
|
33
|
-
<div className={`w-56 border-r border-
|
|
33
|
+
<div className={`w-56 border-r border-border bg-muted/30 p-3 ${className}`}>
|
|
34
34
|
{/* Mini month */}
|
|
35
35
|
<div className="mb-4">
|
|
36
36
|
<div className="flex items-center justify-between mb-2">
|
|
37
|
-
<button onClick={() => model.navigateBack()} className="p-1 hover:bg-
|
|
37
|
+
<button onClick={() => model.navigateBack()} className="p-1 hover:bg-muted rounded">
|
|
38
38
|
<ChevronLeft size={14} />
|
|
39
39
|
</button>
|
|
40
|
-
<span className="text-sm font-medium">
|
|
40
|
+
<span className="text-sm font-medium text-foreground">
|
|
41
41
|
{d.toLocaleDateString(undefined, { month: 'long', year: 'numeric' })}
|
|
42
42
|
</span>
|
|
43
|
-
<button onClick={() => model.navigateForward()} className="p-1 hover:bg-
|
|
43
|
+
<button onClick={() => model.navigateForward()} className="p-1 hover:bg-muted rounded">
|
|
44
44
|
<ChevronRight size={14} />
|
|
45
45
|
</button>
|
|
46
46
|
</div>
|
|
47
47
|
<div className="grid grid-cols-7 gap-0.5 text-center">
|
|
48
48
|
{DAYS.map(day => (
|
|
49
|
-
<div key={day} className="text-[10px] text-
|
|
49
|
+
<div key={day} className="text-[10px] text-muted-foreground font-medium py-0.5">{day}</div>
|
|
50
50
|
))}
|
|
51
51
|
{cells.map((day, i) => (
|
|
52
52
|
<button
|
|
@@ -55,9 +55,9 @@ export const CalendarSidebar = observer<CalendarSidebarProps>(({ model, classNam
|
|
|
55
55
|
onClick={() => day && model.setDate(new Date(year, month, day))}
|
|
56
56
|
className={`text-xs py-0.5 rounded ${
|
|
57
57
|
day === null ? '' :
|
|
58
|
-
isSelected(day) ? 'bg-
|
|
59
|
-
isToday(day) ? 'bg-
|
|
60
|
-
'text-
|
|
58
|
+
isSelected(day) ? 'bg-primary text-primary-foreground' :
|
|
59
|
+
isToday(day) ? 'bg-primary/10 text-primary font-bold' :
|
|
60
|
+
'text-foreground hover:bg-muted'
|
|
61
61
|
}`}
|
|
62
62
|
>
|
|
63
63
|
{day}
|
|
@@ -68,9 +68,9 @@ export const CalendarSidebar = observer<CalendarSidebarProps>(({ model, classNam
|
|
|
68
68
|
|
|
69
69
|
{/* Calendar list */}
|
|
70
70
|
<div>
|
|
71
|
-
<h3 className="text-xs font-semibold text-
|
|
71
|
+
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">Calendars</h3>
|
|
72
72
|
{model.calendars.map(cal => (
|
|
73
|
-
<div key={cal.id} className="flex items-center gap-2 px-2 py-1.5 text-sm text-
|
|
73
|
+
<div key={cal.id} className="flex items-center gap-2 px-2 py-1.5 text-sm text-foreground">
|
|
74
74
|
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: cal.color }} />
|
|
75
75
|
<span className="truncate" title={cal.name}>{cal.name}</span>
|
|
76
76
|
</div>
|
package/src/calendar/DayView.tsx
CHANGED
|
@@ -19,8 +19,8 @@ export const DayView = observer<DayViewProps>(({ model, className = '' }) => {
|
|
|
19
19
|
return (
|
|
20
20
|
<div className={`flex flex-col h-full overflow-auto ${className}`}>
|
|
21
21
|
{/* Header */}
|
|
22
|
-
<div className="border-b border-
|
|
23
|
-
<h2 className="text-lg font-medium text-
|
|
22
|
+
<div className="border-b border-border px-4 py-2 sticky top-0 bg-background z-10">
|
|
23
|
+
<h2 className="text-lg font-medium text-foreground">
|
|
24
24
|
{d.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' })}
|
|
25
25
|
</h2>
|
|
26
26
|
{allDayEvents.length > 0 && (
|
|
@@ -44,11 +44,11 @@ export const DayView = observer<DayViewProps>(({ model, className = '' }) => {
|
|
|
44
44
|
{HOURS.map(hour => {
|
|
45
45
|
const hourEvents = timedEvents.filter(e => e.startDate.getHours() === hour);
|
|
46
46
|
return (
|
|
47
|
-
<div key={hour} className="grid grid-cols-[60px_1fr] border-b border-
|
|
48
|
-
<div className="text-[10px] text-
|
|
47
|
+
<div key={hour} className="grid grid-cols-[60px_1fr] border-b border-border/50 min-h-[48px]">
|
|
48
|
+
<div className="text-[10px] text-muted-foreground text-right pr-2 -mt-2">
|
|
49
49
|
{hour === 0 ? '' : `${hour % 12 || 12} ${hour < 12 ? 'AM' : 'PM'}`}
|
|
50
50
|
</div>
|
|
51
|
-
<div className="border-l border-
|
|
51
|
+
<div className="border-l border-border relative pl-1">
|
|
52
52
|
{hourEvents.map(ev => (
|
|
53
53
|
<button
|
|
54
54
|
key={ev.id}
|
|
@@ -30,11 +30,11 @@ export const EventCard = ({ event, compact = false, onClick, className = '' }: E
|
|
|
30
30
|
return (
|
|
31
31
|
<button
|
|
32
32
|
onClick={onClick}
|
|
33
|
-
className={`text-left w-full p-3 rounded-lg border border-
|
|
33
|
+
className={`text-left w-full p-3 rounded-lg border border-border hover:shadow-sm transition-shadow ${className}`}
|
|
34
34
|
style={{ borderLeftWidth: '3px', borderLeftColor: color }}
|
|
35
35
|
>
|
|
36
|
-
<p className="text-sm font-medium text-
|
|
37
|
-
<div className="mt-1 flex items-center gap-3 text-xs text-
|
|
36
|
+
<p className="text-sm font-medium text-foreground truncate" title={event.title}>{event.title}</p>
|
|
37
|
+
<div className="mt-1 flex items-center gap-3 text-xs text-muted-foreground">
|
|
38
38
|
<span className="flex items-center gap-1">
|
|
39
39
|
<Clock size={12} />
|
|
40
40
|
{event.allDay ? 'All day' : `${formatTime(event.startDate)} - ${formatTime(event.endDate)}`}
|
|
@@ -36,23 +36,23 @@ export const MonthView = observer<MonthViewProps>(({ model, className = '' }) =>
|
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
38
|
<div className={`flex flex-col h-full ${className}`}>
|
|
39
|
-
<div className="grid grid-cols-7 border-b border-
|
|
39
|
+
<div className="grid grid-cols-7 border-b border-border">
|
|
40
40
|
{DAYS.map(day => (
|
|
41
|
-
<div key={day} className="px-2 py-2 text-xs font-medium text-
|
|
41
|
+
<div key={day} className="px-2 py-2 text-xs font-medium text-muted-foreground text-center">{day.slice(0, 3)}</div>
|
|
42
42
|
))}
|
|
43
43
|
</div>
|
|
44
44
|
<div className="flex-1 grid grid-rows-[repeat(auto-fill,1fr)]">
|
|
45
45
|
{weeks.map((week, wi) => (
|
|
46
|
-
<div key={wi} className="grid grid-cols-7 border-b border-
|
|
46
|
+
<div key={wi} className="grid grid-cols-7 border-b border-border min-h-[80px]">
|
|
47
47
|
{week.map((day, di) => {
|
|
48
48
|
const dateKey = day ? `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}` : null;
|
|
49
49
|
const events = dateKey ? (model.eventsByDay.get(dateKey) ?? []) : [];
|
|
50
50
|
return (
|
|
51
|
-
<div key={di} className={`border-r border-
|
|
51
|
+
<div key={di} className={`border-r border-border p-1 ${day === null ? 'bg-muted/50' : ''}`}>
|
|
52
52
|
{day !== null && (
|
|
53
53
|
<>
|
|
54
54
|
<div className={`text-xs mb-0.5 w-6 h-6 flex items-center justify-center rounded-full ${
|
|
55
|
-
isToday(day) ? 'bg-
|
|
55
|
+
isToday(day) ? 'bg-primary text-white font-bold' : 'text-foreground'
|
|
56
56
|
}`}>
|
|
57
57
|
{day}
|
|
58
58
|
</div>
|
|
@@ -61,7 +61,7 @@ export const MonthView = observer<MonthViewProps>(({ model, className = '' }) =>
|
|
|
61
61
|
<EventCard key={ev.id} event={ev} compact onClick={() => model.selectEvent(ev)} />
|
|
62
62
|
))}
|
|
63
63
|
{events.length > 3 && (
|
|
64
|
-
<p className="text-[10px] text-
|
|
64
|
+
<p className="text-[10px] text-muted-foreground pl-1">+{events.length - 3} more</p>
|
|
65
65
|
)}
|
|
66
66
|
</div>
|
|
67
67
|
</>
|
|
@@ -32,23 +32,23 @@ export const WeekView = observer<WeekViewProps>(({ model, className = '' }) => {
|
|
|
32
32
|
return (
|
|
33
33
|
<div className={`flex flex-col h-full overflow-auto ${className}`}>
|
|
34
34
|
{/* Day headers */}
|
|
35
|
-
<div className="sticky top-0 bg-
|
|
35
|
+
<div className="sticky top-0 bg-background z-10 border-b border-border">
|
|
36
36
|
<div className="grid" style={{ gridTemplateColumns: '60px repeat(7, 1fr)' }}>
|
|
37
37
|
<div />
|
|
38
38
|
{days.map((d, i) => (
|
|
39
|
-
<div key={i} className={`text-center py-2 border-l border-
|
|
40
|
-
<div className="text-xs text-
|
|
41
|
-
<div className={`text-lg font-medium ${isToday(d) ? 'text-
|
|
39
|
+
<div key={i} className={`text-center py-2 border-l border-border ${isToday(d) ? 'bg-primary/10' : ''}`}>
|
|
40
|
+
<div className="text-xs text-muted-foreground">{d.toLocaleDateString(undefined, { weekday: 'short' })}</div>
|
|
41
|
+
<div className={`text-lg font-medium ${isToday(d) ? 'text-primary' : 'text-foreground'}`}>{d.getDate()}</div>
|
|
42
42
|
</div>
|
|
43
43
|
))}
|
|
44
44
|
</div>
|
|
45
45
|
|
|
46
46
|
{/* All-day events row */}
|
|
47
47
|
{hasAllDay && (
|
|
48
|
-
<div className="grid border-t border-
|
|
49
|
-
<div className="text-[10px] text-
|
|
48
|
+
<div className="grid border-t border-border" style={{ gridTemplateColumns: '60px repeat(7, 1fr)' }}>
|
|
49
|
+
<div className="text-[10px] text-muted-foreground text-right pr-2 py-1">all-day</div>
|
|
50
50
|
{allDayEventsByDay.map((events, i) => (
|
|
51
|
-
<div key={i} className="border-l border-
|
|
51
|
+
<div key={i} className="border-l border-border px-0.5 py-0.5 space-y-0.5">
|
|
52
52
|
{events.map(ev => (
|
|
53
53
|
<button
|
|
54
54
|
key={ev.id}
|
|
@@ -73,8 +73,8 @@ export const WeekView = observer<WeekViewProps>(({ model, className = '' }) => {
|
|
|
73
73
|
{/* Time grid */}
|
|
74
74
|
<div className="flex-1 relative">
|
|
75
75
|
{HOURS.map(hour => (
|
|
76
|
-
<div key={hour} className="grid border-b border-
|
|
77
|
-
<div className="text-[10px] text-
|
|
76
|
+
<div key={hour} className="grid border-b border-border/50" style={{ gridTemplateColumns: '60px repeat(7, 1fr)', height: '48px' }}>
|
|
77
|
+
<div className="text-[10px] text-muted-foreground text-right pr-2 -mt-2">
|
|
78
78
|
{hour === 0 ? '' : `${hour % 12 || 12} ${hour < 12 ? 'AM' : 'PM'}`}
|
|
79
79
|
</div>
|
|
80
80
|
{days.map((d, i) => {
|
|
@@ -84,7 +84,7 @@ export const WeekView = observer<WeekViewProps>(({ model, className = '' }) => {
|
|
|
84
84
|
);
|
|
85
85
|
const showTimeLine = isToday(d) && hour === currentHour;
|
|
86
86
|
return (
|
|
87
|
-
<div key={i} className="border-l border-
|
|
87
|
+
<div key={i} className="border-l border-border relative">
|
|
88
88
|
{/* Current time indicator */}
|
|
89
89
|
{showTimeLine && (
|
|
90
90
|
<div
|
|
@@ -17,27 +17,27 @@ export const ContactBrowser = observer<ContactBrowserProps>(({ model, className
|
|
|
17
17
|
useEffect(() => { model.loadContacts(); }, [model]);
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<div className={`flex h-full bg-
|
|
21
|
-
{showSidebar && <ContactGroupSidebar model={model} />}
|
|
20
|
+
<div className={`flex h-full bg-background rounded-xl border border-border overflow-hidden ${className}`}>
|
|
21
|
+
{showSidebar && <ContactGroupSidebar model={model} className="hidden lg:block" />}
|
|
22
22
|
|
|
23
|
-
<div className="flex-1 flex flex-col min-w-0 border-r border-
|
|
23
|
+
<div className="flex-1 flex flex-col min-w-0 border-r border-border sm:max-w-[380px]">
|
|
24
24
|
{/* Search */}
|
|
25
|
-
<div className="px-
|
|
25
|
+
<div className="px-3 py-2 border-b border-border">
|
|
26
26
|
<div className="relative">
|
|
27
|
-
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-
|
|
27
|
+
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
|
28
28
|
<input
|
|
29
29
|
type="text"
|
|
30
30
|
placeholder="Search contacts..."
|
|
31
31
|
value={model.searchQuery}
|
|
32
32
|
onChange={e => model.setSearch(e.target.value)}
|
|
33
|
-
className="w-full pl-9 pr-3 py-1.5 text-sm border border-
|
|
33
|
+
className="w-full pl-9 pr-3 py-1.5 text-sm border border-border rounded-lg bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
34
34
|
/>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
{model.loading ? (
|
|
39
39
|
<div className="flex items-center justify-center h-64">
|
|
40
|
-
<Loader2 size={24} className="animate-spin text-
|
|
40
|
+
<Loader2 size={24} className="animate-spin text-muted-foreground" />
|
|
41
41
|
</div>
|
|
42
42
|
) : model.error ? (
|
|
43
43
|
<BrowserError
|
|
@@ -50,7 +50,7 @@ export const ContactBrowser = observer<ContactBrowserProps>(({ model, className
|
|
|
50
50
|
)}
|
|
51
51
|
</div>
|
|
52
52
|
|
|
53
|
-
<ContactDetail model={model} className="flex-1" />
|
|
53
|
+
<ContactDetail model={model} className="flex-1 hidden sm:block" />
|
|
54
54
|
</div>
|
|
55
55
|
);
|
|
56
56
|
});
|
|
@@ -14,22 +14,22 @@ export const ContactCard = ({ contact, selected = false, onClick, className = ''
|
|
|
14
14
|
<button
|
|
15
15
|
onClick={onClick}
|
|
16
16
|
className={`flex items-center gap-3 px-4 py-3 w-full text-left transition-colors ${
|
|
17
|
-
selected ? 'bg-
|
|
17
|
+
selected ? 'bg-primary/10 border-l-2 border-primary' : 'hover:bg-muted/50 border-l-2 border-transparent'
|
|
18
18
|
} ${className}`}
|
|
19
19
|
>
|
|
20
20
|
<ContactAvatar firstName={contact.firstName} lastName={contact.lastName} avatar={contact.avatar} />
|
|
21
21
|
<div className="flex-1 min-w-0">
|
|
22
|
-
<p className="text-sm font-medium text-
|
|
22
|
+
<p className="text-sm font-medium text-foreground truncate" title={`${contact.firstName} ${contact.lastName}`}>
|
|
23
23
|
{contact.firstName} {contact.lastName}
|
|
24
24
|
</p>
|
|
25
25
|
{contact.company && (
|
|
26
|
-
<p className="text-xs text-
|
|
26
|
+
<p className="text-xs text-muted-foreground flex items-center gap-1 truncate" title={contact.company}>
|
|
27
27
|
<Building size={12} />
|
|
28
28
|
{contact.company}
|
|
29
29
|
</p>
|
|
30
30
|
)}
|
|
31
31
|
</div>
|
|
32
|
-
<div className="flex items-center gap-1.5 text-
|
|
32
|
+
<div className="flex items-center gap-1.5 text-muted-foreground">
|
|
33
33
|
{contact.email && <Mail size={14} />}
|
|
34
34
|
{contact.phone && <Phone size={14} />}
|
|
35
35
|
</div>
|
|
@@ -11,10 +11,10 @@ export interface ContactDetailProps {
|
|
|
11
11
|
|
|
12
12
|
const DetailRow = ({ icon: Icon, label, value }: { icon: React.ElementType; label: string; value: string }) => (
|
|
13
13
|
<div className="flex items-start gap-3 py-2">
|
|
14
|
-
<Icon size={16} className="text-
|
|
14
|
+
<Icon size={16} className="text-muted-foreground mt-0.5 flex-shrink-0" />
|
|
15
15
|
<div>
|
|
16
|
-
<p className="text-xs text-
|
|
17
|
-
<p className="text-sm text-
|
|
16
|
+
<p className="text-xs text-muted-foreground">{label}</p>
|
|
17
|
+
<p className="text-sm text-foreground">{value}</p>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
);
|
|
@@ -24,7 +24,7 @@ export const ContactDetail = observer<ContactDetailProps>(({ model, className =
|
|
|
24
24
|
|
|
25
25
|
if (!contact) {
|
|
26
26
|
return (
|
|
27
|
-
<div className={`flex items-center justify-center h-full text-
|
|
27
|
+
<div className={`flex items-center justify-center h-full text-muted-foreground text-sm ${className}`}>
|
|
28
28
|
Select a contact to view details
|
|
29
29
|
</div>
|
|
30
30
|
);
|
|
@@ -34,11 +34,11 @@ export const ContactDetail = observer<ContactDetailProps>(({ model, className =
|
|
|
34
34
|
<div className={`p-6 overflow-y-auto ${className}`}>
|
|
35
35
|
<div className="flex flex-col items-center mb-6">
|
|
36
36
|
<ContactAvatar firstName={contact.firstName} lastName={contact.lastName} avatar={contact.avatar} size="lg" />
|
|
37
|
-
<h2 className="mt-3 text-lg font-semibold text-
|
|
38
|
-
{contact.company && <p className="text-sm text-
|
|
37
|
+
<h2 className="mt-3 text-lg font-semibold text-foreground">{contact.firstName} {contact.lastName}</h2>
|
|
38
|
+
{contact.company && <p className="text-sm text-muted-foreground">{contact.company}</p>}
|
|
39
39
|
</div>
|
|
40
40
|
|
|
41
|
-
<div className="divide-y divide-
|
|
41
|
+
<div className="divide-y divide-border">
|
|
42
42
|
{contact.email && <DetailRow icon={Mail} label="Email" value={contact.email} />}
|
|
43
43
|
{contact.phone && <DetailRow icon={Phone} label="Phone" value={contact.phone} />}
|
|
44
44
|
{contact.company && <DetailRow icon={Building} label="Company" value={contact.company} />}
|
|
@@ -46,12 +46,12 @@ export const ContactDetail = observer<ContactDetailProps>(({ model, className =
|
|
|
46
46
|
{contact.birthday && <DetailRow icon={Calendar} label="Birthday" value={contact.birthday.toLocaleDateString()} />}
|
|
47
47
|
{contact.groups && contact.groups.length > 0 && (
|
|
48
48
|
<div className="flex items-start gap-3 py-2">
|
|
49
|
-
<Tag size={16} className="text-
|
|
49
|
+
<Tag size={16} className="text-muted-foreground mt-0.5 flex-shrink-0" />
|
|
50
50
|
<div>
|
|
51
|
-
<p className="text-xs text-
|
|
51
|
+
<p className="text-xs text-muted-foreground">Groups</p>
|
|
52
52
|
<div className="flex flex-wrap gap-1 mt-1">
|
|
53
53
|
{contact.groups.map(g => (
|
|
54
|
-
<span key={g} className="text-xs bg-
|
|
54
|
+
<span key={g} className="text-xs bg-muted text-muted-foreground px-2 py-0.5 rounded-full">{g}</span>
|
|
55
55
|
))}
|
|
56
56
|
</div>
|
|
57
57
|
</div>
|
|
@@ -9,30 +9,30 @@ export interface ContactGroupSidebarProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const ContactGroupSidebar = observer<ContactGroupSidebarProps>(({ model, className = '' }) => (
|
|
12
|
-
<div className={`w-56 border-r border-
|
|
12
|
+
<div className={`w-56 border-r border-border bg-muted/30 overflow-y-auto ${className}`}>
|
|
13
13
|
<div className="p-3">
|
|
14
|
-
<h3 className="text-xs font-semibold text-
|
|
14
|
+
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">Groups</h3>
|
|
15
15
|
<button
|
|
16
16
|
onClick={() => model.setGroup(null)}
|
|
17
17
|
className={`flex items-center gap-2 w-full px-3 py-2 rounded-lg text-sm transition-colors ${
|
|
18
|
-
model.currentGroup === null ? 'bg-
|
|
18
|
+
model.currentGroup === null ? 'bg-primary/10 text-primary' : 'text-foreground hover:bg-muted'
|
|
19
19
|
}`}
|
|
20
20
|
>
|
|
21
21
|
<Users size={16} />
|
|
22
22
|
<span>All Contacts</span>
|
|
23
|
-
<span className="ml-auto text-xs text-
|
|
23
|
+
<span className="ml-auto text-xs text-muted-foreground">{model.contacts.length}</span>
|
|
24
24
|
</button>
|
|
25
25
|
{model.groups.map(group => (
|
|
26
26
|
<button
|
|
27
27
|
key={group.id}
|
|
28
28
|
onClick={() => model.setGroup(group.id)}
|
|
29
29
|
className={`flex items-center gap-2 w-full px-3 py-2 rounded-lg text-sm transition-colors ${
|
|
30
|
-
model.currentGroup === group.id ? 'bg-
|
|
30
|
+
model.currentGroup === group.id ? 'bg-primary/10 text-primary' : 'text-foreground hover:bg-muted'
|
|
31
31
|
}`}
|
|
32
32
|
>
|
|
33
33
|
<User size={16} />
|
|
34
34
|
<span className="truncate" title={group.name}>{group.name}</span>
|
|
35
|
-
<span className="ml-auto text-xs text-
|
|
35
|
+
<span className="ml-auto text-xs text-muted-foreground">{group.count}</span>
|
|
36
36
|
</button>
|
|
37
37
|
))}
|
|
38
38
|
</div>
|
|
@@ -12,8 +12,8 @@ export const ContactList = observer<ContactListProps>(({ model, className = '' }
|
|
|
12
12
|
<div className={`overflow-y-auto ${className}`}>
|
|
13
13
|
{Array.from(model.groupedByLetter.entries()).map(([letter, contacts]) => (
|
|
14
14
|
<div key={letter}>
|
|
15
|
-
<div className="sticky top-0 bg-
|
|
16
|
-
<span className="text-xs font-semibold text-
|
|
15
|
+
<div className="sticky top-0 bg-muted/50 backdrop-blur-sm px-4 py-1 border-b border-border">
|
|
16
|
+
<span className="text-xs font-semibold text-muted-foreground">{letter}</span>
|
|
17
17
|
</div>
|
|
18
18
|
{contacts.map(contact => (
|
|
19
19
|
<ContactCard
|
|
@@ -26,7 +26,7 @@ export const ContactList = observer<ContactListProps>(({ model, className = '' }
|
|
|
26
26
|
</div>
|
|
27
27
|
))}
|
|
28
28
|
{model.filteredContacts.length === 0 && (
|
|
29
|
-
<div className="flex items-center justify-center h-32 text-
|
|
29
|
+
<div className="flex items-center justify-center h-32 text-muted-foreground text-sm">No contacts found</div>
|
|
30
30
|
)}
|
|
31
31
|
</div>
|
|
32
32
|
));
|
|
@@ -177,6 +177,7 @@ export const FileBrowser: React.FC<FileBrowserProps> = observer(({
|
|
|
177
177
|
onClick={() => model.navigateToPrevFile()}
|
|
178
178
|
disabled={!model.canNavigatePrev}
|
|
179
179
|
title="Previous file (left arrow)"
|
|
180
|
+
aria-label="Previous file"
|
|
180
181
|
>
|
|
181
182
|
<ChevronLeft className="w-4 h-4" />
|
|
182
183
|
</Button>
|
|
@@ -412,8 +413,8 @@ export const FileBrowser: React.FC<FileBrowserProps> = observer(({
|
|
|
412
413
|
>
|
|
413
414
|
{/* Native file drop overlay */}
|
|
414
415
|
{isDragOver && (
|
|
415
|
-
<div className="absolute inset-0 z-50 bg-
|
|
416
|
-
<div className="flex flex-col items-center gap-2 text-
|
|
416
|
+
<div className="absolute inset-0 z-50 bg-primary/10 border-2 border-dashed border-primary rounded-lg flex items-center justify-center pointer-events-none">
|
|
417
|
+
<div className="flex flex-col items-center gap-2 text-primary">
|
|
417
418
|
<Upload className="w-8 h-8" />
|
|
418
419
|
<p className="text-sm font-medium">Drop files here to upload</p>
|
|
419
420
|
<p className="text-xs text-muted-foreground">Files will be added to {model.currentPath}</p>
|
|
@@ -4,7 +4,7 @@ import { AlertCircle, FolderOpen } from 'lucide-react';
|
|
|
4
4
|
import { cn } from '../../lib/utils';
|
|
5
5
|
import { FileBrowserModel } from '../models/FileBrowserModel';
|
|
6
6
|
import { LoadingSpinner } from '@anymux/ui/components/loading-spinner';
|
|
7
|
-
import { ErrorBoundary } from '
|
|
7
|
+
import { ErrorBoundary } from '../../shared/ErrorBoundary';
|
|
8
8
|
|
|
9
9
|
export interface FileBrowserContentProps {
|
|
10
10
|
model: FileBrowserModel;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { FileBrowser } from '../components/FileBrowser';
|
|
3
|
-
import { IFileSystem } from '@anymux/file-system';
|
|
3
|
+
import type { IFileSystem } from '@anymux/file-system';
|
|
4
4
|
|
|
5
5
|
interface BasicUsageProps {
|
|
6
6
|
fileSystem: IFileSystem;
|
|
@@ -8,7 +8,7 @@ interface BasicUsageProps {
|
|
|
8
8
|
|
|
9
9
|
export const BasicUsage: React.FC<BasicUsageProps> = ({ fileSystem }) => {
|
|
10
10
|
return (
|
|
11
|
-
<div className="w-full h-96 border border-
|
|
11
|
+
<div className="w-full h-96 border border-border rounded-lg">
|
|
12
12
|
<FileBrowser
|
|
13
13
|
fileSystem={fileSystem}
|
|
14
14
|
initialPath="/"
|
|
@@ -3,7 +3,7 @@ export { FileBrowser } from './components/FileBrowser';
|
|
|
3
3
|
|
|
4
4
|
// Shared components
|
|
5
5
|
export { default as FileBrowserItemComponent } from './components/shared/FileBrowserItem';
|
|
6
|
-
export { ErrorBoundary, type ErrorBoundaryProps } from '
|
|
6
|
+
export { ErrorBoundary, type ErrorBoundaryProps } from '../shared/ErrorBoundary';
|
|
7
7
|
export { default as FileIcon } from './components/shared/FileIcon';
|
|
8
8
|
|
|
9
9
|
// Types
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IFileSystem } from '@anymux/file-system';
|
|
1
|
+
import type { IFileSystem } from '@anymux/file-system';
|
|
2
2
|
import { IFileBrowserProvider } from './IFileBrowserProvider';
|
|
3
3
|
import { FileBrowserItem, IconDefinition, PreviewData } from '../types/FileBrowserTypes';
|
|
4
4
|
import { LeftPanelMode } from '../models/LeftPanelManagerModel';
|