mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
Fixed visual issues with new dashboard and added folder attribution
This commit is contained in:
@@ -6,25 +6,67 @@
|
||||
*/
|
||||
|
||||
import { ipcRenderer, shell } from 'electron';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { filesystem } from '@noodl/platform';
|
||||
|
||||
import {
|
||||
CloudSyncType,
|
||||
LauncherProjectData
|
||||
} from '@noodl-core-ui/preview/launcher/Launcher/components/LauncherProjectCard';
|
||||
import { Launcher } from '@noodl-core-ui/preview/launcher/Launcher/Launcher';
|
||||
|
||||
import { useEventListener } from '../../hooks/useEventListener';
|
||||
import { IRouteProps } from '../../pages/AppRoute';
|
||||
import { LocalProjectsModel } from '../../utils/LocalProjectsModel';
|
||||
import { LocalProjectsModel, ProjectItem } from '../../utils/LocalProjectsModel';
|
||||
import { ToastLayer } from '../../views/ToastLayer/ToastLayer';
|
||||
|
||||
export interface ProjectsPageProps extends IRouteProps {
|
||||
from: TSFixme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map LocalProjectsModel ProjectItem to LauncherProjectData format
|
||||
*/
|
||||
function mapProjectToLauncherData(project: ProjectItem): LauncherProjectData {
|
||||
return {
|
||||
id: project.id,
|
||||
title: project.name || 'Untitled',
|
||||
localPath: project.retainedProjectDirectory,
|
||||
lastOpened: new Date(project.latestAccessed).toISOString(),
|
||||
imageSrc: project.thumbURI || 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3C/svg%3E',
|
||||
cloudSyncMeta: {
|
||||
type: CloudSyncType.None // TODO: Detect git repos in future
|
||||
}
|
||||
// Git-related fields will be populated in future tasks
|
||||
};
|
||||
}
|
||||
|
||||
export function ProjectsPage(props: ProjectsPageProps) {
|
||||
// Real projects from LocalProjectsModel
|
||||
const [realProjects, setRealProjects] = useState<LauncherProjectData[]>([]);
|
||||
|
||||
// Fetch projects on mount
|
||||
useEffect(() => {
|
||||
// Switch main window size to editor size
|
||||
ipcRenderer.send('main-window-resize', { size: 'editor', center: true });
|
||||
|
||||
// Initial load
|
||||
const loadProjects = async () => {
|
||||
await LocalProjectsModel.instance.fetch();
|
||||
const projects = LocalProjectsModel.instance.getProjects();
|
||||
setRealProjects(projects.map(mapProjectToLauncherData));
|
||||
};
|
||||
|
||||
loadProjects();
|
||||
}, []);
|
||||
|
||||
// Subscribe to project list changes
|
||||
useEventListener(LocalProjectsModel.instance, 'myProjectsChanged', () => {
|
||||
console.log('🔔 Projects list changed, updating dashboard');
|
||||
const projects = LocalProjectsModel.instance.getProjects();
|
||||
setRealProjects(projects.map(mapProjectToLauncherData));
|
||||
});
|
||||
|
||||
const handleCreateProject = useCallback(async () => {
|
||||
try {
|
||||
const direntry = await filesystem.openDialog({
|
||||
@@ -196,6 +238,7 @@ export function ProjectsPage(props: ProjectsPageProps) {
|
||||
|
||||
return (
|
||||
<Launcher
|
||||
projects={realProjects}
|
||||
onCreateProject={handleCreateProject}
|
||||
onOpenProject={handleOpenProject}
|
||||
onLaunchProject={handleLaunchProject}
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
/**
|
||||
* ProjectOrganizationService
|
||||
*
|
||||
* Manages project organization through folders and tags.
|
||||
* Data is stored client-side in electron-store and keyed by project path.
|
||||
*/
|
||||
|
||||
import { EventDispatcher } from '../../../shared/utils/EventDispatcher';
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
// ============================================================================
|
||||
|
||||
export interface Folder {
|
||||
id: string;
|
||||
name: string;
|
||||
parentId: string | null; // null = root level
|
||||
order: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string; // hex color
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface ProjectMeta {
|
||||
folderId: string | null;
|
||||
tagIds: string[];
|
||||
customName?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface ProjectOrganizationData {
|
||||
version: 1;
|
||||
folders: Folder[];
|
||||
tags: Tag[];
|
||||
projectMeta: Record<string, ProjectMeta>; // keyed by project path
|
||||
}
|
||||
|
||||
// Tag color palette
|
||||
export const TAG_COLORS = [
|
||||
'#EF4444', // Red
|
||||
'#F97316', // Orange
|
||||
'#EAB308', // Yellow
|
||||
'#22C55E', // Green
|
||||
'#06B6D4', // Cyan
|
||||
'#3B82F6', // Blue
|
||||
'#8B5CF6', // Purple
|
||||
'#EC4899', // Pink
|
||||
'#6B7280' // Gray
|
||||
];
|
||||
|
||||
// ============================================================================
|
||||
// Service
|
||||
// ============================================================================
|
||||
|
||||
export class ProjectOrganizationService extends EventDispatcher {
|
||||
private static _instance: ProjectOrganizationService;
|
||||
private data: ProjectOrganizationData;
|
||||
private storageKey = 'projectOrganization';
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
this.data = this.loadData();
|
||||
}
|
||||
|
||||
static get instance(): ProjectOrganizationService {
|
||||
if (!ProjectOrganizationService._instance) {
|
||||
ProjectOrganizationService._instance = new ProjectOrganizationService();
|
||||
}
|
||||
return ProjectOrganizationService._instance;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Storage
|
||||
// ============================================================================
|
||||
|
||||
private loadData(): ProjectOrganizationData {
|
||||
try {
|
||||
const stored = localStorage.getItem(this.storageKey);
|
||||
if (stored) {
|
||||
return JSON.parse(stored);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ProjectOrganizationService] Failed to load data:', error);
|
||||
}
|
||||
|
||||
// Return default empty structure
|
||||
return {
|
||||
version: 1,
|
||||
folders: [],
|
||||
tags: [],
|
||||
projectMeta: {}
|
||||
};
|
||||
}
|
||||
|
||||
private saveData(): void {
|
||||
try {
|
||||
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
|
||||
this.notifyListeners('dataChanged', this.data);
|
||||
} catch (error) {
|
||||
console.error('[ProjectOrganizationService] Failed to save data:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Folder Operations
|
||||
// ============================================================================
|
||||
|
||||
createFolder(name: string, parentId?: string | null): Folder {
|
||||
const folder: Folder = {
|
||||
id: this.generateId('folder'),
|
||||
name,
|
||||
parentId: parentId || null,
|
||||
order: this.data.folders.length,
|
||||
createdAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
this.data.folders.push(folder);
|
||||
this.saveData();
|
||||
this.notifyListeners('folderCreated', folder);
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
renameFolder(id: string, name: string): void {
|
||||
const folder = this.data.folders.find((f) => f.id === id);
|
||||
if (!folder) {
|
||||
console.warn('[ProjectOrganizationService] Folder not found:', id);
|
||||
return;
|
||||
}
|
||||
|
||||
folder.name = name;
|
||||
this.saveData();
|
||||
this.notifyListeners('folderRenamed', { id, name });
|
||||
}
|
||||
|
||||
deleteFolder(id: string): void {
|
||||
// Remove folder
|
||||
this.data.folders = this.data.folders.filter((f) => f.id !== id);
|
||||
|
||||
// Remove child folders
|
||||
this.data.folders = this.data.folders.filter((f) => f.parentId !== id);
|
||||
|
||||
// Move projects in deleted folder to uncategorized
|
||||
Object.keys(this.data.projectMeta).forEach((projectPath) => {
|
||||
if (this.data.projectMeta[projectPath].folderId === id) {
|
||||
this.data.projectMeta[projectPath].folderId = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.saveData();
|
||||
this.notifyListeners('folderDeleted', id);
|
||||
}
|
||||
|
||||
reorderFolder(id: string, newOrder: number): void {
|
||||
const folder = this.data.folders.find((f) => f.id === id);
|
||||
if (!folder) return;
|
||||
|
||||
folder.order = newOrder;
|
||||
this.saveData();
|
||||
this.notifyListeners('folderReordered', { id, order: newOrder });
|
||||
}
|
||||
|
||||
getFolders(): Folder[] {
|
||||
return [...this.data.folders].sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
getFolder(id: string): Folder | undefined {
|
||||
return this.data.folders.find((f) => f.id === id);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tag Operations
|
||||
// ============================================================================
|
||||
|
||||
createTag(name: string, color?: string): Tag {
|
||||
const tag: Tag = {
|
||||
id: this.generateId('tag'),
|
||||
name,
|
||||
color: color || this.getNextTagColor(),
|
||||
createdAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
this.data.tags.push(tag);
|
||||
this.saveData();
|
||||
this.notifyListeners('tagCreated', tag);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
renameTag(id: string, name: string): void {
|
||||
const tag = this.data.tags.find((t) => t.id === id);
|
||||
if (!tag) {
|
||||
console.warn('[ProjectOrganizationService] Tag not found:', id);
|
||||
return;
|
||||
}
|
||||
|
||||
tag.name = name;
|
||||
this.saveData();
|
||||
this.notifyListeners('tagRenamed', { id, name });
|
||||
}
|
||||
|
||||
changeTagColor(id: string, color: string): void {
|
||||
const tag = this.data.tags.find((t) => t.id === id);
|
||||
if (!tag) return;
|
||||
|
||||
tag.color = color;
|
||||
this.saveData();
|
||||
this.notifyListeners('tagColorChanged', { id, color });
|
||||
}
|
||||
|
||||
deleteTag(id: string): void {
|
||||
// Remove tag
|
||||
this.data.tags = this.data.tags.filter((t) => t.id !== id);
|
||||
|
||||
// Remove tag from all projects
|
||||
Object.keys(this.data.projectMeta).forEach((projectPath) => {
|
||||
const meta = this.data.projectMeta[projectPath];
|
||||
meta.tagIds = meta.tagIds.filter((tagId) => tagId !== id);
|
||||
});
|
||||
|
||||
this.saveData();
|
||||
this.notifyListeners('tagDeleted', id);
|
||||
}
|
||||
|
||||
getTags(): Tag[] {
|
||||
return [...this.data.tags];
|
||||
}
|
||||
|
||||
getTag(id: string): Tag | undefined {
|
||||
return this.data.tags.find((t) => t.id === id);
|
||||
}
|
||||
|
||||
private getNextTagColor(): string {
|
||||
const usedColors = this.data.tags.map((t) => t.color);
|
||||
const availableColors = TAG_COLORS.filter((c) => !usedColors.includes(c));
|
||||
return availableColors.length > 0 ? availableColors[0] : TAG_COLORS[0];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Project Organization
|
||||
// ============================================================================
|
||||
|
||||
moveProjectToFolder(projectPath: string, folderId: string | null): void {
|
||||
if (!this.data.projectMeta[projectPath]) {
|
||||
this.data.projectMeta[projectPath] = {
|
||||
folderId: null,
|
||||
tagIds: []
|
||||
};
|
||||
}
|
||||
|
||||
this.data.projectMeta[projectPath].folderId = folderId;
|
||||
this.saveData();
|
||||
this.notifyListeners('projectMoved', { projectPath, folderId });
|
||||
}
|
||||
|
||||
addTagToProject(projectPath: string, tagId: string): void {
|
||||
if (!this.data.projectMeta[projectPath]) {
|
||||
this.data.projectMeta[projectPath] = {
|
||||
folderId: null,
|
||||
tagIds: []
|
||||
};
|
||||
}
|
||||
|
||||
const meta = this.data.projectMeta[projectPath];
|
||||
if (!meta.tagIds.includes(tagId)) {
|
||||
meta.tagIds.push(tagId);
|
||||
this.saveData();
|
||||
this.notifyListeners('projectTagAdded', { projectPath, tagId });
|
||||
}
|
||||
}
|
||||
|
||||
removeTagFromProject(projectPath: string, tagId: string): void {
|
||||
const meta = this.data.projectMeta[projectPath];
|
||||
if (!meta) return;
|
||||
|
||||
meta.tagIds = meta.tagIds.filter((id) => id !== tagId);
|
||||
this.saveData();
|
||||
this.notifyListeners('projectTagRemoved', { projectPath, tagId });
|
||||
}
|
||||
|
||||
getProjectMeta(projectPath: string): ProjectMeta | null {
|
||||
return this.data.projectMeta[projectPath] || null;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Queries
|
||||
// ============================================================================
|
||||
|
||||
getProjectsInFolder(folderId: string | null): string[] {
|
||||
return Object.keys(this.data.projectMeta).filter((projectPath) => {
|
||||
const meta = this.data.projectMeta[projectPath];
|
||||
return meta.folderId === folderId;
|
||||
});
|
||||
}
|
||||
|
||||
getProjectsWithTag(tagId: string): string[] {
|
||||
return Object.keys(this.data.projectMeta).filter((projectPath) => {
|
||||
const meta = this.data.projectMeta[projectPath];
|
||||
return meta.tagIds.includes(tagId);
|
||||
});
|
||||
}
|
||||
|
||||
getProjectCountInFolder(folderId: string | null): number {
|
||||
return this.getProjectsInFolder(folderId).length;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Utilities
|
||||
// ============================================================================
|
||||
|
||||
private generateId(prefix: string): string {
|
||||
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
// For debugging
|
||||
exportData(): ProjectOrganizationData {
|
||||
return JSON.parse(JSON.stringify(this.data));
|
||||
}
|
||||
|
||||
importData(data: ProjectOrganizationData): void {
|
||||
this.data = data;
|
||||
this.saveData();
|
||||
}
|
||||
|
||||
clearAll(): void {
|
||||
this.data = {
|
||||
version: 1,
|
||||
folders: [],
|
||||
tags: [],
|
||||
projectMeta: {}
|
||||
};
|
||||
this.saveData();
|
||||
this.notifyListeners('dataCleared', null);
|
||||
}
|
||||
}
|
||||
@@ -219,13 +219,13 @@ TODO: review this icon
|
||||
|
||||
.components-panel-item-selected {
|
||||
line-height: 36px;
|
||||
background-color: var(--theme-color-secondary);
|
||||
color: var(--theme-color-on-secondary);
|
||||
background-color: var(--theme-color-bg-4);
|
||||
color: var(--theme-color-fg-highlight);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.components-panel-item-selected .caret-icon-container {
|
||||
background-color: var(--theme-color-secondary);
|
||||
background-color: var(--theme-color-bg-4);
|
||||
}
|
||||
|
||||
.components-panel-item-selected .components-panel-item-selected {
|
||||
@@ -234,13 +234,13 @@ TODO: review this icon
|
||||
|
||||
.components-panel-item-selected:hover,
|
||||
.components-panel-item-selected:hover .caret-icon-container {
|
||||
background-color: var(--theme-color-secondary-highlight);
|
||||
background-color: var(--theme-color-bg-5);
|
||||
}
|
||||
|
||||
.components-panel-item-selected .components-panel-edit-button:hover,
|
||||
.components-panel-item-selected .components-panel-item-dropdown:hover {
|
||||
background-color: var(--theme-color-secondary);
|
||||
color: var(--theme-color-on-secondary);
|
||||
background-color: var(--theme-color-bg-3);
|
||||
color: var(--theme-color-fg-highlight);
|
||||
}
|
||||
|
||||
.is-folder-component:hover .components-panel-folder-label {
|
||||
|
||||
@@ -122,9 +122,10 @@
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-bottom-color: var(--theme-color-bg-5);
|
||||
border-bottom-color: var(--theme-color-bg-4);
|
||||
border-width: 10px;
|
||||
margin-left: -10px;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.popup-layer-popout-arrow.right {
|
||||
|
||||
@@ -1,111 +1,112 @@
|
||||
.Root {
|
||||
container-name: editortopbar;
|
||||
height: 36px;
|
||||
background-color: var(--theme-color-bg-2);
|
||||
border-bottom: 2px solid var(--theme-color-bg-1);
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.LeftSide,
|
||||
.RightSide {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
||||
// please use these general selectors with great caution
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.LeftSide {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.is-padded-s {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.is-padded {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.is-padded-l {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.LeftSide {
|
||||
> div:not(:last-child) {
|
||||
border-right: 1px solid var(--theme-color-bg-1);
|
||||
}
|
||||
}
|
||||
|
||||
.RightSide {
|
||||
> div:not(:first-child) {
|
||||
border-left: 1px solid var(--theme-color-bg-1);
|
||||
}
|
||||
}
|
||||
|
||||
.UrlBarWrapper {
|
||||
flex-grow: 1;
|
||||
min-width: 150px;
|
||||
max-width: 500px;
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.UrlBarTextInput {
|
||||
width: 100%;
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
path {
|
||||
fill: var(--theme-color-fg-highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.TooltipPositioner {
|
||||
height: 32px;
|
||||
padding-bottom: 1px;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.TopbarSelect {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
* {
|
||||
color: var(--theme-color-fg-default);
|
||||
fill: var(--theme-color-fg-default);
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
color: var(--theme-color-fg-highlight);
|
||||
fill: var(--theme-color-fg-highlight);
|
||||
}
|
||||
}
|
||||
|
||||
.ZoomSelect {
|
||||
padding-right: 4px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.DesignPreviewModeButton {
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.DeployButton {
|
||||
.Root.is-small & {
|
||||
padding: 0 4px 0 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
.Root {
|
||||
container-name: editortopbar;
|
||||
height: 36px;
|
||||
background-color: var(--theme-color-bg-2);
|
||||
border-bottom: 2px solid var(--theme-color-bg-1);
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.LeftSide,
|
||||
.RightSide {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
||||
// please use these general selectors with great caution
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.LeftSide {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.is-padded-s {
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.is-padded {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.is-padded-l {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.LeftSide {
|
||||
> div:not(:last-child) {
|
||||
border-right: 1px solid var(--theme-color-bg-1);
|
||||
}
|
||||
}
|
||||
|
||||
.RightSide {
|
||||
> div:not(:first-child) {
|
||||
border-left: 1px solid var(--theme-color-bg-1);
|
||||
}
|
||||
}
|
||||
|
||||
.UrlBarWrapper {
|
||||
flex-grow: 1;
|
||||
min-width: 150px;
|
||||
max-width: 500px;
|
||||
margin-right: -1px;
|
||||
}
|
||||
|
||||
.UrlBarTextInput {
|
||||
width: 100%;
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
path {
|
||||
fill: var(--theme-color-fg-highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.TooltipPositioner {
|
||||
height: 32px;
|
||||
padding-bottom: 1px;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.TopbarSelect {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
* {
|
||||
color: var(--theme-color-fg-highlight);
|
||||
fill: var(--theme-color-fg-highlight);
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
color: var(--theme-color-fg-highlight);
|
||||
fill: var(--theme-color-fg-highlight);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.ZoomSelect {
|
||||
padding-right: 4px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.DesignPreviewModeButton {
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.DeployButton {
|
||||
.Root.is-small & {
|
||||
padding: 0 4px 0 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
}
|
||||
|
||||
&.is-current {
|
||||
background-color: var(--theme-color-secondary);
|
||||
background-color: var(--theme-color-bg-4);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { CustomPropertyAnimation, useCustomPropertyValue } from '@noodl-hooks/useCustomPropertyValue';
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactNode, useEffect, useState } from 'react';
|
||||
|
||||
import { NodeType } from '@noodl-constants/NodeType';
|
||||
|
||||
import { Collapsible } from '@noodl-core-ui/components/layout/Collapsible';
|
||||
import { Text, TextSize, TextType } from '@noodl-core-ui/components/typography/Text';
|
||||
import { Title, TitleSize, TitleVariant } from '@noodl-core-ui/components/typography/Title';
|
||||
|
||||
import css from './NodePickerCategory.module.scss';
|
||||
import { CustomPropertyAnimation, useCustomPropertyValue } from '@noodl-hooks/useCustomPropertyValue';
|
||||
import { Title, TitleSize, TitleVariant } from '@noodl-core-ui/components/typography/Title';
|
||||
import { Text, TextSize, TextType } from '@noodl-core-ui/components/typography/Text';
|
||||
|
||||
interface NodePickerCategoryProps {
|
||||
title: string;
|
||||
@@ -86,7 +88,7 @@ export default function NodePickerCategory({
|
||||
css['Arrow'],
|
||||
isCollapsedState ? css['Arrow--is-collapsed'] : css['Arrow--is-not-collapsed']
|
||||
])}
|
||||
src="../assets/icons/editor/right_arrow_22.svg"
|
||||
src="/assets/icons/editor/right_arrow_22.svg"
|
||||
/>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ export function NodeLibrary({ model, parentModel, pos, attachToRoot, runtimeType
|
||||
createNewComment(model, pos);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
icon={<img src="../assets/icons/comment.svg" />}
|
||||
icon={<img src="/assets/icons/comment.svg" />}
|
||||
/>
|
||||
</NodePickerSection>
|
||||
) : null}
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
}
|
||||
|
||||
.lesson-item.selected {
|
||||
background-color: var(--theme-color-fg-highlight);
|
||||
color: var(--theme-color-secondary-dim);
|
||||
background-color: var(--theme-color-bg-4);
|
||||
color: var(--theme-color-fg-highlight);
|
||||
opacity: 1;
|
||||
transition: background-color 200ms, opacity 200ms;
|
||||
}
|
||||
@@ -128,9 +128,9 @@
|
||||
}
|
||||
|
||||
.lesson-item-popup {
|
||||
background-color: var(--theme-color-secondary);
|
||||
background-color: var(--theme-color-bg-3);
|
||||
width: 512px;
|
||||
color: white;
|
||||
color: var(--theme-color-fg-highlight);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--theme-color-secondary);
|
||||
background-color: var(--theme-color-bg-4);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--theme-color-secondary-highlight);
|
||||
background-color: var(--theme-color-bg-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,4 @@
|
||||
|
||||
.SearchResults {
|
||||
overflow: hidden overlay;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user