1.3.3 • Published 3 years ago

@nokacreative/generic-react-table v1.3.3

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

A generic table written in React and Typescript with many features.

View the demo here. The demo's repository can also be found here.

Installation

npm i @nokacreative/generic-react-table

or

yarn add @nokacreative/generic-react-table

Features

  • Column sorting, resizing, reordering, and pinning
  • Sorting
  • Paging
  • Searching
  • Filtering
  • Row selection (single and multiple)
  • Integration with a server side
  • Automatic handling (rendering, sort calculations, filter generation, etc.) of data types such as plain and rich text, numbers, dates, money, colours

Usage

Overview

  1. Define your columns
  2. Plug them and your data into <Table>
import { Table, TableColumn, DataType } from '@nokacreative/generic-react-table'
import { data } from './database'

const columns: TableColumn<UserModel>[] = [
  {
    headerText: 'Column 1',
    propertyPath: 'column1',
    type: DataType.PLAIN_TEXT,
  }
]

const App = () => (
  <Table columns={columns} data={data} />
)

Code Samples

Gives an overview of the code used in the demo. For the full code, see its repository.

All custom types that will be shown in these examples are described in-depth at the bottom of this document, in the "Comprehensive API reference" section.

Basic table

Basic Table

Below are the definitions used for this particular sample. The CUSTOM and RELATION data types give you a render() function for you to decide how you want to render the cell, while all other data types automatically render their contents based on the given propertyPath.

import { Table, TableColumn, DataType } from '@noka/generic-react-table'
import { database } from './database'

interface UserModel {
  id: string
  userName: string
  displayName: string
  dateJoined: number
  groupId: string
}

const columns = (groups: GroupModel[]): TableColumn<UserModel>[] => [
  {
    headerText: 'User Name',
    type: DataType.CUSTOM,
    render: (user: UserModel) => (
      <Link to={ROUTES.userDetails(user.id)}>{user.userName}</Link>
    ),
  },
  {
    propertyPath: 'displayName',
    headerText: 'Display Name',
    type: DataType.PLAIN_TEXT,
  },
  {
    propertyPath: 'groupId',
    headerText: 'Group',
    type: DataType.RELATION,
    relatedDataList: groups,
    render: (relatedGroup: GroupModel) => (
      <Link to={ROUTES.groupDetails(relatedGroup.id)}>{relatedGroup.name}</Link>
    ),
  },
  {
    propertyPath: 'dateJoined',
    headerText: 'Date Joined',
    type: DataType.DATE,
    showTime: true,
    defaultWidth: 'max-content',
  },
]

const App = () => (
  <Table columns={columns(database.groups)} data={database.users} />
)

Column sorting and resizing

Sorting and resizing

const columns: TableColumn<GroupModel>[] = [
  {
    headerText: 'Name',
    type: DataType.CUSTOM,
    render: (group: GroupModel) => (
      <Link to={ROUTES.groupDetails(group.id)}>{group.name}</Link>
    ),
    isSortable: true,
    sortAccessor: (group: GroupModel) => group.name,
    defaultSortDirection: SortDirection.ASCENDING,
    isResizable: true,
    defaultWidth: '200px',
  },
  {
    propertyPath: 'description',
    headerText: 'Rich Description',
    type: DataType.RICH_TEXT,
    isResizable: true,
  },
  {
    propertyPath: 'numUsers',
    headerText: '# Users',
    type: DataType.NUMBER,
    isSortable: true,
    defaultWidth: '0.5fr',
  },
]

const App = () => (
  <Table columns={columns(database.groups)} data={database.users} />
)

Pinned columns and column reordering

Pinned columns

Pinning columns allow them to stay in place, even if everything else needs to scroll. There is no new code in the column definitions for this example--all you need are two properties in the Table component:

<Table
  columns={columns}
  data={database.tags}
  numPinnedColumns={1} <--
  canReorderColumns    <--
/>

Minimum Number of Rows and Paging

Paging

Setting a minimum number of rows allows the table to stay a consistent length through different pages of possibly different data lengths. Ex. if min num rows are set to 5 when there are only 3 results:

Min num rows

<Table
  columns={columns}
  data={database.products}
  minNumRows={5}
  usePaging
  defaultPageSize={5}
  pageSizeOptions={[1, 2, 5, 10]}
  showResultCount
/>

Row selection

Single select and multiple-select are both supported.

Row selection

function onSingleSelection(user: UserModel) {
  alert(`You have selected the user with ID ${user.id}, name ${user.userName}`)
}

function onMultipleSelections(_: UserModel, allSelections: UserModel[]) {
  setButtonDisabled(allSelections.length === 0)
}

<Table
  columns={columns(groups)}
  data={users}
  onRowSelected={onSingleSelection}
/>

<Table
  columns={columns(groups)}
  data={users}
  onRowSelected={onMultipleSelections}
  keepSelections
/>

Row Reordering

Row Reordering

Row orders are persisted through paging, sorting, and filtering.

A move indicator is automatically added to each cell in the first column. A callback function can optionally be passed in for when a row is reordered.

function onRowReordred(row: ProductModel, fromIndex: number, toIndex: number) {
  ...
}

<Table
  columns={columns}
  data={products}
  useRowReordering
  onRowReordered={onRowReordred}
/>

Server-side Paging and Sorting

The necessary properties are useServerSidePaging and onPage(), and useServerSideSorting and onSort(). totalNumPages and totalNumResults must also be passed in when using server-side paging.

// Container
import { SortingRule } from '@noka/generic-react-table'

function doFetch() {
  fakeClient.fetchProducts(currParams.current).then((response: ServerResponse) => {
    setData(response.products) // <-- This data is passed into the view, which updates the table
    setTotalNumPages(response.totalNumPages)
    setTotalNumResults(response.totalNumResults)
    setLoading(false)
  })
}

function onSort(currentSortingRules: SortingRule<ProductModel>[]) {
  setLoading(true)
  currParams.current.sortingRules = currentSortingRules
  doFetch()
}

function onPage(pageIndex: number, pageSize: number) {
  setLoading(true)
  currParams.current.pageIndex = pageIndex
  currParams.current.pageSize = pageSize
  doFetch()
}

// View
<Table
  columns={columns}
  data={props.data}
  minNumRows={5}
  showResultCount

  // Paging
  usePaging
  useServerSidePaging
  onPage={props.onPage}
  totalNumPages={props.totalNumPages}
  totalNumResults={props.totalNumResults}
  // Optional
  defaultPageSize={DEFAULT_PAGE_SIZE}
  pageSizeOptions={[2, 3, 5]}

  // Sorting
  useServerSideSorting
  onSort={props.onSort}
  // Optional
  canSortMultipleColumns

  isLoading={props.isLoading}
/>

Search and filter

Search and filter

<Table
  columns={columns}
  data={database.people}
  isSearchable
  isFilterable
  tableName="People"
  showResultCount
/>

// Example columns
{
  propertyPath: 'age',
  headerText: 'Age',
  type: DataType.NUMBER,
  filterType: FilterType.EXACT_MATCH,
}

{
  propertyPath: 'dateOfBirth',
  headerText: 'Date of Birth',
  type: DataType.DATE,
  filterType: FilterType.RANGED,
}

Server-side search and filter

Works the same way as server-side paging and sorting; with the properties useServerSideSearching and onSearch(), and useServerSideFiltering and onFilter().

<Table
  columns={columns}
  data={props.data}

  // Search
  isSearchable
  useServerSideSearching
  onSearch={props.onSearch}

  // Filter
  isFilterable
  useServerSideFiltering
  onFilter={props.onFilter}

  // Necessary for either
  totalNumResults={props.totalNumResults}

  isLoading={props.isLoading}
  showResultCount
  tableName="People from Server"
/>

Style overriding

Alternate colours

All colours are defined as CSS variables and scoped under the noka-table-colors class. By simply overriding that class's variables, the table's colours can be customized to your liking; refer to the last section of the API reference to see all availble variables.

Variables can be overriden via directly setting them in the aforementioned class, which will change all tables globally:

.noka-table-colors {
  --headerCellBg: #e2e2e2;
  --headerCellText: #181818;
  --active: #00cae4;
}

Or by adding an ID (or extra className) to the table, and scoping the colours there:

<Table id="my-table" {...} />

#my-table {
  --cellBg: #181818;
  --alternateCellBg: black;
}

A full list of the available variables, their default values, and their descriptions can be found in the "Style overriding - CSS colour variables" section under the API reference.

Text overriding

(Pardon the Google Translate)

Text overriding

All messages and formatters (eg. date and money) can be overriden.

import { MessageOverrides, FormatterOverrides } from '@nokacreative/generic-react-table'

const messageOverrides: MessageOverrides = {
  noData: (pluralEntityName?: string) => `il n'y a pas de ${pluralEntityName}`,
  noFilterResults: "Il n'y a aucun résultat pour les filtres donnés!",
  noSearchResults: "Il n'y a aucun résultat pour le terme de recherche donné!",
  filters: {
    moneySymbol: '€',
    togglerButtonTooltip: 'Filtre',
    clearButtonTooltip: 'Effacer tous les filtres',
    placeholders: {
      genericFilter: 'Filtre',
      exactMatch: 'exactement',
      partialMatch: 'contient',
      (etc.)
    },
    datePicker: {
      dateFormat: 'dd MMMM yyyy',
      locale: 'fr',
    },
  }
}

const formatterOverrides: FormatterOverrides = {
  date: (timeValue: number) =>
    new Date(timeValue).toLocaleDateString('fr-FR', {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
    }),
  money: (value: number) => `€${value}`,
}

<Table
  {...}
  messageOverrides={messageOverrides}
  formatterOverrides={formatterOverrides}
/>

To see the full list of overridable properties, see the "Text Overrides" section under the API reference.

react-datepicker is used for the Datepicker. Thus, other than setting the locale in the overrides as written above, changing its locale contains a bit more steps; refer to the Localization section of the linked package to see how it's done.

Rich-Text Configurations

Rich-Text Configurations

Rich text columns sanitize using a specific whitelist of formatting tags + <br> and <p> by default. You can override this behaviour in a variety of ways, such as disallowing line-breaks, setting your own sanitization options, or transforming specific tags into others. Below are some sample column definitions (with all non-relevant properties stripped out):

{
  headerText: 'No Line-breaks',
  disallowLineBreaks: true,
},
{
  headerText: 'Custom (only <b> allowed)',
  sanitizationOptions: {
    allowedTags: ['b'],
  },
},
{
  headerText: 'Custom (spans with class attribute allowed)',
  sanitizationOptions: {
    allowedTags: ['span'],
    allowedAttributes: {
      span: ['class'],
    },
  },
},
{
  headerText: 'Custom (transform <b> into <i>)',
  sanitizationOptions: {
    transformTags: { b: 'i' },
  },
}

To see the full list of options and their descriptions, refer to the "Rich Text Columns" sections in the API reference below.

Comprehensive API reference

Common enums

Data (Column) Types

Enum NameDescription
PLAIN_TEXTAny non-formatted string.
RICH_TEXTAny string with HTML formatting. Note that only simple tags (such as i, b, br, etc.) are allowed.
NUMBERAny numeric value.
DATEAny date. It expects a time value, eg. the result of Date.getTime().
COLORAny valid CSS colour value, such as blue, #0000FF, etc.
MONEYAny plain numeric value. It is automatically formatted when rendered.
RELATIONA value that is used in another dataset. Ex. Users in a system with groups may have a groupId property. When this data type is used for a column, the corresponding Group object is automatically retrieved, and can be rendered however you want.
CUSTOMAnything that does not fall into the above categories.

There's also a self-explanatory SortDirection enum that is referenced below, with values of ASCENDING and DESCENDING.

Filter Types

Enum NameDescription
EXACT_MATCHMatch exactly what the user types (but is case-insenstive).
PARTIAL_MATCHReturns a match when data contains the search term.
RANGEDHas a minimum and maximum. For numeric types only (including DATE and MONEY).
MINIMUMReturns a match when the value is at least (and including) a certain threshold.
MAXIMUMReturns a match when the value is at most (and including) a certain threshold.

Table component properties

Base

PropertytypeRequiredDescription
columnsTableColumn[]YThe column definitions.
dataT[]YThe data that the table is to render.
pluralEntityNamestringNEx. 'products', 'users', etc. Used in result messages, num selected messages, and when the table is empty (ex. "No items to display").
numPinnedColumnsnumberNThe number of columns that should be pinned, if any.
canReorderColumnsbooleanNWhether or not columns can be reordered.
minNumRowsnumberNThe minimum number of rows the table must have.
showResultCountbooleanNWhether or not to show the number of results in the table.
isLoadingbooleanNShows a loader if true.
loaderReact.ReactNodeNOverride the default loading text with a component.
canSortMultipleColumnsbooleanNFalse by default.
tableNamestringNThe name of the table. Displayed in the header, if given.
searchDebounceMilisnumberNThe number of miliseconds to debounce searching and filtering inputs with. 200 by default.
headerCellTemplateFunctionNA template to override the default rendering of the header cell with. (Check below for the function signature)

The function signature of headerCellTemplate is as such:

(
  headerText: string,
  columnDefinition: TableColumn<T>,
  sortIndicator: JSX.Element | undefined,
  onSort: (() => void) | undefined,
  filter: JSX.Element | undefined
) => JSX.Element

All properties below are technically optional, but required if you want the specific functionality defined by each header.

Paging

PropertytypeRequiredDescription
usePagingbooleanYWhether or not the table should be paged. False by default.
defaultPageSizenumberNThe number of results that should be in each page.
pageSizeOptionsnumber[]NGenerates a dropdown that allows the user to select their preferred page size. If not given, the dropdown will not appear.
useServerSidePagingbooleanNWhether or not to use server-side paging. False by default.
onPage(pageIndex: number, pageSize: number) => voidConditionalNecessary when useServerSidePaging is true.
totalNumPagesnumberConditionalNecessary when useServerSidePaging is true. Should be returned from the server.

Row selection

PropertytypeRequiredDescription
onRowSelected(row: T, allSelections: T[], isDeselected: boolean) => voidYWhat to do when a row has been selected. allSelections will be empty if only single selections are supported.
keepSelectionsbooleanNWhether or not to keep selections, eg. allow multiple selections. False by default.

Row reordering

PropertytypeRequiredDescription
useRowReorderingbooleanYWhether or not to enable row reordering.
onRowReordered(row: T, fromRowIndex: number, toRowIndex: number) => voidNA callback function for when a row has been reordered.

Searching

PropertytypeRequiredDescription
isSearchablebooleanYWhether or not the table can be searched through. False by default.
useServerSideSearchingbooleanNFalse by default.
onSearch(currentFilters: FilterMap) => voidConditionalNecessary if useServerSideFiltering is true.

Filtering

PropertytypeRequiredDescription
isFilterablebooleanYWhether or not the table can be filtered. False by default.
showFilteredResultCountbooleanNIf true, the result count will say "Showing x of y results (filtered from z)". False by default.
useServerSideFilteringbooleanNFalse by default.
onFilter(currentFilters: FilterMap) => voidConditionalNecessary if useServerSideFiltering is true.

Server-only common properties

PropertytypeRequiredDescription
totalNumResultsnumberYThe total number of results.

Column property break down

Common properties

PropertytypeRequiredDescription
typeDataTypeYThe type of the column.
headerTextstringYThe text that is displayed in the column's header.
defaultWidthstringNHow wide you want the column to be. Can be in px, or take in any Grid Layout value.
isResizablebooleanNWhether or not the column can be resized.
isSortablebooleanNWhether or not the column can be sorted.
sortAccessor(row: T) => anyNAll column types (aside from CUSTOM and RELATION) sort based on their dataType by default. You can override this behaviour by returning the data you would instead like to sort by.
defaultSortDirectionSortDirectionNThe direction to sort by, by default.
searchMatcher(row: T, searchTerm: string) => booleanNLike with the sortAccessor, all non-custom and non-relation columns have their own code for determining whether or not its data includes the search term. You can override this behaviour (or define it for custom/relational columns) with this function.
emptyCellTextstringNWhat this specific column should display if there is no data. Is - by default, and can be overridden for the entire table with the messageOverrides.emptyCellText property.

Plain text columns

PropertytypeRequiredDescription
propertyPathstringYWhere the property lies in your model. Ex. if you have UserModel { id: string }, and you want an ID column, you would write propertyPath: 'id'.
filterTypeFilterType (PARTIAL_MATCH or EXACT_MATCH only)NOnly used if isFilterable is set to true in the Table component. Defaults to partial.

Rich text columns

PropertytypeRequiredDescription
propertyPathstringYWhere the property lies in your model. Ex. if you have UserModel { bio: string }, and you want an Bio column, you would write propertyPath: 'bio'.
disallowLineBreaksbooleanNWhether or not to remove line-breaks. False by default.
sanitizationOptionsobjectNCustom options. Will completely override default sanitization options. The object definition and default values are listed below.

Sanitization Options

PropertytypeRequiredDefault ValueDescription
allowedTagsstring[]N[ 'b', 'i', 'u', 'strikethrough', 'strong', 'small', 'em', 'mark', 'ins', 'del', 'sub', 'sup' ]A whitelist of allowed HTML tags. Everything else will be removed.
allowedAttributes{ tag: string: string[] }N{}A whitelist of all allowed attributes, per-tag.
transformTags{ tag: string: string | Function }N{}Specify tags to transform from and to, ex. { b: 'i' } will turn all <b> tags into <i> ones.

The function signature for transformTags is as follows:

(
  tagName: string,
  attribs: { [attr: string]: string }
) => {
  tagName: string;
  attribs: { [attr: string]: string }
}

// Ex.
{
  transformTags: {
    ol: (tagName: string, attribs: { [attr: string]: string }) => {
      // do stuff
      return {
        tagName: 'ul',
        attribs: {
          class: 'foo'
        }
      }
    }
  }
}

You can also refer to the sanitize-html-react documentation to see usage samples.

Number and Money columns

PropertytypeRequiredDescription
propertyPathstringYWhere the property lies in your model.
filterTypeFilterType (excluding PARTIAL_MATCH)ConditionalOnly used if isFilterable is set to true in the Table component. It does not default to anything, so it must be specified in this case.

Date columns

PropertytypeRequiredDescription
propertyPathstringYWhere the property lies in your model.
showTimebooleanNWhether or not to show the time with the date.
showSecondsbooleanNWhether or not to include seconds with the time. False by default.
filterTypeFilterType (excluding PARTIAL_MATCH)ConditionalOnly used if isFilterable is set to true in the Table component. It does not default to anything, so it must be specified in this case.

Colour columns

PropertytypeRequiredDescription
propertyPathstringYWhere the property lies in your model.
filterIsMultiplebooleanNOnly used if isFilterable is set to true in the Table component. Defaults to false. Determines whether or not multiple selections can be made in the filter.
showTextbooleanNWhether or not to show the text value of the colour (ex. red or #FF0000), as opposed to just the colour itself. False by default.

Relation columns

PropertytypeRequiredDescription
propertyPathstringYWhere the property lies in your model.
relatedDataList{ id: string }[] | { key: string: any }YWhere to get the related data from.
render(relatedData: any) => string | JSX.ElementYHow to display the cell data.
filterCustomFilterConditionalMust be defined if isFilterable is set to true in the Table component.

Custom columns

PropertytypeRequiredDescription
render(data: T) => string | JSX.Element | nullYHow to display the cell data.
filterCustomFilterConditionalMust be defined if isFilterable is set to true in the Table component.

Custom Filters

Used for custom and relation columns.

Text

PropertytypeRequiredDescription
typeCustomFilterType.TEXTYTells the compiler that you want to use this type of filter.
matcher(value: string, row: T, relatedDataItem?: any) => booleanYDetermines whether or not the given value should return a match for the row. relatedDataItem is passed in when the column is a relational one.

Number

PropertytypeRequiredDescription
typeCustomFilterType.NUMBERYTells the compiler that you want to use this type of filter.
isRangedbooleanNWhether or not to use a ranged filter (eg. with min and max values).
matcher(value: number, row: T, relatedDataItem?: any) => booleanConditionalDetermines whether or not the given value should return a match for the row. relatedDataItem is passed in when the column is a relational one. Either this or the ranged version below must be defined.
matcher( min: number | '', max: number | '', row: T, relatedDataItem?: any ) => boolean }ConditionalSame as above, but for ranged filters only. An empty value is passed in as ''.

Dropdown

PropertytypeRequiredDescription
typeCustomFilterType.DROPDOWNYTells the compiler that you want to use this type of filter.
optionsDropdownOption[]YThe options to pass into the dropdown. DropwonOption is defined as { text: string, value: any, render?: () => ReactNode }.
matcher(value: any, row: T, relatedDataItem?: any) => booleanDetermines whether or not the given value should return a match for the row. relatedDataItem is passed in when the column is a relational one.
isMultiplebooleanNWhether or not multiple selections can be made for this filter.

Style overriding - CSS colour variables

Common

VariableDefault valueDescription
--text#505050The colour of all normal text elements. Used in non-header cells, icons, dropdowns, and pagination buttons.
--headerText#777777The colour of table headers and input-clear icons.
--links#1abc9cUsed for actual links, and any element that behaves as one (ex. the pagination buttons/links, on hover).
--textAgainstNoBgWhatever text isAnything that is rendered against a transparent background, ex. the header icons, pagination buttons, etc.
--inputBgcellBgThe background of any input.
--inputTexttextThe text colour of any input.
--active#ff9933The colour of any active or focused elements.

Table

VariableDefault valueDescription
--cellBgwhiteThe background of a normal cell/row.
--alternateCellBg#f7f5f5The background of the alternating rows.
--headerCellBg#bc451aThe background of the header cells.
--headerCellTextwhiteThe text colour of the header cells.
--rowHover#ffd6adUsed when a selectable row is hovered.
--linksDark#008066A darker version of --links, used exclusively when a selectable row is hovered, as the colours of the row hover may cause links to look too faint.
--separator#b39f98The colour of the table borders.

Text overrides

Everything is optional.

General Messages

PropertytypeDefault valueDescription
noData(pluralEntityName?: string) => stringNo {pluralEntityName || items} to display.For when there is no data passed into the table.
noFilterResultsstringNo results are available for the selected filters.-
noSearchResultsstringNo results are available for the given search term.-
xResults(x: number, pluralEntityName?: string) => stringx {pluralEntityName || results}The number of results text at the bottom left corner of the table.
showingXofYResults(x: number, y: number, pluralEntityName?: string) => stringShowing x out of y {pluralEntityName || results}Used when there is paging.
resultsFilteredFrom(from: number, pluralEntityName?: string) => string(Filtered from from)Used when there are filters. Appended to the end of either of the above texts.
filtersobject-See the section below.
searchTogglerButtonstringSearchThe tooltip that appears when you hover over the Search icon in the table's header.
emptyCellstring-For when a cell is empty. Can be overridden for a specific column with the column's emptyCellText property.

Filter Messages/Text (General)

PropertytypeDefault valueDescription
moneySymbolstring$Used in front of money filters.
togglerButtonTooltipstringFilterThe tooltip that pops up when you hover over the Filter icon in the table's header.
clearButtonTooltipstringClear All FiltersThe tooltip that pops up when you hover over the Clear-Filter icon in the table's header.
placeholdersobject-See the section below.
datePickerobject-See the second section below.
emptyDropdownstring(Empty)Used when a dropdown filter has no options.

Filter Placeholders

All of these are strings.

PropertyDefault valueDescription
genericFilterFilterText for anything that is not covered by the below variables.
exactMatchExactFor text filters that use an exact match
partialMatchContainsFor text filters that use a partial match
dateExactExactlyFor date filters that use an exact match
dateRangeFromFromFor ranged date filters: the 'from' value
dateRangeToToFor ranged date filters: the 'to' value
dateMinFromFor date filters that check against a minimum threshold
dateMaxUntilFor date filters that check against a maximum threshold
numericExactExactlyFor numberic (number or money) filters that use an exact match
numericRangeFromMinFor ranged number filters: the 'min' value
numericRangeToMaxFor ranged number filters: the 'max' value
numericMinAt leastFor numeric filters that check against a minimum threshold
numericMaxAt mostFor numeric filters that check against a maximum threshold
dropdownSingleFilterFor dropdown filters where only a single selection can be made
dropdownMultipleMultipleFor dropdown filters where multiple selections can be made

Datepicker

PropertytypeDefault valueDescription
dateFormatstring'MM/dd/yyyy'Note these should be written in Moment.js formats.
timeFormat(withSeconds: boolean) => stringhh:mm a or hh:mm:ss a with seconds-
localestringundefined-

Formatters

PropertySignatureDefault valueDescription
date(timeValue: number, showTime: boolean,showSeconds: boolean) => stringEx. 01/29/2021 11:50:30 AMHow to format any given date, with the value passed in as what's retrieved from .getTime().
money(value: number) => stringEx. $100,000,000.00How to format moneric values.
1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.2.0

3 years ago

1.3.0

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago