Skip to content

Commit

Permalink
Refactor Meta class and surrounding code
Browse files Browse the repository at this point in the history
  • Loading branch information
seancolsen committed Mar 1, 2022
1 parent 02e22cb commit e6bcfdb
Show file tree
Hide file tree
Showing 50 changed files with 1,583 additions and 983 deletions.
6 changes: 0 additions & 6 deletions mathesar_ui/src/App.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ export interface SchemaResponse extends SchemaEntry, TreeItem {
tables: DBObjectEntry[];
}

// TODO: Come up with a better name for representing both tables and views
export enum TabularType {
Table = 1,
View = 2,
}

export type DbType = string;

export interface FilterConfiguration {
Expand Down
9 changes: 9 additions & 0 deletions mathesar_ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
import Header from '@mathesar/header/Header.svelte';
import { toast } from '@mathesar/stores/toast';
import { confirmationController } from '@mathesar/stores/confirmation';
import { currentSchemaId } from '@mathesar/stores/schemas';
import { beginUpdatingUrlWhenSchemaChanges } from './utils/routing';
// This is a bit of a hack to deal with our routing still being a patchwork of
// declarative and imperative logic. Without this call, the URL will not
// reliably set the query params when the schema changes. It actually _will_
// set the query params _sometimes_, but we weren't able to figure out why the
// behavior is inconsistent.
beginUpdatingUrlWhenSchemaChanges(currentSchemaId);
</script>

<ToastPresenter entries={toast.entries} />
Expand Down
60 changes: 45 additions & 15 deletions mathesar_ui/src/api/tables/records.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,51 @@
export interface Grouping {
/** Each string is a column name */
columns: string[];
mode: GroupingMode;
/**
* When `mode` === 'distinct', `num_groups` will always be `null`.
*
* When `mode` === 'percentile', `num_groups` will give the number of groups,
* as specified in the request params.
*/
num_groups: number | null;
ranged: boolean;
groups: Group[];
}

export type SortDirection = 'asc' | 'desc';
export interface SortingEntry {
/** column name */
field: string;
direction: SortDirection;
}
export type FilterCombination = 'and' | 'or';
export type FilterOperation = 'eq' | 'ne' | 'get_duplicates';
export interface FilterCondition {
/** column name */
field: string;
op: FilterOperation;
value: unknown;
}
type MakeFilteringOption<U> = U extends string
? { [k in U]: FilterCondition[] }
: never;
export type Filtering =
| FilterCondition[]
| MakeFilteringOption<FilterCombination>;

export interface GetRequestParams {
limit?: number;
offset?: number;
order_by?: SortingEntry[];
grouping?: Pick<Grouping, 'columns'>;
filters?: Filtering;
}

export type ResultValue = string | number | boolean | null;

export interface Result {
/** keys are column names */
[k: string]: ResultValue;
}

Expand Down Expand Up @@ -29,21 +74,6 @@ export interface Group {
result_indices: number[];
}

export interface Grouping {
/** Each string is a column name */
columns: string[];
mode: GroupingMode;
/**
* When `mode` === 'distinct', `num_groups` will always be `null`.
*
* When `mode` === 'percentile', `num_groups` will give the number of groups,
* as specified in the request params.
*/
num_groups: number | null;
ranged: boolean;
groups: Group[];
}

export interface Response {
count: number;
grouping: Grouping | null;
Expand Down
105 changes: 105 additions & 0 deletions mathesar_ui/src/component-library/common/utils/ImmutableMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
export default class ImmutableMap<
Key extends string | number | boolean | null,
Value,
> {
private map: Map<Key, Value>;

constructor(i: Iterable<[Key, Value]> = []) {
this.map = new Map(i);
}

/**
* This method exists to allow us to subclass this class and call the
* constructor of the subclass from within this base class.
*
* If there's a way we can use generics to avoid `any` here, we'd love to
* know.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private getNewInstance(...args: any[]): this {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
return new (this.constructor as any)(...args) as this;
}

/**
* The value supplied here will overwrite any value that is already associated
* with `key`.
*/
with(key: Key, value: Value): this {
const map = new Map(this.map);
map.set(key, value);
return this.getNewInstance(map);
}

/**
* When the same keys exist in within the entries of this instance and the
* entries supplied, the values from the entries supplied will be used instead
* of the values in this instance. This behavior is consistent with the `with`
* method.
*/
withEntries(entries: Iterable<[Key, Value]>): this {
const map = new Map(this.map);
[...entries].forEach(([key, value]) => {
map.set(key, value);
});
return this.getNewInstance(map);
}

/**
* If `key` already exists, its corresponding value will remain. If `key` does
* not exist, then the value supplied here will be used.
*/
coalesce(key: Key, value: Value): this {
return this.has(key) ? this : this.with(key, value);
}

/**
* When the same keys exist in within the entries of this instance and the
* entries supplied, the values from this instance will be used instead of the
* values from the supplied entries. This behavior is consistent with the
* `coalesce` method.
*/
coalesceEntries(other: Iterable<[Key, Value]>): this {
const map = new Map(this.map);
[...other].forEach(([key, value]) => {
if (!this.has(key)) {
map.set(key, value);
}
});
return this.getNewInstance(map);
}

without(key: Key): this {
const map = new Map(this.map);
map.delete(key);
return this.getNewInstance(map);
}

has(key: Key): boolean {
return this.map.has(key);
}

get(key: Key): Value | undefined {
return this.map.get(key);
}

get size(): number {
return this.map.size;
}

keys(): IterableIterator<Key> {
return this.map.keys();
}

values(): IterableIterator<Value> {
return this.map.values();
}

entries(): IterableIterator<[Key, Value]> {
return this.map.entries();
}

[Symbol.iterator](): IterableIterator<[Key, Value]> {
return this.entries();
}
}
30 changes: 24 additions & 6 deletions mathesar_ui/src/component-library/common/utils/ImmutableSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,38 @@ export default class ImmutableSet<T extends string | number | boolean | null> {
this.set = new Set(i);
}

with(item: T): ImmutableSet<T> {
/**
* This method exists to allow us to subclass this class and call the
* constructor of the subclass from within this base class.
*
* If there's a way we can use generics to avoid `any` here, we'd love to
* know.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private getNewInstance(...args: any[]): this {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
return new (this.constructor as any)(...args) as this;
}

with(item: T): this {
const set = new Set(this.set);
set.add(item);
return new ImmutableSet(set);
return this.getNewInstance(set);
}

union(other: ImmutableSet<T>): ImmutableSet<T> {
union(other: ImmutableSet<T>): this {
const set = new Set(this.set);
[...other.values()].forEach((value) => {
set.add(value);
});
return new ImmutableSet(set);
return this.getNewInstance(set);
}

without(item: T): ImmutableSet<T> {
without(item: T): this {
const set = new Set(this.set);
set.delete(item);
return new ImmutableSet(set);
//
return this.getNewInstance(set);
}

has(item: T): boolean {
Expand All @@ -40,4 +54,8 @@ export default class ImmutableSet<T extends string | number | boolean | null> {
valuesArray(): T[] {
return [...this.set.values()];
}

[Symbol.iterator](): IterableIterator<T> {
return this.values();
}
}
13 changes: 13 additions & 0 deletions mathesar_ui/src/component-library/common/utils/formatUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { hasStringLabelProperty } from './typeUtils';

export function formatSize(sizeInBytes: number): string {
if (sizeInBytes === 0) {
return '0 B';
Expand All @@ -13,3 +15,14 @@ export function formatSize(sizeInBytes: number): string {

return `${repValue.toFixed(2)} ${repUnit}B`;
}

/**
* If the given value has a label property and it is a string, return it.
* Otherwise, return the value itself, converted to a string.
*/
export function getLabel(v: unknown): string {
if (hasStringLabelProperty(v)) {
return v.label;
}
return String(v);
}
1 change: 1 addition & 0 deletions mathesar_ui/src/component-library/common/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export { default as CancellablePromise } from './CancellablePromise';
export { default as EventHandler } from './EventHandler';
export { default as ImmutableSet } from './ImmutableSet';
export { default as ImmutableMap } from './ImmutableMap';

// Utility Functions
export * from './domUtils';
Expand Down
7 changes: 7 additions & 0 deletions mathesar_ui/src/component-library/common/utils/typeUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function hasLabelProperty(v: unknown): v is { label: unknown } {
return typeof v === 'object' && v !== null && 'label' in v;
}

export function hasStringLabelProperty(v: unknown): v is { label: string } {
return hasLabelProperty(v) && typeof v.label === 'string';
}
1 change: 1 addition & 0 deletions mathesar_ui/src/component-library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export { default as Dropdown } from './dropdown/Dropdown.svelte';
export { default as FileUpload } from './file-upload/FileUpload.svelte';
export { default as Notification } from './notification/Notification.svelte';
export { default as Pagination } from './pagination/Pagination.svelte';
export { default as SimpleSelect } from './simple-select/SimpleSelect.svelte';
export { default as Select } from './select/Select.svelte';
export { default as TabContainer } from './tabs/TabContainer.svelte';
export { default as Tree } from './tree/Tree.svelte';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
undefined;
// Total number of pages.
//
// TODO: @seancolsen says:
// > Refactor `pageCount` to no longer be an exported prop. We're exporting it
// > just so the parent component can access the calculation done within this
// > component. That's an unconventional flow of data. I'd rather do the
// > calculation in the parent and pass it down to this component.
export let pageCount = 0;
// ARIA Label for component
Expand Down
8 changes: 8 additions & 0 deletions mathesar_ui/src/component-library/select/Select.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
<!--
@component
**NOTICE** This component will eventually be redesigned, with its props
behaving more like `SimpleSelect`.
https://github.com/centerofci/mathesar/issues/1099
-->
<script lang="ts">
import { createEventDispatcher, tick } from 'svelte';
import { Dropdown } from '@mathesar-component-library';
Expand Down
Loading

0 comments on commit e6bcfdb

Please sign in to comment.