Skip to content

Added Table, TableItem & ErrorPrompt #347

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

Merged
merged 11 commits into from
Apr 6, 2021
38 changes: 38 additions & 0 deletions src/components/ErrorPrompt/RNErrorPrompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NodeWidget, QErrorMessage, QErrorMessageSignals } from "@nodegui/nodegui";
import { throwUnsupported } from "../../utils/helpers";
import { RNWidget } from "../config";
import { DialogProps, setDialogProps } from "../Dialog/RNDialog";

export interface ErrorPromptProps extends DialogProps<QErrorMessageSignals> {
message: string;
}

function setErrorPromptProps(widget: RNErrorPrompt, newProps: ErrorPromptProps, oldProps: ErrorPromptProps) {
const setter: ErrorPromptProps = {
set message(message: string) {
widget.showMessage(message);
widget.close();
},
};
Object.assign(setter, newProps);
setDialogProps(widget, newProps, oldProps);
}

export class RNErrorPrompt extends QErrorMessage implements RNWidget {
setProps(newProps: ErrorPromptProps, oldProps: ErrorPromptProps): void {
setErrorPromptProps(this, newProps, oldProps);
}
appendInitialChild(child: NodeWidget<any>): void {
throwUnsupported(this);
}
appendChild(child: NodeWidget<any>): void {
throwUnsupported(this);
}
insertBefore(child: NodeWidget<any>, beforeChild: NodeWidget<any>): void {
throwUnsupported(this);
}
removeChild(child: NodeWidget<any>): void {
throwUnsupported(this);
}
static tagName = "error-prompt";
}
43 changes: 43 additions & 0 deletions src/components/ErrorPrompt/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Fiber } from "react-reconciler";
import { AppContainer } from "../../reconciler";
import { ComponentConfig, registerComponent } from "../config";
import { RNErrorPrompt, ErrorPromptProps } from "./RNErrorPrompt";

class ErrorPromptConfig extends ComponentConfig {
tagName: string = RNErrorPrompt.tagName;
shouldSetTextContent(nextProps: ErrorPromptProps): boolean {
return false;
}
createInstance(newProps: ErrorPromptProps, rootInstance: AppContainer, context: any, workInProgress: Fiber): RNErrorPrompt {
const widget = new RNErrorPrompt();
widget.setProps(newProps, { message: "" });
return widget;
}
commitMount(instance: RNErrorPrompt, newProps: ErrorPromptProps, internalInstanceHandle: any): void {
if (newProps.visible !== false && newProps.open !== false) {
instance.show();
}
return;
}
commitUpdate(instance: RNErrorPrompt, updatePayload: any, oldProps: ErrorPromptProps, newProps: ErrorPromptProps, finishedWork: Fiber): void {
instance.setProps(newProps, oldProps);
}
}
/**
* ErrorPrompt inherits the functionality of nodegui's `QErrorMessage`
* @property `message` the message that needs to be displayed
* @example
* ```javascriptreact
* function ErrorApplet(){
* const [open, setOpen] = useState(false);
* return (
* <View>
* <ErrorPrompt open={open} message="Error message!"/>
* <Button text="Error" on={{clicked:()=>setOpen(true)}}/>
* </View>
* )
* }
* ```
*/

export const ErrorPrompt = registerComponent<ErrorPromptProps>(new ErrorPromptConfig());
173 changes: 173 additions & 0 deletions src/components/Table/RNTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { FlexLayout, NodeWidget, QTableWidget, QTableWidgetItem, QTableWidgetSignals, SortOrder } from "@nodegui/nodegui";
import { ViewProps, setViewProps } from "../View/RNView";
import { RNComponent } from "../config";
import { RNTableItem } from "../TableItem/RNTableItem";

export interface CellRange {
row: number;
column: number;
}
interface HorizontalHeader extends Omit<CellRange, "row"> {
item: QTableWidgetItem;
}
interface VerticalHeader extends Omit<CellRange, "column"> {
item: QTableWidgetItem;
}

interface CellWidget extends CellRange {
widget: NodeWidget<any>;
}

interface Sort extends Omit<CellRange, "row"> {
order?: SortOrder;
}

interface ColumnSize extends Omit<CellRange, "row"> {
width: number;
}

interface RowSize extends Omit<CellRange, "column"> {
width: number;
}

export interface TableProps extends ViewProps<QTableWidgetSignals> {
cellRange: CellRange;
horizontalHeaderItems?: HorizontalHeader[];
horizontalHeaderLabels?: string[];
verticalHeaderItems?: VerticalHeader[];
verticalHeaderLabels?: string[];
cellWidgets?: CellWidget[];
currentCell?: CellRange;
sortItems?: Sort;
selectedColumn?: number;
selectedRow?: number;
showGrid?: boolean;
columnWidth?: ColumnSize[];
rowHeight?: RowSize[];
sortingEnabled?: boolean;
hideColumns?: number[];
hideRows?: number[];
}

type CustomTableProps = Omit<TableProps, "cellRange">;

function verifyRanges({ row: rowCount, column: columnCount }: CellRange, { row, column }: Partial<CellRange>) {
if (row && (row < 0 || row > rowCount - 1)) {
console.warn(`Row "${row}" is out of range "${rowCount - 1}"`);
}
if (column && (column < 0 || column > columnCount - 1)) {
console.warn(`Column "${column}" is out range "${columnCount - 1}"`);
}
}

/**
* @ignore
*/
export const setTableProps = (widget: RNTable, newProps: CustomTableProps, oldProps: CustomTableProps) => {
const cellRange: CellRange = {
row: widget.rowCount(),
column: widget.columnCount(),
};

const setter: CustomTableProps = {
set horizontalHeaderItems(items: HorizontalHeader[]) {
for (const item of items) {
widget.setHorizontalHeaderItem(item.column, item.item);
}
},
set horizontalHeaderLabels(labels: string[]) {
widget.setHorizontalHeaderLabels(labels);
},
set verticalHeaderItems(items: VerticalHeader[]) {
for (const { row, item } of items) {
verifyRanges(cellRange, { row });
widget.setVerticalHeaderItem(row, item);
}
},
set verticalHeaderLabels(labels: string[]) {
widget.setVerticalHeaderLabels(labels);
},
set cellWidgets(cellWidgets: CellWidget[]) {
for (const { column, row, widget: cellWidget } of cellWidgets) {
verifyRanges(cellRange, { row, column });
widget.setCellWidget(row, column, cellWidget);
}
},
set currentCell({ row, column }: CellRange) {
verifyRanges(cellRange, { row, column });
widget.setCurrentCell(row, column);
},
set sortItems({ column, order }: Sort) {
verifyRanges(cellRange, { column });
widget.sortItems(column, order);
},
set selectedColumn(column: number) {
verifyRanges(cellRange, { column });
widget.selectColumn(column);
},
set selectedRow(row: number) {
widget.selectRow(row);
},
set showGrid(showGrid: boolean) {
widget.setShowGrid(showGrid);
},
set columnWidth(sizes: ColumnSize[]) {
for (const { column, width } of sizes) {
verifyRanges(cellRange, { column });
widget.setColumnWidth(column, width);
}
},
set rowHeight(sizes: RowSize[]) {
for (const { row, width } of sizes) {
verifyRanges(cellRange, { row });
widget.setRowHeight(row, width);
}
},
set sortingEnabled(sortingEnabled: boolean) {
widget.setSortingEnabled(sortingEnabled);
},
set hideColumns(columns: number[]) {
for (const column of columns) {
verifyRanges(cellRange, { column });
widget.hideColumn(column);
}
},
set hideRows(rows: number[]) {
for (const row of rows) {
verifyRanges(cellRange, { row });
widget.hideRow(row);
}
},
};
Object.assign(setter, newProps);
setViewProps(widget, newProps, oldProps);
};

/**
* @ignore
*/
export class RNTable extends QTableWidget implements RNComponent {
setProps(newProps: CustomTableProps, oldProps: CustomTableProps): void {
setTableProps(this, newProps, oldProps);
}
removeChild(child: NodeWidget<any>): void {
child.close();
}
appendInitialChild(child: RNTableItem): void {
const { cellPosition } = child;
if (!this.layout) {
this.setLayout(new FlexLayout());
}
const row = this.rowCount();
const column = this.columnCount();
verifyRanges({ row, column }, { row: cellPosition[0], column: cellPosition[1] });
this.setItem(cellPosition[0], cellPosition[1], child);
}
appendChild(child: RNTableItem): void {
this.appendInitialChild(child);
}
insertBefore(child: RNTableItem, beforeChild: RNTableItem): void {
this.appendInitialChild(child);
}
static tagName = "table";
}
48 changes: 48 additions & 0 deletions src/components/Table/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { registerComponent, ComponentConfig } from "../config";
import { Fiber } from "react-reconciler";
import { RNTable, TableProps } from "./RNTable";
import { AppContainer } from "../../reconciler";

class TableConfig extends ComponentConfig {
tagName = RNTable.tagName;
shouldSetTextContent(nextProps: TableProps): boolean {
return false;
}
createInstance(newProps: TableProps, rootInstance: AppContainer, context: any, workInProgress: Fiber): RNTable {
const widget = new RNTable(newProps.cellRange.row, newProps.cellRange.column);
widget.setProps(newProps, {});
return widget;
}
commitMount(instance: RNTable, newProps: TableProps, internalInstanceHandle: any): void {
if (newProps.visible !== false) {
instance.show();
}
return;
}
commitUpdate(instance: RNTable, updatePayload: any, oldProps: TableProps, newProps: TableProps, finishedWork: Fiber): void {
instance.setProps(newProps, oldProps);
}
}
/**
* React implementation of nodegui's [QTableWidget](https://docs.nodegui.org/docs/api/generated/classes/qtablewidget/)
* @property `cellRange` define the number of rows & columns to create
* @example
* ```javascriptreact
* return (
* <Table
cellRange={{ row: 2, column: 2 }} // 2 x 2 = 4 cells
style="flex: 1;"
horizontalHeaderLabels={["What", "How", "When"]}
verticalHeaderLabels={["yes", "this", "later"]}
hideRows={[0]} //hides 0 indexed rows
>
<TableItem cellPosition={[0, 0]} text="1" toolTip="Tooltip"/>
<TableItem cellPosition={[0, 1]} text="2"/>
<TableItem cellPosition={[1, 0]} text="3"/>
<TableItem cellPosition={[1, 1]} text="4"/>
</Table>
* )
* ```
*/

export const Table = registerComponent<TableProps>(new TableConfig());
Loading