Skip to content

Manage index indicator #547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: vnext
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 35 additions & 30 deletions src/common/components/dropdown/dropdown.component.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,73 +27,57 @@

.select-text {
width: 100%;
min-width: 150px;
}

.options {
--border-select-width: 1px;
--border-select-color: var(--input-border-color-active);
list-style: none;
padding: 8px;
padding-left: 36px;
margin: 0;
position: absolute;
text-align: left;
z-index: 3;
top: calc(0% + var(--border-select-width));
top: calc(100% + var(--border-select-width));
left: calc(0px - var(--border-select-width));
width: calc(100% + var(--border-select-width) * 2);
border: var(--border-select-width) solid var(--input-border-color-active);
border-top: none;
background-color: var(--bg-input);
border-radius: var(--border-radius-xs);
max-height: 300px;
}

.select-select:hover {
--border-select-color: var(--input-border-color-active);
box-shadow: 0 0 4px var(--hover-checkbox);
background-color: var(--hover-input);
color: inherit;
}
.select-active {
--border-select-color: var(--input-border-color-active);
box-shadow: 0 0 4px var(--hover-checkbox);
background-color: var(--hover-input);
}

.select-error {
--border-select-color: var(--color-error);
}

.select-error.select-select:hover {
--border-select-color: var(--color-error);
}
.options ul {
list-style: none;
padding-left: 20px;
overflow-y: auto;
}

.options li {
font-size: 16px;
color: var(--text-color);
cursor: pointer;
display: flex;
align-items: center;
padding: 8px;
gap: 8px;
}

.options li:hover {
color: var(--input-border-color-active);
background-color: var(--hover-input);
}

.svg {
position: absolute;
left: 8px;
display: flex;
align-items: flex-end;
align-items: center;
justify-content: center;
width: 20px;
height: 21px;
height: 20px;
color: var(--input-border-color-active);
}

.svg.tick {
margin-right: 8px;
}

.veil {
position: fixed;
top: 0;
Expand All @@ -103,3 +87,24 @@
background-color: var(--veil-modal);
opacity: 0.5;
}

.select-select:hover {
--border-select-color: var(--input-border-color-active);
box-shadow: 0 0 4px var(--hover-checkbox);
background-color: var(--hover-input);
color: inherit;
}

.select-active {
--border-select-color: var(--input-border-color-active);
box-shadow: 0 0 4px var(--hover-checkbox);
background-color: var(--hover-input);
}

.select-error {
--border-select-color: var(--color-error);
}

.select-error.select-select:hover {
--border-select-color: var(--color-error);
}
52 changes: 37 additions & 15 deletions src/common/components/dropdown/dropdown.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,43 @@ import { SELECT_AN_OPTION } from './dropdown.const';
import { useA11ySelect } from '@/common/a11y';
import { Tick } from '../icons/tick-icon.component';

// import { handleFocus, handleNextFocus } from './dropdown.business';

interface Props {
name: string;
options: DropdownOptionVm[];
value?: string;
onChange: (field: string) => void;
value?: string[]; // Change to an array for multi-selection
onChange: (fields: string[]) => void; // Update to handle multiple selections
selectTitle?: string;
//TODO: css class?
isError?: boolean;
multiSelect?: boolean; // Add multiSelect prop
}

export const Dropdown: React.FC<Props> = props => {
const { name, options, selectTitle, value, onChange, isError } = props;
const {
name,
options,
selectTitle,
value = [],
onChange,
isError,
multiSelect,
} = props;

const findSelectedOption = (value: string | undefined) => {
return options.find(option => option.id === value);
const findSelectedOptions = (value: string[] | undefined) => {
return options.filter(option => value?.includes(option.id));
};

const handleChange = (option: DropdownOptionVm | undefined) => {
option ? onChange(option.id) : onChange('');
if (!option) return; // Handle undefined case
if (multiSelect) {
// Handle multi-selection logic
const newValue = value.includes(option.id)
? value.filter(v => v !== option.id) // Remove if already selected
: [...value, option.id]; // Add if not selected
onChange(newValue);
} else {
// Handle single selection
onChange([option.id]);
}
};

const {
Expand All @@ -36,12 +53,11 @@ export const Dropdown: React.FC<Props> = props => {
setIsOpen,
options: a11yOptions,
selectedOption,
setSelectedOption,
onFocusOption,
} = useA11ySelect(
options,
option => option.id,
findSelectedOption(value),
multiSelect ? undefined : findSelectedOptions(value)[0], // Handle single or multi-select
handleChange
);

Expand All @@ -64,7 +80,13 @@ export const Dropdown: React.FC<Props> = props => {
tabIndex={0}
>
<p className={classes.selectText}>
{selectedOption?.label || selectTitle || SELECT_AN_OPTION}
{multiSelect
? value
.map(id => options.find(option => option.id === id)?.label)
.join(', ') ||
selectTitle ||
SELECT_AN_OPTION
: selectedOption?.label || selectTitle || SELECT_AN_OPTION}
</p>
<ExpandDown />
{isOpen && (
Expand All @@ -88,12 +110,12 @@ export const Dropdown: React.FC<Props> = props => {
key={option.id}
role="option"
tabIndex={option.tabIndex}
aria-selected={selectedOption?.id === option.id}
onClick={() => setSelectedOption(option.id)}
aria-selected={value.includes(option.id)} // Check if the option is selected
onClick={() => handleChange(option)}
ref={onFocusOption(option)}
>
<div className={classes.svg} aria-hidden="true">
{selectedOption?.id === option.id ? <Tick /> : ''}
{value.includes(option.id) ? <Tick /> : ''}
</div>
{option.label}
</li>
Expand Down
1 change: 1 addition & 0 deletions src/common/components/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './dark-icon.component';
export * from './light-icon.component';
export * from './edit-icon.component';
export * from './key-icon.component';
export * from './canvas-setting-icon.component';
export * from './relation-icon.component';
export * from './zoom-in-icon.component';
Expand Down
12 changes: 12 additions & 0 deletions src/common/components/icons/key-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const KeyIcon = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 256 256"
>
<path d="M200,48A56,56,0,1,0,141.09,97.42L53.66,184.85a8,8,0,0,0-2.34,5.66V208H40a8,8,0,0,0-8,8v32a8,8,0,0,0,8,8H72a8,8,0,0,0,8-8V240h16a8,8,0,0,0,8-8V226.68a8,8,0,0,0,2.34-5.66l13.59-13.59,13.6,13.6a8,8,0,0,0,11.32,0l24-24a8,8,0,0,0,0-11.32l-13.6-13.6,27.08-27.08A56,56,0,1,0,200,48Zm0,32a24,24,0,1,1-24-24A24,24,0,0,1,200,80Z" />
</svg>
);
};
1 change: 1 addition & 0 deletions src/common/components/modal-dialog/modal-dialog.const.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const CANVAS_SETTINGS_TITLE = 'Canvas Settings';
export const ADD_RELATION_TITLE = 'Add Relation';
export const MANAGE_INDEX_TITLE = 'Manage Index';
export const EDIT_RELATION_TITLE = 'Edit Relation';
export const ADD_COLLECTION_TITLE = 'Add Collection';
export const EDIT_COLLECTION_TITLE = 'Edit Collection';
Expand Down
33 changes: 33 additions & 0 deletions src/core/functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IndexField } from './providers';

export const isNullOrWhiteSpace = (str?: string) => !str?.trim();

export const parseManageIndexFields = (fieldsString?: string): IndexField[] => {
const fields = fieldsString
?.split(/\s*,\s*/) // Split by commas with spaces
?.map(field => {
const [name, ...orderParts] = field.trim().split(/\s+/); // Split by one or more spaces
return { name, orderMethod: orderParts.join(' ') }; // Handle multi-word order methods
});
return fields?.filter(x => !isNullOrWhiteSpace(x.name)) as IndexField[];
};

export const clonify = <T>(input: object): T => {
const str = JSON.stringify(input);
const obj = JSON.parse(str);
return obj as T;
};

export const isEqual = (
a?: string,
b?: string,
ignoreCaseSensivity?: boolean
): boolean => {
ignoreCaseSensivity = ignoreCaseSensivity ?? true;
if (ignoreCaseSensivity) {
a = a?.toLowerCase();
b = b?.toLowerCase();
}
if (a === b) return true;
return false;
};
10 changes: 10 additions & 0 deletions src/core/model/errorHandling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface errorHandling {
errorKey?: string;
errorMessage?: string;
isSuccessful: boolean;
}

export interface Output<T> {
errorHandling: errorHandling;
data?: T;
}
20 changes: 20 additions & 0 deletions src/core/providers/canvas-schema/canvas-schema-vlatest.model.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Coords, FieldType, GUID, Size } from '@/core/model';
import { errorHandling } from '@/core/model/errorHandling';

export interface TableVm {
id: string;
fields: FieldVm[];
tableName: string;
x: number; // Canvas X Position
y: number; // Canvas Y Position
indexes?: IndexVm[];
}

export interface FieldVm {
Expand Down Expand Up @@ -40,6 +42,22 @@ export interface DatabaseSchemaVm {
isPristine?: boolean;
}

export type OrderMethod = 'Ascending' | 'Descending';

export interface IndexField {
name: string;
orderMethod: OrderMethod;
}
export interface IndexVm {
id: string;
name: string;
isUnique: boolean;
sparse: boolean;
fields: IndexField[];
fieldsString?: string;
partialFilterExpression?: string;
}

export const createDefaultDatabaseSchemaVm = (): DatabaseSchemaVm => ({
version: '0.1',
tables: [],
Expand Down Expand Up @@ -68,8 +86,10 @@ export interface CanvasSchemaContextVm {
updateTablePosition: UpdatePositionFn;
doFieldToggleCollapse: (tableId: string, fieldId: GUID) => void;
updateFullTable: (table: TableVm) => void;
updateFullTableByCheckingIndexes: (table: TableVm) => errorHandling;
addTable: (table: TableVm) => void;
addRelation: (relation: RelationVm) => void;
addIndexes: (tableId: GUID, indexes: IndexVm[]) => void;
doSelectElement: (id: GUID | null) => void;
canUndo: () => boolean;
canRedo: () => boolean;
Expand Down
19 changes: 18 additions & 1 deletion src/core/providers/canvas-schema/canvas-schema.business.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { produce } from 'immer';
import { FieldVm, RelationVm, TableVm } from './canvas-schema-vlatest.model';
import {
FieldVm,
IndexVm,
RelationVm,
TableVm,
} from './canvas-schema-vlatest.model';
import { DatabaseSchemaVm } from './canvas-schema-vlatest.model';
import { GUID } from '@/core/model';

Expand Down Expand Up @@ -105,3 +110,15 @@ export const updateRelation = (
draft.relations[index] = relation;
}
});

export const updateIndexes = (
tableId: GUID,
indexes: IndexVm[],
dbSchema: DatabaseSchemaVm
): DatabaseSchemaVm =>
produce(dbSchema, draft => {
const tableIndex = draft.tables.findIndex(t => t.id === tableId);
if (tableIndex !== -1) {
draft.tables[tableIndex].indexes = indexes;
}
});
Loading