Compare commits

..

No commits in common. "main" and "v1.0.2" have entirely different histories.
main ... v1.0.2

13 changed files with 736 additions and 998 deletions

View file

@ -1,7 +1,6 @@
import React from "react"; import React from "react";
import {Application} from "@kernelui/core"; import {Application} from "@kernelui/core";
import {DemoTable} from "./DemoTable"; import {DemoTable} from "./DemoTable";
import {RemoveDemoTable} from "./RemoveDemoTable";
export function DemoApp() export function DemoApp()
{ {
@ -10,8 +9,6 @@ export function DemoApp()
<h1>Random table</h1> <h1>Random table</h1>
<DemoTable /> <DemoTable />
<RemoveDemoTable />
</Application> </Application>
) )
} }

View file

@ -1,95 +0,0 @@
import React, {useState} from "react";
import {createSmartable} from "../src/Smartable/Smartable";
import {createColumn, createColumns} from "../src/Smartable/Column";
import {StringFilter} from "../src/Smartable/Filters/StringFilter";
import { Cell } from "../src/Smartable/Cell";
// Create main table.
const Smartable = createSmartable({
columns: createColumns(
createColumn("name", {
title: "Name",
filter: StringFilter,
}),
createColumn("actions", {
title: "Actions",
}),
),
});
export function RemoveDemoTable()
{
const [rows, setRows] = useState<string[]>([
"Formica rufa",
"Lasius niger",
"Camponotus pennsylvanicus",
"Solenopsis invicta",
"Atta cephalotes",
"Pogonomyrmex barbatus",
"Myrmica rubra",
"Dorymyrmex insanus",
"Pheidole megacephala",
"Crematogaster scutellaris",
"Tetramorium caespitum",
"Tapinoma sessile",
"Linepithema humile",
"Monomorium pharaonis",
"Odontomachus bauri",
"Paraponera clavata",
"Oecophylla smaragdina",
"Pseudomyrmex gracilis",
"Eciton burchellii",
"Anoplolepis gracilipes",
"Acromyrmex octospinosus",
"Acanthomyops claviger",
"Dorylus nigricans",
"Neivamyrmex nigrescens",
"Hypoponera punctatissima",
"Solenopsis geminata",
"Camponotus chromaiodes",
"Brachymyrmex depilis",
"Ectatomma ruidum",
"Proceratium silaceum",
"Cephalotes atratus",
"Neoponera villosa",
"Dinoponera gigantea",
"Prenolepis imparis",
"Lasius flavus",
"Formica fusca",
"Myrmecia gulosa",
"Solenopsis molesta",
"Camponotus herculeanus",
"Cataulacus granulatus",
"Daceton armigerum",
"Polyrhachis dives",
"Pheidole dentata",
"Tetramorium immigrans",
"Messor barbarus",
"Cardiocondyla obscurior",
"Nylanderia flavipes",
"Forelius pruinosus",
"Amblyopone pallipes",
]);
return (
<Smartable.Table data={rows.map((row, rowIndex) => ({
cells: {
name: {
data: row,
},
actions: {
data: null,
element: (
<Cell>
<button className={"remove"}
onClick={() => setRows(rows.toSpliced(rowIndex, 1))}>
Remove
</button>
</Cell>
)
},
},
}))} disableFilter={true} disableSort={true} />
);
}

View file

@ -1,4 +1,4 @@
import "@kernelui/core/lib/index.css"; import "@kernelui/core/lib/style.css";
import "../src/styles/smartable.less"; import "../src/styles/smartable.less";
import React from "react"; import React from "react";

View file

@ -1,18 +1,2 @@
import "./src/styles/smartable.less"; import "./src/styles/smartable.less";
export * from "./src/Smartable/AsyncManager";
export * from "./src/Smartable/Cell";
export * from "./src/Smartable/Column";
export * from "./src/Smartable/Instance";
export * from "./src/Smartable/Row";
export * from "./src/Smartable/Smartable";
export * from "./src/Smartable/Sort";
export * from "./src/Smartable/Cells/ClickableCell";
export * from "./src/Smartable/Columns/ColumnFilter";
export * from "./src/Smartable/Columns/ColumnHeading";
export * from "./src/Smartable/Filters/EnumFilter";
export * from "./src/Smartable/Filters/NumberFilter";
export * from "./src/Smartable/Filters/StringFilter";

View file

@ -1,5 +1,5 @@
{ {
"version": "1.1.2", "version": "1.0.2",
"name": "@kernelui/smartable", "name": "@kernelui/smartable",
"description": "Kernel UI Smartable.", "description": "Kernel UI Smartable.",
"scripts": { "scripts": {
@ -17,22 +17,22 @@
"@kernelui:registry": "https://code.zeptotech.net/api/packages/UIKernel/npm/" "@kernelui:registry": "https://code.zeptotech.net/api/packages/UIKernel/npm/"
}, },
"devDependencies": { "devDependencies": {
"@kernelui/core": "^1.8.2", "@kernelui/core": "^1.1.2",
"@phosphor-icons/react": "^2.1.7", "@phosphor-icons/react": "^2.1.7",
"@types/node": "^22.0.0", "@types/node": "^22.0.0",
"@types/react": "^18.3.12", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.0",
"less": "^4.2.0", "less": "^4.2.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^7.0.1", "react-router-dom": "^6.26.2",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"vite": "^6.0.1", "vite": "^5.2.11",
"vite-plugin-dts": "^4.3.0" "vite-plugin-dts": "^3.9.1"
}, },
"peerDependencies": { "peerDependencies": {
"@kernelui/core": "^1.8.2" "@kernelui/core": "^1.1.2"
}, },
"packageManager": "yarn@4.5.0" "packageManager": "yarn@4.5.0"
} }

View file

@ -91,12 +91,6 @@ class AsyncManager<CK extends ColumnKey>
*/ */
protected rowsLoaded: boolean = false; protected rowsLoaded: boolean = false;
/**
* Tells if rows need to be reinitialized or not.
* @protected
*/
protected reinitRows: boolean = false;
/** /**
* Rows data. * Rows data.
* @protected * @protected
@ -135,9 +129,6 @@ class AsyncManager<CK extends ColumnKey>
// Ignore undefined value. // Ignore undefined value.
if (rowsDefinitions == undefined) return; if (rowsDefinitions == undefined) return;
// Rows have been reinitialized.
this.reinitRows = true;
// Initialize rows data and cells definitions. // Initialize rows data and cells definitions.
this.rowsData = []; this.rowsData = [];
this.cellsDefinitions = []; this.cellsDefinitions = [];
@ -235,7 +226,6 @@ class AsyncManager<CK extends ColumnKey>
{ {
if (!( if (!(
// Checking that there is at least one changed value. // Checking that there is at least one changed value.
this.reinitRows ||
this.rowsData.length > 0 || this.cellsDefinitions.some((rowCells) => ( this.rowsData.length > 0 || this.cellsDefinitions.some((rowCells) => (
Object.keys(rowCells).length > 0 Object.keys(rowCells).length > 0
)) ))
@ -245,14 +235,11 @@ class AsyncManager<CK extends ColumnKey>
// Initialize new data. // Initialize new data.
const newData = { const newData = {
rows: !this.rowsLoaded ? undefined : this.reinitRows ? [] : [ rows: !this.rowsLoaded ? undefined : [
...(this.currentDataState?.rows ?? []) ...(this.currentDataState?.rows ?? [])
], ],
}; };
// Rows have been reinitialized.
this.reinitRows = false;
for (const [rowId, newRow] of this.rowsData?.entries()) for (const [rowId, newRow] of this.rowsData?.entries())
{ // Update value of each new row. { // Update value of each new row.
newData.rows[rowId] = { newData.rows[rowId] = {

View file

@ -4,7 +4,7 @@ import {ColumnFilter, useFilterState} from "../Columns/ColumnFilter";
/** /**
* Filter value regex. * Filter value regex.
*/ */
const filterRegex = /^([=!><])?([0-9]+)$/; export const filterRegex = /^([=!><])?([0-9]+)$/;
/** /**
* Number column filter. * Number column filter.

View file

@ -5,7 +5,7 @@ import {normalizeString} from "@kernelui/core";
/** /**
* Filter value regex. * Filter value regex.
*/ */
const filterRegex = /^([=!])?([^=!].+)$/; export const filterRegex = /^([=!])?([^=!].+)$/;
/** /**
* String column filter. * String column filter.

View file

@ -5,7 +5,7 @@ import {RowInstance, RowLoader} from "./Row";
import {useAsyncManager} from "./AsyncManager"; import {useAsyncManager} from "./AsyncManager";
import {ColumnHeading} from "./Columns/ColumnHeading"; import {ColumnHeading} from "./Columns/ColumnHeading";
import {sortRows} from "./Sort"; import {sortRows} from "./Sort";
import {AutoPaginate, classes} from "@kernelui/core"; import {AutoPaginate} from "@kernelui/core";
/** /**
* Smartable instance component properties. * Smartable instance component properties.
@ -66,10 +66,10 @@ export function PaginatedInstance<CK extends ColumnKey>(props: InstancePropertie
/** /**
* Base component for a Smartable table. * Base component for a Smartable table.
*/ */
export function Table<CK extends ColumnKey>({className, columns, children}: React.PropsWithChildren<InstanceProperties<CK>>) export function Table<CK extends ColumnKey>({columns, children}: React.PropsWithChildren<InstanceProperties<CK>>)
{ {
return ( return (
<table className={classes("smartable", className)}> <table className={"smartable"}>
<thead> <thead>
<ColumnsHeadings columns={columns} /> <ColumnsHeadings columns={columns} />
</thead> </thead>
@ -85,12 +85,9 @@ export function Table<CK extends ColumnKey>({className, columns, children}: Reac
*/ */
export function ColumnsHeadings<CK extends ColumnKey>({columns}: {columns: Columns<CK>}) export function ColumnsHeadings<CK extends ColumnKey>({columns}: {columns: Columns<CK>})
{ {
// Get feature disable options.
const {disableSort, disableFilter} = useTable<CK>();
return ( return (
<> <>
<tr className={classes("headings", disableSort === true ? "disable-sort" : undefined)}> <tr className={"headings"}>
{ // Showing title of each column. { // Showing title of each column.
Object.keys(columns).map((key) => ( Object.keys(columns).map((key) => (
<AutoColumnContextProvider key={key} columnKey={key}> <AutoColumnContextProvider key={key} columnKey={key}>
@ -99,21 +96,17 @@ export function ColumnsHeadings<CK extends ColumnKey>({columns}: {columns: Colum
)) ))
} }
</tr> </tr>
{ // Add filters if filter feature is not disabled. <tr className={"filters"}>
disableFilter !== true && ( { // Add columns filters, if there are some.
<tr className={"filters"}> (Object.entries(columns) as [CK, Column][]).map(([columnKey, column]) => (
{ // Add columns filters, if there are some. column.filter && (
(Object.entries(columns) as [CK, Column][]).map(([columnKey, column]) => ( <AutoColumnContextProvider key={columnKey as string} columnKey={columnKey}>
column.filter && ( <td>{column.filter.element}</td>
<AutoColumnContextProvider key={columnKey as string} columnKey={columnKey}> </AutoColumnContextProvider>
<td>{column.filter.element}</td> )
</AutoColumnContextProvider> ))
) }
)) </tr>
}
</tr>
)
}
</> </>
); );
} }
@ -161,7 +154,7 @@ export function TableBody<CK extends ColumnKey>({pagination}: {
sortedRows ? ( sortedRows ? (
sortedRows.map((rowData, index) => ( sortedRows.map((rowData, index) => (
// Rendering each row from its definition. // Rendering each row from its definition.
<RowInstance key={index} row={rowData} /> <RowInstance row={rowData} />
)) ))
) : ( ) : (
<RowLoader /> <RowLoader />

View file

@ -15,11 +15,6 @@ export type SmartableData<CK extends ColumnKey> = Promisable<(Promisable<RowDefi
*/ */
export interface SmartableProperties<CK extends ColumnKey> export interface SmartableProperties<CK extends ColumnKey>
{ {
/**
* Table custom class name.
*/
className?: string;
/** /**
* Table data. * Table data.
*/ */
@ -49,16 +44,6 @@ export interface SmartableProperties<CK extends ColumnKey>
*/ */
pageSize: number; pageSize: number;
}; };
/**
* Disable sort feature.
*/
disableSort?: boolean;
/**
* Disable filter feature.
*/
disableFilter?: boolean;
} }
/** /**

View file

@ -1,89 +1,81 @@
tr.headings tr.headings
{ {
&.disable-sort th
{ {
th { pointer-events: none; } position: relative;
}
&:not(.disable-sort) cursor: pointer;
{
th &::before, &::after
{ // Sorting order indicator.
transition: height 0.2s ease, background 0.2s ease, top 0.2s ease, bottom 0.2s ease;
content: "";
position: absolute;
top: 0;
bottom: 0;
display: block;
margin: auto;
box-sizing: border-box;
background: var(--background-darkest);
}
&::before
{ {
position: relative; right: calc(0.33em - 1px);
cursor: pointer; width: 2px;
height: 0;
border-radius: 2px;
}
&::after
{
right: calc(0.33em - 3px);
&::before, &::after width: 6px;
{ // Sorting order indicator. height: 6px;
transition: height 0.2s ease, background 0.2s ease, top 0.2s ease, bottom 0.2s ease; border-radius: 6px;
}
content: ""; &.asc, &.desc
position: absolute; {
top: 0; &::after, &::before
bottom: 0; {
background: var(--primary);
display: block;
margin: auto;
box-sizing: border-box;
background: var(--background-darkest);
} }
&::before &::before
{ {
right: calc(0.33em - 1px); height: 0.8em;
width: 2px;
height: 0;
border-radius: 2px;
} }
&::after }
{
right: calc(0.33em - 3px);
width: 6px; &.asc::after
height: 6px; {
border-radius: 6px; top: 0.5em;
} }
&.desc::after
{
bottom: 0.5em;
}
&.asc, &.desc .order
{ { // Sort order indicator.
&::after, &::before position: relative;
{ top: 0;
background: var(--primary); bottom: 0;
}
&::before display: block;
{ margin: auto 0;
height: 0.8em; padding: 0.3em 0.3em 0;
} align-items: center;
} justify-content: center;
color: var(--foreground-lightest);
font-size: 0.7em;
&.asc::after float: right;
{
top: 0.5em;
}
&.desc::after
{
bottom: 0.5em;
}
.order
{ // Sort order indicator.
position: relative;
top: 0;
bottom: 0;
display: block;
margin: auto 0;
padding: 0.3em 0.3em 0;
align-items: center;
justify-content: center;
color: var(--foreground-lightest);
font-size: 0.7em;
float: right;
}
} }
} }
} }

View file

@ -16,7 +16,7 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
fileName: "index", fileName: "index",
}, },
rollupOptions: { rollupOptions: {
external: ["react", "react-dom", "react-router-dom", "@phosphor-icons/react", "@kernelui/core"], external: ["@kernelui/core"],
}, },
}, },

1395
yarn.lock

File diff suppressed because it is too large Load diff