mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 01:21:33 +03:00
Fix ESLint errors, add some docs (#11339)
- Fix ESLint errors - Add documentation for *some* functions with blank documentation # Important Notes None
This commit is contained in:
parent
45ad3a751c
commit
5faddf52f0
@ -26,7 +26,8 @@
|
|||||||
"./src/types": "./src/types.d.ts"
|
"./src/types": "./src/types.d.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run"
|
"test": "vitest run",
|
||||||
|
"lint": "eslint . --max-warnings=0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tanstack/query-core": "5.54.1",
|
"@tanstack/query-core": "5.54.1",
|
||||||
|
@ -4,17 +4,19 @@
|
|||||||
// === AccessToken ===
|
// === AccessToken ===
|
||||||
// ===================
|
// ===================
|
||||||
|
|
||||||
/** Payload for the `save-access-token` event that saves an access token to a credentials file. */
|
/** Credentials to be saved to a credentials file. */
|
||||||
export interface AccessToken {
|
export interface AccessToken {
|
||||||
/** The JWT token to save. */
|
/** The user's JWT token. */
|
||||||
readonly accessToken: string
|
readonly accessToken: string
|
||||||
/** The Cognito app integration client id. */
|
/** The ID for the Cognito app integration. */
|
||||||
readonly clientId: string
|
readonly clientId: string
|
||||||
/** The refresh token taken from authorization flow. */
|
/** The refresh token taken from the authorization flow. */
|
||||||
readonly refreshToken: string
|
readonly refreshToken: string
|
||||||
/** The Cognito url to refresh the token. */
|
/** The Cognito URL to refresh the token. */
|
||||||
readonly refreshUrl: string
|
readonly refreshUrl: string
|
||||||
/** The when the token will expire.
|
/**
|
||||||
* This is a string representation of a date in ISO 8601 format (e.g. "2021-01-01T00:00:00Z"). */
|
* The expiry time of the token.
|
||||||
|
* This is a string representation of a date in ISO 8601 format (e.g. "2021-01-01T00:00:00Z").
|
||||||
|
*/
|
||||||
readonly expireAt: string
|
readonly expireAt: string
|
||||||
}
|
}
|
||||||
|
12
app/common/src/appConfig.d.ts
vendored
12
app/common/src/appConfig.d.ts
vendored
@ -1,17 +1,21 @@
|
|||||||
/** @file Functions for managing app configuration. */
|
/** @file Functions for managing app configuration. */
|
||||||
|
|
||||||
/** Read environment variables from a file based on the `ENSO_CLOUD_ENV_FILE_NAME`
|
/**
|
||||||
|
* Read environment variables from a file based on the `ENSO_CLOUD_ENV_FILE_NAME`
|
||||||
* environment variable. Reads from `.env` if the variable is blank or absent.
|
* environment variable. Reads from `.env` if the variable is blank or absent.
|
||||||
* DOES NOT override existing environment variables if the variable is absent. */
|
* DOES NOT override existing environment variables if the variable is absent.
|
||||||
|
*/
|
||||||
export function readEnvironmentFromFile(): Promise<void>
|
export function readEnvironmentFromFile(): Promise<void>
|
||||||
|
|
||||||
/** An object containing app configuration to inject.
|
/**
|
||||||
|
* An object containing app configuration to inject.
|
||||||
*
|
*
|
||||||
* This includes:
|
* This includes:
|
||||||
* - the base URL for backend endpoints
|
* - the base URL for backend endpoints
|
||||||
* - the WebSocket URL for the chatbot
|
* - the WebSocket URL for the chatbot
|
||||||
* - the unique identifier for the cloud environment, for use in Sentry logs
|
* - the unique identifier for the cloud environment, for use in Sentry logs
|
||||||
* - Stripe, Sentry and Amplify public keys */
|
* - Stripe, Sentry and Amplify public keys
|
||||||
|
*/
|
||||||
export function getDefines(serverPort?: number): Record<string, string>
|
export function getDefines(serverPort?: number): Record<string, string>
|
||||||
|
|
||||||
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
|
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/** @file Functions for managing app configuration. */
|
/** @file Functions for managing app configuration. */
|
||||||
import * as fs from 'node:fs/promises'
|
import * as fs from 'node:fs/promises'
|
||||||
import * as path from 'node:path'
|
import * as path from 'node:path'
|
||||||
|
import * as process from 'node:process'
|
||||||
import * as url from 'node:url'
|
import * as url from 'node:url'
|
||||||
|
|
||||||
// ===============================
|
// ===============================
|
||||||
@ -19,14 +20,13 @@ export async function readEnvironmentFromFile() {
|
|||||||
const filePath = path.join(url.fileURLToPath(new URL('../..', import.meta.url)), fileName)
|
const filePath = path.join(url.fileURLToPath(new URL('../..', import.meta.url)), fileName)
|
||||||
const buildInfo = await (async () => {
|
const buildInfo = await (async () => {
|
||||||
try {
|
try {
|
||||||
return await import('../../../../build.json', { with: { type: 'json' } })
|
return await import('../../../build.json', { with: { type: 'json' } })
|
||||||
} catch {
|
} catch {
|
||||||
return { commit: '', version: '', engineVersion: '', name: '' }
|
return { commit: '', version: '', engineVersion: '', name: '' }
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
try {
|
try {
|
||||||
const file = await fs.readFile(filePath, { encoding: 'utf-8' })
|
const file = await fs.readFile(filePath, { encoding: 'utf-8' })
|
||||||
// eslint-disable-next-line jsdoc/valid-types
|
|
||||||
/** @type {readonly (readonly [string, string])[]} */
|
/** @type {readonly (readonly [string, string])[]} */
|
||||||
let entries = file.split('\n').flatMap(line => {
|
let entries = file.split('\n').flatMap(line => {
|
||||||
if (/^\s*$|^.s*#/.test(line)) {
|
if (/^\s*$|^.s*#/.test(line)) {
|
||||||
@ -47,14 +47,10 @@ export async function readEnvironmentFromFile() {
|
|||||||
if (!isProduction || entries.length > 0) {
|
if (!isProduction || entries.length > 0) {
|
||||||
Object.assign(process.env, variables)
|
Object.assign(process.env, variables)
|
||||||
}
|
}
|
||||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
|
||||||
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
|
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
|
||||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
|
||||||
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
|
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
|
||||||
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
|
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
|
||||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
|
||||||
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
|
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
|
||||||
const expectedKeys = Object.keys(DUMMY_DEFINES)
|
const expectedKeys = Object.keys(DUMMY_DEFINES)
|
||||||
.map(key => key.replace(/^process[.]env[.]/, ''))
|
.map(key => key.replace(/^process[.]env[.]/, ''))
|
||||||
@ -147,7 +143,6 @@ const DUMMY_DEFINES = {
|
|||||||
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
|
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
|
||||||
export function loadTestEnvironmentVariables() {
|
export function loadTestEnvironmentVariables() {
|
||||||
for (const [k, v] of Object.entries(DUMMY_DEFINES)) {
|
for (const [k, v] of Object.entries(DUMMY_DEFINES)) {
|
||||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
|
||||||
process.env[k.replace(/^process[.]env[.]/, '')] = v
|
process.env[k.replace(/^process[.]env[.]/, '')] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
app/common/src/buildUtils.d.ts
vendored
24
app/common/src/buildUtils.d.ts
vendored
@ -3,26 +3,34 @@
|
|||||||
/** Indent size for stringifying JSON. */
|
/** Indent size for stringifying JSON. */
|
||||||
export const INDENT_SIZE: number
|
export const INDENT_SIZE: number
|
||||||
|
|
||||||
/** Get the environment variable value.
|
/**
|
||||||
|
* Get the environment variable value.
|
||||||
* @param name - The name of the environment variable.
|
* @param name - The name of the environment variable.
|
||||||
* @returns The value of the environment variable.
|
* @returns The value of the environment variable.
|
||||||
* @throws {Error} If the environment variable is not set. */
|
* @throws {Error} If the environment variable is not set.
|
||||||
|
*/
|
||||||
export function requireEnv(name: string): string
|
export function requireEnv(name: string): string
|
||||||
|
|
||||||
/** Read the path from environment variable and resolve it.
|
/**
|
||||||
|
* Read the path from environment variable and resolve it.
|
||||||
* @param name - The name of the environment variable.
|
* @param name - The name of the environment variable.
|
||||||
* @returns The resolved path.
|
* @returns The resolved path.
|
||||||
* @throws {Error} If the environment variable is not set. */
|
* @throws {Error} If the environment variable is not set.
|
||||||
|
*/
|
||||||
export function requireEnvResolvedPath(name: string): string
|
export function requireEnvResolvedPath(name: string): string
|
||||||
|
|
||||||
/** Read the path from environment variable and resolve it. Verify that it exists.
|
/**
|
||||||
|
* Read the path from environment variable and resolve it. Verify that it exists.
|
||||||
* @param name - The name of the environment variable.
|
* @param name - The name of the environment variable.
|
||||||
* @returns The resolved path.
|
* @returns The resolved path.
|
||||||
* @throws {Error} If the environment variable is not set or path does not exist. */
|
* @throws {Error} If the environment variable is not set or path does not exist.
|
||||||
|
*/
|
||||||
export function requireEnvPathExist(name: string): string
|
export function requireEnvPathExist(name: string): string
|
||||||
|
|
||||||
/** Get the common prefix of the two strings.
|
/**
|
||||||
|
* Get the common prefix of the two strings.
|
||||||
* @param a - the first string.
|
* @param a - the first string.
|
||||||
* @param b - the second string.
|
* @param b - the second string.
|
||||||
* @returns The common prefix. */
|
* @returns The common prefix.
|
||||||
|
*/
|
||||||
export function getCommonPrefix(a: string, b: string): string
|
export function getCommonPrefix(a: string, b: string): string
|
||||||
|
@ -23,8 +23,10 @@ export enum Platform {
|
|||||||
android = 'Android',
|
android = 'Android',
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The platform the app is currently running on.
|
/**
|
||||||
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts. */
|
* The platform the app is currently running on.
|
||||||
|
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts.
|
||||||
|
*/
|
||||||
export function platform() {
|
export function platform() {
|
||||||
if (isOnWindowsPhone()) {
|
if (isOnWindowsPhone()) {
|
||||||
// MUST be before Android and Windows.
|
// MUST be before Android and Windows.
|
||||||
@ -96,8 +98,10 @@ export enum Browser {
|
|||||||
opera = 'Opera',
|
opera = 'Opera',
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the platform the app is currently running on.
|
/**
|
||||||
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts. */
|
* Return the platform the app is currently running on.
|
||||||
|
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts.
|
||||||
|
*/
|
||||||
export function browser(): Browser {
|
export function browser(): Browser {
|
||||||
if (isOnElectron()) {
|
if (isOnElectron()) {
|
||||||
return Browser.electron
|
return Browser.electron
|
||||||
@ -117,10 +121,12 @@ export function browser(): Browser {
|
|||||||
return Browser.unknown
|
return Browser.unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Returns `true` if running in Electron, else `false`.
|
/**
|
||||||
|
* Returns `true` if running in Electron, else `false`.
|
||||||
* This is used to determine whether to use a `MemoryRouter` (stores history in an array)
|
* This is used to determine whether to use a `MemoryRouter` (stores history in an array)
|
||||||
* or a `BrowserRouter` (stores history in the path of the URL).
|
* or a `BrowserRouter` (stores history in the path of the URL).
|
||||||
* It is also used to determine whether to send custom state to Amplify for a workaround. */
|
* It is also used to determine whether to send custom state to Amplify for a workaround.
|
||||||
|
*/
|
||||||
export function isOnElectron() {
|
export function isOnElectron() {
|
||||||
return /electron/i.test(navigator.userAgent)
|
return /electron/i.test(navigator.userAgent)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ window.dataLayer = window.dataLayer || []
|
|||||||
export function gtag(_action: 'config' | 'event' | 'js' | 'set', ..._args: unknown[]) {
|
export function gtag(_action: 'config' | 'event' | 'js' | 'set', ..._args: unknown[]) {
|
||||||
// @ts-expect-error This is explicitly not given types as it is a mistake to acess this
|
// @ts-expect-error This is explicitly not given types as it is a mistake to acess this
|
||||||
// anywhere else.
|
// anywhere else.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, prefer-rest-params
|
||||||
window.dataLayer.push(arguments)
|
window.dataLayer.push(arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export function event(name: string, params?: object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gtag('js', new Date())
|
gtag('js', new Date())
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention, camelcase
|
||||||
gtag('set', 'linker', { accept_incoming: true })
|
gtag('set', 'linker', { accept_incoming: true })
|
||||||
gtag('config', GOOGLE_ANALYTICS_TAG)
|
gtag('config', GOOGLE_ANALYTICS_TAG)
|
||||||
if (GOOGLE_ANALYTICS_TAG === 'G-CLTBJ37MDM') {
|
if (GOOGLE_ANALYTICS_TAG === 'G-CLTBJ37MDM') {
|
||||||
|
24
app/common/src/index.d.ts
vendored
24
app/common/src/index.d.ts
vendored
@ -1,18 +1,22 @@
|
|||||||
/** @file This module contains metadata about the product and distribution,
|
/**
|
||||||
|
* @file This module contains metadata about the product and distribution,
|
||||||
* and various other constants that are needed in multiple sibling packages.
|
* and various other constants that are needed in multiple sibling packages.
|
||||||
*
|
*
|
||||||
* Code in this package is used by two or more sibling packages of this package. The code is defined
|
* Code in this package is used by two or more sibling packages of this package. The code is defined
|
||||||
* here when it is not possible for a sibling package to own that code without introducing a
|
* here when it is not possible for a sibling package to own that code without introducing a
|
||||||
* circular dependency in our packages. */
|
* circular dependency in our packages.
|
||||||
|
*/
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// === Product metadata ===
|
// === Product metadata ===
|
||||||
// ========================
|
// ========================
|
||||||
|
|
||||||
/** URL protocol scheme for deep links to authentication flow pages, without the `:` suffix.
|
/**
|
||||||
|
* URL protocol scheme for deep links to authentication flow pages, without the `:` suffix.
|
||||||
*
|
*
|
||||||
* For example: the deep link URL
|
* For example: the deep link URL
|
||||||
* `enso://authentication/register?code=...&state=...` uses this scheme. */
|
* `enso://authentication/register?code=...&state=...` uses this scheme.
|
||||||
|
*/
|
||||||
export const DEEP_LINK_SCHEME: string
|
export const DEEP_LINK_SCHEME: string
|
||||||
|
|
||||||
/** Name of the product. */
|
/** Name of the product. */
|
||||||
@ -21,12 +25,16 @@ export const PRODUCT_NAME: string
|
|||||||
/** Company name, used as the copyright holder. */
|
/** Company name, used as the copyright holder. */
|
||||||
export const COMPANY_NAME: string
|
export const COMPANY_NAME: string
|
||||||
|
|
||||||
/** The domain on which the Cloud Dashboard web app is hosted.
|
/**
|
||||||
* Excludes the protocol (`https://`). */
|
* The domain on which the Cloud Dashboard web app is hosted.
|
||||||
|
* Excludes the protocol (`https://`).
|
||||||
|
*/
|
||||||
export const CLOUD_DASHBOARD_DOMAIN: string
|
export const CLOUD_DASHBOARD_DOMAIN: string
|
||||||
|
|
||||||
/** COOP, COEP, and CORP headers: https://web.dev/coop-coep/
|
/**
|
||||||
|
* COOP, COEP, and CORP headers: https://web.dev/coop-coep/
|
||||||
*
|
*
|
||||||
* These are required to increase the resolution of `performance.now()` timers,
|
* These are required to increase the resolution of `performance.now()` timers,
|
||||||
* making profiling a lot more accurate and consistent. */
|
* making profiling a lot more accurate and consistent.
|
||||||
|
*/
|
||||||
export const COOP_COEP_CORP_HEADERS: [header: string, value: string][]
|
export const COOP_COEP_CORP_HEADERS: [header: string, value: string][]
|
||||||
|
@ -10,9 +10,7 @@ import * as vueQuery from '@tanstack/vue-query'
|
|||||||
import * as idbKeyval from 'idb-keyval'
|
import * as idbKeyval from 'idb-keyval'
|
||||||
|
|
||||||
declare module '@tanstack/query-core' {
|
declare module '@tanstack/query-core' {
|
||||||
/**
|
/** Query client with additional methods. */
|
||||||
* Query client with additional methods.
|
|
||||||
*/
|
|
||||||
interface QueryClient {
|
interface QueryClient {
|
||||||
/**
|
/**
|
||||||
* Clear the cache stored in Tanstack Query and the persister storage.
|
* Clear the cache stored in Tanstack Query and the persister storage.
|
||||||
@ -20,19 +18,13 @@ declare module '@tanstack/query-core' {
|
|||||||
* Usually you should use `queryClient.invalidateQueries` instead.
|
* Usually you should use `queryClient.invalidateQueries` instead.
|
||||||
*/
|
*/
|
||||||
readonly clearWithPersister: () => Promise<void>
|
readonly clearWithPersister: () => Promise<void>
|
||||||
/**
|
/** Clear the cache stored in the persister storage. */
|
||||||
* Clear the cache stored in the persister storage.
|
|
||||||
*/
|
|
||||||
readonly nukePersister: () => Promise<void>
|
readonly nukePersister: () => Promise<void>
|
||||||
}
|
}
|
||||||
/**
|
/** Specifies the invalidation behavior of a mutation. */
|
||||||
* Specifies the invalidation behavior of a mutation.
|
|
||||||
*/
|
|
||||||
interface Register {
|
interface Register {
|
||||||
readonly mutationMeta: {
|
readonly mutationMeta: {
|
||||||
/**
|
/** List of query keys to invalidate when the mutation succeeds. */
|
||||||
* List of query keys to invalidate when the mutation succeeds.
|
|
||||||
*/
|
|
||||||
readonly invalidates?: queryCore.QueryKey[]
|
readonly invalidates?: queryCore.QueryKey[]
|
||||||
/**
|
/**
|
||||||
* List of query keys to await invalidation before the mutation is considered successful.
|
* List of query keys to await invalidation before the mutation is considered successful.
|
||||||
@ -71,9 +63,7 @@ const DEFAULT_QUERY_PERSIST_TIME_MS = 30 * 24 * 60 * 60 * 1000 // 30 days
|
|||||||
|
|
||||||
const DEFAULT_BUSTER = 'v1.1'
|
const DEFAULT_BUSTER = 'v1.1'
|
||||||
|
|
||||||
/**
|
/** Create a new Tanstack Query client. */
|
||||||
* Create a new Tanstack Query client.
|
|
||||||
*/
|
|
||||||
export function createQueryClient(): QueryClient {
|
export function createQueryClient(): QueryClient {
|
||||||
const store = idbKeyval.createStore('enso', 'query-persist-cache')
|
const store = idbKeyval.createStore('enso', 'query-persist-cache')
|
||||||
queryCore.onlineManager.setOnline(navigator.onLine)
|
queryCore.onlineManager.setOnline(navigator.onLine)
|
||||||
|
@ -29,8 +29,10 @@ export const UserGroupId = newtype.newtypeConstructor<UserGroupId>()
|
|||||||
export type DirectoryId = newtype.Newtype<string, 'DirectoryId'>
|
export type DirectoryId = newtype.Newtype<string, 'DirectoryId'>
|
||||||
export const DirectoryId = newtype.newtypeConstructor<DirectoryId>()
|
export const DirectoryId = newtype.newtypeConstructor<DirectoryId>()
|
||||||
|
|
||||||
/** Unique identifier for an asset representing the items inside a directory for which the
|
/**
|
||||||
* request to retrive the items has not yet completed. */
|
* Unique identifier for an asset representing the items inside a directory for which the
|
||||||
|
* request to retrive the items has not yet completed.
|
||||||
|
*/
|
||||||
export type LoadingAssetId = newtype.Newtype<string, 'LoadingAssetId'>
|
export type LoadingAssetId = newtype.Newtype<string, 'LoadingAssetId'>
|
||||||
export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
|
export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
|
||||||
|
|
||||||
@ -38,8 +40,10 @@ export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
|
|||||||
export type EmptyAssetId = newtype.Newtype<string, 'EmptyAssetId'>
|
export type EmptyAssetId = newtype.Newtype<string, 'EmptyAssetId'>
|
||||||
export const EmptyAssetId = newtype.newtypeConstructor<EmptyAssetId>()
|
export const EmptyAssetId = newtype.newtypeConstructor<EmptyAssetId>()
|
||||||
|
|
||||||
/** Unique identifier for an asset representing the nonexistent children of a directory
|
/**
|
||||||
* that failed to fetch. */
|
* Unique identifier for an asset representing the nonexistent children of a directory
|
||||||
|
* that failed to fetch.
|
||||||
|
*/
|
||||||
export type ErrorAssetId = newtype.Newtype<string, 'ErrorAssetId'>
|
export type ErrorAssetId = newtype.Newtype<string, 'ErrorAssetId'>
|
||||||
export const ErrorAssetId = newtype.newtypeConstructor<ErrorAssetId>()
|
export const ErrorAssetId = newtype.newtypeConstructor<ErrorAssetId>()
|
||||||
|
|
||||||
@ -74,9 +78,7 @@ export type AssetId = IdType[keyof IdType]
|
|||||||
export type CheckoutSessionId = newtype.Newtype<string, 'CheckoutSessionId'>
|
export type CheckoutSessionId = newtype.Newtype<string, 'CheckoutSessionId'>
|
||||||
export const CheckoutSessionId = newtype.newtypeConstructor<CheckoutSessionId>()
|
export const CheckoutSessionId = newtype.newtypeConstructor<CheckoutSessionId>()
|
||||||
|
|
||||||
/**
|
/** Unique identifier for a subscription. */
|
||||||
* Unique identifier for a subscription.
|
|
||||||
*/
|
|
||||||
export type SubscriptionId = newtype.Newtype<string, 'SubscriptionId'>
|
export type SubscriptionId = newtype.Newtype<string, 'SubscriptionId'>
|
||||||
export const SubscriptionId = newtype.newtypeConstructor<SubscriptionId>()
|
export const SubscriptionId = newtype.newtypeConstructor<SubscriptionId>()
|
||||||
|
|
||||||
@ -129,14 +131,18 @@ export function isUserGroupId(id: string): id is UserGroupId {
|
|||||||
|
|
||||||
const PLACEHOLDER_USER_GROUP_PREFIX = 'usergroup-placeholder-'
|
const PLACEHOLDER_USER_GROUP_PREFIX = 'usergroup-placeholder-'
|
||||||
|
|
||||||
/** Whether a given {@link UserGroupId} represents a user group that does not yet exist on the
|
/**
|
||||||
* server. */
|
* Whether a given {@link UserGroupId} represents a user group that does not yet exist on the
|
||||||
|
* server.
|
||||||
|
*/
|
||||||
export function isPlaceholderUserGroupId(id: string) {
|
export function isPlaceholderUserGroupId(id: string) {
|
||||||
return id.startsWith(PLACEHOLDER_USER_GROUP_PREFIX)
|
return id.startsWith(PLACEHOLDER_USER_GROUP_PREFIX)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a new {@link UserGroupId} that represents a placeholder user group that is yet to finish
|
/**
|
||||||
* being created on the backend. */
|
* Return a new {@link UserGroupId} that represents a placeholder user group that is yet to finish
|
||||||
|
* being created on the backend.
|
||||||
|
*/
|
||||||
export function newPlaceholderUserGroupId() {
|
export function newPlaceholderUserGroupId() {
|
||||||
return UserGroupId(`${PLACEHOLDER_USER_GROUP_PREFIX}${uniqueString.uniqueString()}`)
|
return UserGroupId(`${PLACEHOLDER_USER_GROUP_PREFIX}${uniqueString.uniqueString()}`)
|
||||||
}
|
}
|
||||||
@ -153,17 +159,21 @@ export enum BackendType {
|
|||||||
|
|
||||||
/** Metadata uniquely identifying a user inside an organization. */
|
/** Metadata uniquely identifying a user inside an organization. */
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
/** The ID of the parent organization. If this is a sole user, they are implicitly in an
|
/**
|
||||||
* organization consisting of only themselves. */
|
* The ID of the parent organization. If this is a sole user, they are implicitly in an
|
||||||
|
* organization consisting of only themselves.
|
||||||
|
*/
|
||||||
readonly organizationId: OrganizationId
|
readonly organizationId: OrganizationId
|
||||||
/** The name of the parent organization. */
|
/** The name of the parent organization. */
|
||||||
readonly organizationName?: string
|
readonly organizationName?: string
|
||||||
/** The ID of this user.
|
/**
|
||||||
|
* The ID of this user.
|
||||||
*
|
*
|
||||||
* The user ID is globally unique. Thus, the user ID is always sufficient to uniquely identify a
|
* The user ID is globally unique. Thus, the user ID is always sufficient to uniquely identify a
|
||||||
* user. The user ID is guaranteed to never change, once assigned. For these reasons, the user ID
|
* user. The user ID is guaranteed to never change, once assigned. For these reasons, the user ID
|
||||||
* should be the preferred way to uniquely refer to a user. That is, when referring to a user,
|
* should be the preferred way to uniquely refer to a user. That is, when referring to a user,
|
||||||
* prefer this field over `name`, `email`, `subject`, or any other mechanism, where possible. */
|
* prefer this field over `name`, `email`, `subject`, or any other mechanism, where possible.
|
||||||
|
*/
|
||||||
readonly userId: UserId
|
readonly userId: UserId
|
||||||
readonly name: string
|
readonly name: string
|
||||||
readonly email: EmailAddress
|
readonly email: EmailAddress
|
||||||
@ -173,8 +183,10 @@ export interface UserInfo {
|
|||||||
|
|
||||||
/** A user in the application. These are the primary owners of a project. */
|
/** A user in the application. These are the primary owners of a project. */
|
||||||
export interface User extends UserInfo {
|
export interface User extends UserInfo {
|
||||||
/** If `false`, this account is awaiting acceptance from an administrator, and endpoints other than
|
/**
|
||||||
* `usersMe` will not work. */
|
* If `false`, this account is awaiting acceptance from an administrator, and endpoints other than
|
||||||
|
* `usersMe` will not work.
|
||||||
|
*/
|
||||||
readonly isEnabled: boolean
|
readonly isEnabled: boolean
|
||||||
readonly isOrganizationAdmin: boolean
|
readonly isOrganizationAdmin: boolean
|
||||||
readonly rootDirectoryId: DirectoryId
|
readonly rootDirectoryId: DirectoryId
|
||||||
@ -200,11 +212,15 @@ export enum ProjectState {
|
|||||||
provisioned = 'Provisioned',
|
provisioned = 'Provisioned',
|
||||||
opened = 'Opened',
|
opened = 'Opened',
|
||||||
closed = 'Closed',
|
closed = 'Closed',
|
||||||
/** A frontend-specific state, representing a project that should be displayed as
|
/**
|
||||||
* `openInProgress`, but has not yet been added to the backend. */
|
* A frontend-specific state, representing a project that should be displayed as
|
||||||
|
* `openInProgress`, but has not yet been added to the backend.
|
||||||
|
*/
|
||||||
placeholder = 'Placeholder',
|
placeholder = 'Placeholder',
|
||||||
/** A frontend-specific state, representing a project that should be displayed as `closed`,
|
/**
|
||||||
* but is still in the process of shutting down. */
|
* A frontend-specific state, representing a project that should be displayed as `closed`,
|
||||||
|
* but is still in the process of shutting down.
|
||||||
|
*/
|
||||||
closing = 'Closing',
|
closing = 'Closing',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,11 +390,13 @@ export interface Label {
|
|||||||
readonly color: LChColor
|
readonly color: LChColor
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Type of application that a {@link Version} applies to.
|
/**
|
||||||
|
* Type of application that a {@link Version} applies to.
|
||||||
*
|
*
|
||||||
* We keep track of both backend and IDE versions, so that we can update the two independently.
|
* We keep track of both backend and IDE versions, so that we can update the two independently.
|
||||||
* However the format of the version numbers is the same for both, so we can use the same type for
|
* However the format of the version numbers is the same for both, so we can use the same type for
|
||||||
* both. We just need this enum to disambiguate. */
|
* both. We just need this enum to disambiguate.
|
||||||
|
*/
|
||||||
export enum VersionType {
|
export enum VersionType {
|
||||||
backend = 'Backend',
|
backend = 'Backend',
|
||||||
ide = 'Ide',
|
ide = 'Ide',
|
||||||
@ -458,9 +476,7 @@ export interface ResourceUsage {
|
|||||||
readonly storage: number
|
readonly storage: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Metadata for a subscription. */
|
||||||
* Metadata for a subscription.
|
|
||||||
*/
|
|
||||||
export interface Subscription {
|
export interface Subscription {
|
||||||
readonly id?: SubscriptionId
|
readonly id?: SubscriptionId
|
||||||
readonly plan?: Plan
|
readonly plan?: Plan
|
||||||
@ -566,7 +582,7 @@ export interface UpdatedDirectory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The type returned from the "create directory" endpoint. */
|
/** The type returned from the "create directory" endpoint. */
|
||||||
export interface Directory extends DirectoryAsset {}
|
export type Directory = DirectoryAsset
|
||||||
|
|
||||||
/** The subset of asset fields returned by the "copy asset" endpoint. */
|
/** The subset of asset fields returned by the "copy asset" endpoint. */
|
||||||
export interface CopiedAsset {
|
export interface CopiedAsset {
|
||||||
@ -711,8 +727,10 @@ export enum AssetType {
|
|||||||
secret = 'secret',
|
secret = 'secret',
|
||||||
datalink = 'datalink',
|
datalink = 'datalink',
|
||||||
directory = 'directory',
|
directory = 'directory',
|
||||||
/** A special {@link AssetType} representing the unknown items of a directory, before the
|
/**
|
||||||
* request to retrieve the items completes. */
|
* A special {@link AssetType} representing the unknown items of a directory, before the
|
||||||
|
* request to retrieve the items completes.
|
||||||
|
*/
|
||||||
specialLoading = 'specialLoading',
|
specialLoading = 'specialLoading',
|
||||||
/** A special {@link AssetType} representing a directory listing that is empty. */
|
/** A special {@link AssetType} representing a directory listing that is empty. */
|
||||||
specialEmpty = 'specialEmpty',
|
specialEmpty = 'specialEmpty',
|
||||||
@ -732,8 +750,10 @@ export interface IdType {
|
|||||||
readonly [AssetType.specialError]: ErrorAssetId
|
readonly [AssetType.specialError]: ErrorAssetId
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Integers (starting from 0) corresponding to the order in which each asset type should appear
|
/**
|
||||||
* in a directory listing. */
|
* Integers (starting from 0) corresponding to the order in which each asset type should appear
|
||||||
|
* in a directory listing.
|
||||||
|
*/
|
||||||
export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
|
export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
|
||||||
// This is a sequence of numbers, not magic numbers. `1000` is an arbitrary number
|
// This is a sequence of numbers, not magic numbers. `1000` is an arbitrary number
|
||||||
// that are higher than the number of possible asset types.
|
// that are higher than the number of possible asset types.
|
||||||
@ -753,22 +773,28 @@ export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
|
|||||||
// === Asset ===
|
// === Asset ===
|
||||||
// =============
|
// =============
|
||||||
|
|
||||||
/** Metadata uniquely identifying a directory entry.
|
/**
|
||||||
* These can be Projects, Files, Secrets, or other directories. */
|
* Metadata uniquely identifying a directory entry.
|
||||||
|
* These can be Projects, Files, Secrets, or other directories.
|
||||||
|
*/
|
||||||
export interface BaseAsset {
|
export interface BaseAsset {
|
||||||
readonly id: AssetId
|
readonly id: AssetId
|
||||||
readonly title: string
|
readonly title: string
|
||||||
readonly modifiedAt: dateTime.Rfc3339DateTime
|
readonly modifiedAt: dateTime.Rfc3339DateTime
|
||||||
/** This is defined as a generic {@link AssetId} in the backend, however it is more convenient
|
/**
|
||||||
* (and currently safe) to assume it is always a {@link DirectoryId}. */
|
* This is defined as a generic {@link AssetId} in the backend, however it is more convenient
|
||||||
|
* (and currently safe) to assume it is always a {@link DirectoryId}.
|
||||||
|
*/
|
||||||
readonly parentId: DirectoryId
|
readonly parentId: DirectoryId
|
||||||
readonly permissions: readonly AssetPermission[] | null
|
readonly permissions: readonly AssetPermission[] | null
|
||||||
readonly labels: readonly LabelName[] | null
|
readonly labels: readonly LabelName[] | null
|
||||||
readonly description: string | null
|
readonly description: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Metadata uniquely identifying a directory entry.
|
/**
|
||||||
* These can be Projects, Files, Secrets, or other directories. */
|
* Metadata uniquely identifying a directory entry.
|
||||||
|
* These can be Projects, Files, Secrets, or other directories.
|
||||||
|
*/
|
||||||
export interface Asset<Type extends AssetType = AssetType> extends BaseAsset {
|
export interface Asset<Type extends AssetType = AssetType> extends BaseAsset {
|
||||||
readonly type: Type
|
readonly type: Type
|
||||||
readonly id: IdType[Type]
|
readonly id: IdType[Type]
|
||||||
@ -776,31 +802,33 @@ export interface Asset<Type extends AssetType = AssetType> extends BaseAsset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.directory}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.directory}>. */
|
||||||
export interface DirectoryAsset extends Asset<AssetType.directory> {}
|
export type DirectoryAsset = Asset<AssetType.directory>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.project}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.project}>. */
|
||||||
export interface ProjectAsset extends Asset<AssetType.project> {}
|
export type ProjectAsset = Asset<AssetType.project>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.file}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.file}>. */
|
||||||
export interface FileAsset extends Asset<AssetType.file> {}
|
export type FileAsset = Asset<AssetType.file>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.datalink}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.datalink}>. */
|
||||||
export interface DatalinkAsset extends Asset<AssetType.datalink> {}
|
export type DatalinkAsset = Asset<AssetType.datalink>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.secret}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.secret}>. */
|
||||||
export interface SecretAsset extends Asset<AssetType.secret> {}
|
export type SecretAsset = Asset<AssetType.secret>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.specialLoading}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.specialLoading}>. */
|
||||||
export interface SpecialLoadingAsset extends Asset<AssetType.specialLoading> {}
|
export type SpecialLoadingAsset = Asset<AssetType.specialLoading>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.specialEmpty}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.specialEmpty}>. */
|
||||||
export interface SpecialEmptyAsset extends Asset<AssetType.specialEmpty> {}
|
export type SpecialEmptyAsset = Asset<AssetType.specialEmpty>
|
||||||
|
|
||||||
/** A convenience alias for {@link Asset}<{@link AssetType.specialError}>. */
|
/** A convenience alias for {@link Asset}<{@link AssetType.specialError}>. */
|
||||||
export interface SpecialErrorAsset extends Asset<AssetType.specialError> {}
|
export type SpecialErrorAsset = Asset<AssetType.specialError>
|
||||||
|
|
||||||
/** Creates a {@link DirectoryAsset} representing the root directory for the organization,
|
/**
|
||||||
* with all irrelevant fields initialized to default values. */
|
* Creates a {@link DirectoryAsset} representing the root directory for the organization,
|
||||||
|
* with all irrelevant fields initialized to default values.
|
||||||
|
*/
|
||||||
export function createRootDirectoryAsset(directoryId: DirectoryId): DirectoryAsset {
|
export function createRootDirectoryAsset(directoryId: DirectoryId): DirectoryAsset {
|
||||||
return {
|
return {
|
||||||
type: AssetType.directory,
|
type: AssetType.directory,
|
||||||
@ -860,8 +888,10 @@ export function createPlaceholderProjectAsset(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link SpecialLoadingAsset}, with all irrelevant fields initialized to default
|
/**
|
||||||
* values. */
|
* Creates a {@link SpecialLoadingAsset}, with all irrelevant fields initialized to default
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoadingAsset {
|
export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoadingAsset {
|
||||||
return {
|
return {
|
||||||
type: AssetType.specialLoading,
|
type: AssetType.specialLoading,
|
||||||
@ -876,8 +906,10 @@ export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoad
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link SpecialEmptyAsset}, with all irrelevant fields initialized to default
|
/**
|
||||||
* values. */
|
* Creates a {@link SpecialEmptyAsset}, with all irrelevant fields initialized to default
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyAsset {
|
export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyAsset {
|
||||||
return {
|
return {
|
||||||
type: AssetType.specialEmpty,
|
type: AssetType.specialEmpty,
|
||||||
@ -892,8 +924,10 @@ export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link SpecialErrorAsset}, with all irrelevant fields initialized to default
|
/**
|
||||||
* values. */
|
* Creates a {@link SpecialErrorAsset}, with all irrelevant fields initialized to default
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
export function createSpecialErrorAsset(directoryId: DirectoryId): SpecialErrorAsset {
|
export function createSpecialErrorAsset(directoryId: DirectoryId): SpecialErrorAsset {
|
||||||
return {
|
return {
|
||||||
type: AssetType.specialError,
|
type: AssetType.specialError,
|
||||||
@ -1011,8 +1045,10 @@ export interface AssetVersions {
|
|||||||
// === compareAssetPermissions ===
|
// === compareAssetPermissions ===
|
||||||
// ===============================
|
// ===============================
|
||||||
|
|
||||||
/** Return a positive number when `a > b`, a negative number when `a < b`, and `0`
|
/**
|
||||||
* when `a === b`. */
|
* Return a positive number when `a > b`, a negative number when `a < b`, and `0`
|
||||||
|
* when `a === b`.
|
||||||
|
*/
|
||||||
export function compareAssetPermissions(a: AssetPermission, b: AssetPermission) {
|
export function compareAssetPermissions(a: AssetPermission, b: AssetPermission) {
|
||||||
const relativePermissionPrecedence =
|
const relativePermissionPrecedence =
|
||||||
permissions.PERMISSION_ACTION_PRECEDENCE[a.permission] -
|
permissions.PERMISSION_ACTION_PRECEDENCE[a.permission] -
|
||||||
@ -1126,8 +1162,10 @@ export interface CreateProjectRequestBody {
|
|||||||
readonly datalinkId?: DatalinkId
|
readonly datalinkId?: DatalinkId
|
||||||
}
|
}
|
||||||
|
|
||||||
/** HTTP request body for the "update project" endpoint.
|
/**
|
||||||
* Only updates of the `projectName` or `ami` are allowed. */
|
* HTTP request body for the "update project" endpoint.
|
||||||
|
* Only updates of the `projectName` or `ami` are allowed.
|
||||||
|
*/
|
||||||
export interface UpdateProjectRequestBody {
|
export interface UpdateProjectRequestBody {
|
||||||
readonly projectName: string | null
|
readonly projectName: string | null
|
||||||
readonly ami: Ami | null
|
readonly ami: Ami | null
|
||||||
@ -1256,8 +1294,10 @@ export function compareAssets(a: AnyAsset, b: AnyAsset) {
|
|||||||
// === getAssetId ===
|
// === getAssetId ===
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** A convenience function to get the `id` of an {@link Asset}.
|
/**
|
||||||
* This is useful to avoid React re-renders as it is not re-created on each function call. */
|
* A convenience function to get the `id` of an {@link Asset}.
|
||||||
|
* This is useful to avoid React re-renders as it is not re-created on each function call.
|
||||||
|
*/
|
||||||
export function getAssetId<Type extends AssetType>(asset: Asset<Type>) {
|
export function getAssetId<Type extends AssetType>(asset: Asset<Type>) {
|
||||||
return asset.id
|
return asset.id
|
||||||
}
|
}
|
||||||
@ -1313,16 +1353,16 @@ export function stripProjectExtension(name: string) {
|
|||||||
return name.replace(/[.](?:tar[.]gz|zip|enso-project)$/, '')
|
return name.replace(/[.](?:tar[.]gz|zip|enso-project)$/, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return both the name and extension of the project file name (if any).
|
/**
|
||||||
* Otherwise, returns the entire name as the basename. */
|
* Return both the name and extension of the project file name (if any).
|
||||||
|
* Otherwise, returns the entire name as the basename.
|
||||||
|
*/
|
||||||
export function extractProjectExtension(name: string) {
|
export function extractProjectExtension(name: string) {
|
||||||
const [, basename, extension] = name.match(/^(.*)[.](tar[.]gz|zip|enso-project)$/) ?? []
|
const [, basename, extension] = name.match(/^(.*)[.](tar[.]gz|zip|enso-project)$/) ?? []
|
||||||
return { basename: basename ?? name, extension: extension ?? '' }
|
return { basename: basename ?? name, extension: extension ?? '' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Network error class. */
|
||||||
* Network error class.
|
|
||||||
*/
|
|
||||||
export class NetworkError extends Error {
|
export class NetworkError extends Error {
|
||||||
/**
|
/**
|
||||||
* Create a new instance of the {@link NetworkError} class.
|
* Create a new instance of the {@link NetworkError} class.
|
||||||
@ -1336,9 +1376,7 @@ export class NetworkError extends Error {
|
|||||||
super(message)
|
super(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/** Error class for when the user is not authorized to access a resource. */
|
||||||
* Error class for when the user is not authorized to access a resource.
|
|
||||||
*/
|
|
||||||
export class NotAuthorizedError extends NetworkError {}
|
export class NotAuthorizedError extends NetworkError {}
|
||||||
|
|
||||||
// ===============
|
// ===============
|
||||||
|
@ -15,15 +15,19 @@ export function shallowEqual<T>(a: readonly T[], b: readonly T[]) {
|
|||||||
// === includes ===
|
// === includes ===
|
||||||
// ================
|
// ================
|
||||||
|
|
||||||
/** Returns a type predicate that returns true if and only if the value is in the array.
|
/**
|
||||||
* The array MUST contain every element of `T`. */
|
* Returns a type predicate that returns true if and only if the value is in the array.
|
||||||
|
* The array MUST contain every element of `T`.
|
||||||
|
*/
|
||||||
export function includes<T>(array: T[], item: unknown): item is T {
|
export function includes<T>(array: T[], item: unknown): item is T {
|
||||||
const arrayOfUnknown: unknown[] = array
|
const arrayOfUnknown: unknown[] = array
|
||||||
return arrayOfUnknown.includes(item)
|
return arrayOfUnknown.includes(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a type predicate that returns true if and only if the value is in the iterable.
|
/**
|
||||||
* The iterable MUST contain every element of `T`. */
|
* Returns a type predicate that returns true if and only if the value is in the iterable.
|
||||||
|
* The iterable MUST contain every element of `T`.
|
||||||
|
*/
|
||||||
export function includesPredicate<T>(array: Iterable<T>) {
|
export function includesPredicate<T>(array: Iterable<T>) {
|
||||||
const set: Set<unknown> = array instanceof Set ? array : new Set<T>(array)
|
const set: Set<unknown> = array instanceof Set ? array : new Set<T>(array)
|
||||||
return (item: unknown): item is T => set.has(item)
|
return (item: unknown): item is T => set.has(item)
|
||||||
@ -36,32 +40,40 @@ export function includesPredicate<T>(array: Iterable<T>) {
|
|||||||
/** The value returned when {@link Array.findIndex} fails. */
|
/** The value returned when {@link Array.findIndex} fails. */
|
||||||
const NOT_FOUND = -1
|
const NOT_FOUND = -1
|
||||||
|
|
||||||
/** Insert items before the first index `i` for which `predicate(array[i])` is `true`.
|
/**
|
||||||
* Insert the items at the end if the `predicate` never returns `true`. */
|
* Insert items before the first index `i` for which `predicate(array[i])` is `true`.
|
||||||
|
* Insert the items at the end if the `predicate` never returns `true`.
|
||||||
|
*/
|
||||||
export function spliceBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
export function spliceBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
||||||
const index = array.findIndex(predicate)
|
const index = array.findIndex(predicate)
|
||||||
array.splice(index === NOT_FOUND ? array.length : index, 0, ...items)
|
array.splice(index === NOT_FOUND ? array.length : index, 0, ...items)
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a copy of the array, with items inserted before the first index `i` for which
|
/**
|
||||||
|
* Return a copy of the array, with items inserted before the first index `i` for which
|
||||||
* `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never
|
* `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never
|
||||||
* returns `true`. */
|
* returns `true`.
|
||||||
|
*/
|
||||||
export function splicedBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
export function splicedBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
||||||
return spliceBefore(Array.from(array), items, predicate)
|
return spliceBefore(Array.from(array), items, predicate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert items after the first index `i` for which `predicate(array[i])` is `true`.
|
/**
|
||||||
* Insert the items at the end if the `predicate` never returns `true`. */
|
* Insert items after the first index `i` for which `predicate(array[i])` is `true`.
|
||||||
|
* Insert the items at the end if the `predicate` never returns `true`.
|
||||||
|
*/
|
||||||
export function spliceAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
export function spliceAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
||||||
const index = array.findIndex(predicate)
|
const index = array.findIndex(predicate)
|
||||||
array.splice(index === NOT_FOUND ? array.length : index + 1, 0, ...items)
|
array.splice(index === NOT_FOUND ? array.length : index + 1, 0, ...items)
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a copy of the array, with items inserted after the first index `i` for which
|
/**
|
||||||
|
* Return a copy of the array, with items inserted after the first index `i` for which
|
||||||
* `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never
|
* `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never
|
||||||
* returns `true`. */
|
* returns `true`.
|
||||||
|
*/
|
||||||
export function splicedAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
export function splicedAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
||||||
return spliceAfter(Array.from(array), items, predicate)
|
return spliceAfter(Array.from(array), items, predicate)
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,10 @@ export type Rfc3339DateTime = newtype.Newtype<string, 'Rfc3339DateTime'>
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||||
export const Rfc3339DateTime = newtype.newtypeConstructor<Rfc3339DateTime>()
|
export const Rfc3339DateTime = newtype.newtypeConstructor<Rfc3339DateTime>()
|
||||||
|
|
||||||
/** Return a new {@link Date} with units below days (hours, minutes, seconds and milliseconds)
|
/**
|
||||||
* set to `0`. */
|
* Return a new {@link Date} with units below days (hours, minutes, seconds and milliseconds)
|
||||||
|
* set to `0`.
|
||||||
|
*/
|
||||||
export function toDate(dateTime: Date) {
|
export function toDate(dateTime: Date) {
|
||||||
return new Date(dateTime.getFullYear(), dateTime.getMonth(), dateTime.getDate())
|
return new Date(dateTime.getFullYear(), dateTime.getMonth(), dateTime.getDate())
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,10 @@ type NewtypeVariant<TypeName extends string> = {
|
|||||||
readonly _$type: TypeName
|
readonly _$type: TypeName
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An interface specifying the variant of a newtype, where the discriminator is mutable.
|
/**
|
||||||
* This is safe, as the discriminator should be a string literal type anyway. */
|
* An interface specifying the variant of a newtype, where the discriminator is mutable.
|
||||||
|
* This is safe, as the discriminator should be a string literal type anyway.
|
||||||
|
*/
|
||||||
// This is required for compatibility with the dependency `enso-chat`.
|
// This is required for compatibility with the dependency `enso-chat`.
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
type MutableNewtypeVariant<TypeName extends string> = {
|
type MutableNewtypeVariant<TypeName extends string> = {
|
||||||
@ -21,7 +23,8 @@ type MutableNewtypeVariant<TypeName extends string> = {
|
|||||||
_$type: TypeName
|
_$type: TypeName
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used to create a "branded type",
|
/**
|
||||||
|
* Used to create a "branded type",
|
||||||
* which contains a property that only exists at compile time.
|
* which contains a property that only exists at compile time.
|
||||||
*
|
*
|
||||||
* `Newtype<string, 'A'>` and `Newtype<string, 'B'>` are not compatible with each other,
|
* `Newtype<string, 'A'>` and `Newtype<string, 'B'>` are not compatible with each other,
|
||||||
@ -33,11 +36,14 @@ type MutableNewtypeVariant<TypeName extends string> = {
|
|||||||
* It is similar to a `newtype` in other languages.
|
* It is similar to a `newtype` in other languages.
|
||||||
* Note however because TypeScript is structurally typed,
|
* Note however because TypeScript is structurally typed,
|
||||||
* a branded type is assignable to its base type:
|
* a branded type is assignable to its base type:
|
||||||
* `a: string = asNewtype<Newtype<string, 'Name'>>(b)` successfully typechecks. */
|
* `a: string = asNewtype<Newtype<string, 'Name'>>(b)` successfully typechecks.
|
||||||
|
*/
|
||||||
export type Newtype<T, TypeName extends string> = NewtypeVariant<TypeName> & T
|
export type Newtype<T, TypeName extends string> = NewtypeVariant<TypeName> & T
|
||||||
|
|
||||||
/** Extracts the original type out of a {@link Newtype}.
|
/**
|
||||||
* Its only use is in {@link newtypeConstructor}. */
|
* Extracts the original type out of a {@link Newtype}.
|
||||||
|
* Its only use is in {@link newtypeConstructor}.
|
||||||
|
*/
|
||||||
type UnNewtype<T extends Newtype<unknown, string>> =
|
type UnNewtype<T extends Newtype<unknown, string>> =
|
||||||
T extends infer U & NewtypeVariant<T['_$type']> ?
|
T extends infer U & NewtypeVariant<T['_$type']> ?
|
||||||
U extends infer V & MutableNewtypeVariant<T['_$type']> ?
|
U extends infer V & MutableNewtypeVariant<T['_$type']> ?
|
||||||
@ -51,9 +57,11 @@ type NotNewtype = {
|
|||||||
readonly _$type?: never
|
readonly _$type?: never
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts a value that is not a newtype, to a value that is a newtype.
|
/**
|
||||||
|
* Converts a value that is not a newtype, to a value that is a newtype.
|
||||||
* This function intentionally returns another function, to ensure that each function instance
|
* This function intentionally returns another function, to ensure that each function instance
|
||||||
* is only used for one type, avoiding the de-optimization caused by polymorphic functions. */
|
* is only used for one type, avoiding the de-optimization caused by polymorphic functions.
|
||||||
|
*/
|
||||||
export function newtypeConstructor<T extends Newtype<unknown, string>>() {
|
export function newtypeConstructor<T extends Newtype<unknown, string>>() {
|
||||||
// This cast is unsafe.
|
// This cast is unsafe.
|
||||||
// `T` has an extra property `_$type` which is used purely for typechecking
|
// `T` has an extra property `_$type` which is used purely for typechecking
|
||||||
|
@ -16,8 +16,10 @@ export type Mutable<T> = {
|
|||||||
/** Prevents generic parameter inference by hiding the type parameter behind a conditional type. */
|
/** Prevents generic parameter inference by hiding the type parameter behind a conditional type. */
|
||||||
type NoInfer<T> = [T][T extends T ? 0 : never]
|
type NoInfer<T> = [T][T extends T ? 0 : never]
|
||||||
|
|
||||||
/** Immutably shallowly merge an object with a partial update.
|
/**
|
||||||
* Does not preserve classes. Useful for preserving order of properties. */
|
* Immutably shallowly merge an object with a partial update.
|
||||||
|
* Does not preserve classes. Useful for preserving order of properties.
|
||||||
|
*/
|
||||||
export function merge<T extends object>(object: T, update: Partial<T>): T {
|
export function merge<T extends object>(object: T, update: Partial<T>): T {
|
||||||
for (const [key, value] of Object.entries(update)) {
|
for (const [key, value] of Object.entries(update)) {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
@ -57,8 +59,10 @@ export function unsafeMutable<T extends object>(object: T): { -readonly [K in ke
|
|||||||
// === unsafeEntries ===
|
// === unsafeEntries ===
|
||||||
// =====================
|
// =====================
|
||||||
|
|
||||||
/** Return the entries of an object. UNSAFE only when it is possible for an object to have
|
/**
|
||||||
* extra keys. */
|
* Return the entries of an object. UNSAFE only when it is possible for an object to have
|
||||||
|
* extra keys.
|
||||||
|
*/
|
||||||
export function unsafeEntries<T extends object>(
|
export function unsafeEntries<T extends object>(
|
||||||
object: T,
|
object: T,
|
||||||
): readonly { [K in keyof T]: readonly [K, T[K]] }[keyof T][] {
|
): readonly { [K in keyof T]: readonly [K, T[K]] }[keyof T][] {
|
||||||
@ -83,8 +87,10 @@ export function unsafeRemoveUndefined<T extends object>(
|
|||||||
// === mapEntries ===
|
// === mapEntries ===
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Return the entries of an object. UNSAFE only when it is possible for an object to have
|
/**
|
||||||
* extra keys. */
|
* Return the entries of an object. UNSAFE only when it is possible for an object to have
|
||||||
|
* extra keys.
|
||||||
|
*/
|
||||||
export function mapEntries<K extends PropertyKey, V, W>(
|
export function mapEntries<K extends PropertyKey, V, W>(
|
||||||
object: Record<K, V>,
|
object: Record<K, V>,
|
||||||
map: (key: K, value: V) => W,
|
map: (key: K, value: V) => W,
|
||||||
|
@ -111,8 +111,10 @@ export const FROM_PERMISSION_ACTION: Readonly<Record<PermissionAction, Permissio
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The corresponding {@link PermissionAction} for each {@link Permission}.
|
/**
|
||||||
* Assumes no docs sub-permission and no execute sub-permission. */
|
* The corresponding {@link PermissionAction} for each {@link Permission}.
|
||||||
|
* Assumes no docs sub-permission and no execute sub-permission.
|
||||||
|
*/
|
||||||
export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAction>> = {
|
export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAction>> = {
|
||||||
[Permission.owner]: PermissionAction.own,
|
[Permission.owner]: PermissionAction.own,
|
||||||
[Permission.admin]: PermissionAction.admin,
|
[Permission.admin]: PermissionAction.admin,
|
||||||
@ -123,8 +125,10 @@ export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAc
|
|||||||
[Permission.delete]: PermissionAction.view,
|
[Permission.delete]: PermissionAction.view,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The corresponding {@link text.TextId} for each {@link Permission}.
|
/**
|
||||||
* Assumes no docs sub-permission and no execute sub-permission. */
|
* The corresponding {@link text.TextId} for each {@link Permission}.
|
||||||
|
* Assumes no docs sub-permission and no execute sub-permission.
|
||||||
|
*/
|
||||||
export const TYPE_TO_TEXT_ID: Readonly<Record<Permission, text.TextId>> = {
|
export const TYPE_TO_TEXT_ID: Readonly<Record<Permission, text.TextId>> = {
|
||||||
[Permission.owner]: 'ownerPermissionType',
|
[Permission.owner]: 'ownerPermissionType',
|
||||||
[Permission.admin]: 'adminPermissionType',
|
[Permission.admin]: 'adminPermissionType',
|
||||||
@ -181,13 +185,13 @@ interface BasePermissions<T extends Permission> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Owner permissions for an asset. */
|
/** Owner permissions for an asset. */
|
||||||
interface OwnerPermissions extends BasePermissions<Permission.owner> {}
|
type OwnerPermissions = BasePermissions<Permission.owner>
|
||||||
|
|
||||||
/** Admin permissions for an asset. */
|
/** Admin permissions for an asset. */
|
||||||
interface AdminPermissions extends BasePermissions<Permission.admin> {}
|
type AdminPermissions = BasePermissions<Permission.admin>
|
||||||
|
|
||||||
/** Editor permissions for an asset. */
|
/** Editor permissions for an asset. */
|
||||||
interface EditPermissions extends BasePermissions<Permission.edit> {}
|
type EditPermissions = BasePermissions<Permission.edit>
|
||||||
|
|
||||||
/** Reader permissions for an asset. */
|
/** Reader permissions for an asset. */
|
||||||
interface ReadPermissions extends BasePermissions<Permission.read> {
|
interface ReadPermissions extends BasePermissions<Permission.read> {
|
||||||
|
@ -42,7 +42,7 @@ const guiTabCases = [
|
|||||||
v.test.each([
|
v.test.each([
|
||||||
{ group: 'Dashboard', cases: dashboardTabCases },
|
{ group: 'Dashboard', cases: dashboardTabCases },
|
||||||
{ group: 'GUI', cases: guiTabCases },
|
{ group: 'GUI', cases: guiTabCases },
|
||||||
])('Tab clip path: $group', ({ group, cases }) => {
|
])('Tab clip path: $group', ({ cases }) => {
|
||||||
cases.forEach(({ input, expected }) => {
|
cases.forEach(({ input, expected }) => {
|
||||||
const result = tabBar.tabClipPath(input.bounds, input.radius, (input as TabClipPathInput)?.side)
|
const result = tabBar.tabClipPath(input.bounds, input.radius, (input as TabClipPathInput)?.side)
|
||||||
v.expect(result).toBe(expected)
|
v.expect(result).toBe(expected)
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["DOM", "es2023"],
|
"lib": ["DOM", "es2023"],
|
||||||
"allowJs": false,
|
"allowJs": true,
|
||||||
"checkJs": false,
|
"checkJs": true,
|
||||||
"skipLibCheck": false
|
"skipLibCheck": false
|
||||||
},
|
},
|
||||||
"include": ["./src/", "./src/text/english.json", "../types/"]
|
"include": ["./src/", "./src/text/english.json", "../types/"]
|
||||||
|
@ -12,9 +12,7 @@ export default class EditorPageActions extends PageActions {
|
|||||||
get goToPage(): Omit<goToPageActions.GoToPageActions, 'editor'> {
|
get goToPage(): Omit<goToPageActions.GoToPageActions, 'editor'> {
|
||||||
return goToPageActions.goToPageActions(this.step.bind(this))
|
return goToPageActions.goToPageActions(this.step.bind(this))
|
||||||
}
|
}
|
||||||
/**
|
/** Waits for the editor to load. */
|
||||||
* Waits for the editor to load.
|
|
||||||
*/
|
|
||||||
waitForEditorToLoad(): EditorPageActions {
|
waitForEditorToLoad(): EditorPageActions {
|
||||||
return this.step('wait for the editor to load', async () => {
|
return this.step('wait for the editor to load', async () => {
|
||||||
await this.page.waitForSelector('[data-testid=editor]', { state: 'visible' })
|
await this.page.waitForSelector('[data-testid=editor]', { state: 'visible' })
|
||||||
|
@ -35,9 +35,7 @@ test('Disconnect an edge from a port', async ({ page }) => {
|
|||||||
await expect(await edgesToNodeWithBinding(page, 'sum')).toHaveCount(EDGE_PARTS)
|
await expect(await edgesToNodeWithBinding(page, 'sum')).toHaveCount(EDGE_PARTS)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Scenario: We replace the `sum` parameter in the `prod` node` with the `ten` node. */
|
||||||
* Scenario: We replace the `sum` parameter in the `prod` node` with the `ten` node.
|
|
||||||
*/
|
|
||||||
test('Connect an node to a port', async ({ page }) => {
|
test('Connect an node to a port', async ({ page }) => {
|
||||||
await initGraph(page)
|
await initGraph(page)
|
||||||
|
|
||||||
@ -57,9 +55,7 @@ test('Connect an node to a port', async ({ page }) => {
|
|||||||
await expect(graphNodeByBinding(page, 'prod')).toContainText('ten')
|
await expect(graphNodeByBinding(page, 'prod')).toContainText('ten')
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** As above, but by dragging edge instead of clicking source and target separately. */
|
||||||
* As above, but by dragging edge instead of clicking source and target separately.
|
|
||||||
*/
|
|
||||||
test('Connect an node to a port via dragging the edge', async ({ page }) => {
|
test('Connect an node to a port via dragging the edge', async ({ page }) => {
|
||||||
await initGraph(page)
|
await initGraph(page)
|
||||||
|
|
||||||
|
@ -34,9 +34,7 @@ test('Existence of edges between nodes', async ({ page }) => {
|
|||||||
await expect(await edgesToNodeWithBinding(page, 'five')).toHaveCount(0)
|
await expect(await edgesToNodeWithBinding(page, 'five')).toHaveCount(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Prepare the graph for the tests. We drag the `ten` node to the right for better access to its outgoing edge. */
|
||||||
* Prepare the graph for the tests. We drag the `ten` node to the right for better access to its outgoing edge.
|
|
||||||
*/
|
|
||||||
async function initGraph(page: Page) {
|
async function initGraph(page: Page) {
|
||||||
await actions.goToGraph(page)
|
await actions.goToGraph(page)
|
||||||
await actions.dragNodeByBinding(page, 'ten', 400, 0)
|
await actions.dragNodeByBinding(page, 'ten', 400, 0)
|
||||||
|
@ -5,9 +5,7 @@ import { mockExpressionUpdate } from './expressionUpdates'
|
|||||||
import * as locate from './locate'
|
import * as locate from './locate'
|
||||||
import { graphNodeByBinding } from './locate'
|
import { graphNodeByBinding } from './locate'
|
||||||
|
|
||||||
/**
|
/** Prepare the graph for the tests. We add the table type to the `aggregated` node. */
|
||||||
* Prepare the graph for the tests. We add the table type to the `aggregated` node.
|
|
||||||
*/
|
|
||||||
async function initGraph(page: Page) {
|
async function initGraph(page: Page) {
|
||||||
await actions.goToGraph(page)
|
await actions.goToGraph(page)
|
||||||
await mockExpressionUpdate(page, 'aggregated', { type: 'Standard.Table.Table.Table' })
|
await mockExpressionUpdate(page, 'aggregated', { type: 'Standard.Table.Table.Table' })
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build-cloud": "cross-env CLOUD_BUILD=true corepack pnpm run build",
|
"build-cloud": "cross-env CLOUD_BUILD=true corepack pnpm run build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "cross-env eslint . --max-warnings=0",
|
"lint": "eslint . --max-warnings=0",
|
||||||
"format": "prettier --version && prettier --write src/ && eslint . --fix",
|
"format": "prettier --version && prettier --write src/ && eslint . --fix",
|
||||||
"dev:vite": "vite",
|
"dev:vite": "vite",
|
||||||
"test": "corepack pnpm run /^^^^test:.*/",
|
"test": "corepack pnpm run /^^^^test:.*/",
|
||||||
|
@ -7,9 +7,7 @@ export const DOCUMENTS = getDocumentsPath()
|
|||||||
|
|
||||||
const CHILD_PROCESS_TIMEOUT = 3000
|
const CHILD_PROCESS_TIMEOUT = 3000
|
||||||
|
|
||||||
/**
|
/** Detects path of the user documents directory depending on the operating system. */
|
||||||
* Detects path of the user documents directory depending on the operating system.
|
|
||||||
*/
|
|
||||||
function getDocumentsPath(): string | undefined {
|
function getDocumentsPath(): string | undefined {
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
return getLinuxDocumentsPath()
|
return getLinuxDocumentsPath()
|
||||||
@ -22,18 +20,14 @@ function getDocumentsPath(): string | undefined {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the user documents path on Linux. */
|
||||||
* Returns the user documents path on Linux.
|
|
||||||
*/
|
|
||||||
function getLinuxDocumentsPath(): string {
|
function getLinuxDocumentsPath(): string {
|
||||||
const xdgDocumentsPath = getXdgDocumentsPath()
|
const xdgDocumentsPath = getXdgDocumentsPath()
|
||||||
|
|
||||||
return xdgDocumentsPath ?? path.join(os.homedir(), 'enso')
|
return xdgDocumentsPath ?? path.join(os.homedir(), 'enso')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets the documents directory from the XDG directory management system. */
|
||||||
* Gets the documents directory from the XDG directory management system.
|
|
||||||
*/
|
|
||||||
function getXdgDocumentsPath(): string | undefined {
|
function getXdgDocumentsPath(): string | undefined {
|
||||||
const out = childProcess.spawnSync('xdg-user-dir', ['DOCUMENTS'], {
|
const out = childProcess.spawnSync('xdg-user-dir', ['DOCUMENTS'], {
|
||||||
timeout: CHILD_PROCESS_TIMEOUT,
|
timeout: CHILD_PROCESS_TIMEOUT,
|
||||||
@ -54,9 +48,7 @@ function getMacOsDocumentsPath(): string {
|
|||||||
return path.join(os.homedir(), 'Documents')
|
return path.join(os.homedir(), 'Documents')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Get the path to the `My Documents` Windows directory. */
|
||||||
* Get the path to the `My Documents` Windows directory.
|
|
||||||
*/
|
|
||||||
function getWindowsDocumentsPath(): string | undefined {
|
function getWindowsDocumentsPath(): string | undefined {
|
||||||
const out = childProcess.spawnSync(
|
const out = childProcess.spawnSync(
|
||||||
'reg',
|
'reg',
|
||||||
|
@ -45,23 +45,17 @@ export const SUPPORT_EMAIL = 'cloud@enso.org'
|
|||||||
/** Return the `mailto:` URL for contacting support. */
|
/** Return the `mailto:` URL for contacting support. */
|
||||||
export const SUPPORT_EMAIL_URL = `mailto:${SUPPORT_EMAIL}`
|
export const SUPPORT_EMAIL_URL = `mailto:${SUPPORT_EMAIL}`
|
||||||
|
|
||||||
/**
|
/** Build a Subscription URL for a given plan. */
|
||||||
* Build a Subscription URL for a given plan.
|
|
||||||
*/
|
|
||||||
export function getUpgradeURL(plan: string): string {
|
export function getUpgradeURL(plan: string): string {
|
||||||
return SUBSCRIBE_PATH + '?plan=' + plan
|
return SUBSCRIBE_PATH + '?plan=' + plan
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Return the mailto URL for contacting sales. */
|
||||||
* Return the mailto URL for contacting sales.
|
|
||||||
*/
|
|
||||||
export function getSalesEmail(): string {
|
export function getSalesEmail(): string {
|
||||||
return 'mailto:contact@enso.org'
|
return 'mailto:contact@enso.org'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Build a Subscription URL for contacting sales. */
|
||||||
* Build a Subscription URL for contacting sales.
|
|
||||||
*/
|
|
||||||
export function getContactSalesURL(): string {
|
export function getContactSalesURL(): string {
|
||||||
return 'mailto:contact@enso.org?subject=Upgrading%20to%20Organization%20Plan'
|
return 'mailto:contact@enso.org?subject=Upgrading%20to%20Organization%20Plan'
|
||||||
}
|
}
|
||||||
|
@ -306,16 +306,12 @@ export class Cognito {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Refresh the current user's session. */
|
||||||
* Refresh the current user's session.
|
|
||||||
*/
|
|
||||||
async refreshUserSession() {
|
async refreshUserSession() {
|
||||||
return Promise.resolve(results.Ok(null))
|
return Promise.resolve(results.Ok(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns MFA preference for the current user. */
|
||||||
* Returns MFA preference for the current user.
|
|
||||||
*/
|
|
||||||
async getMFAPreference() {
|
async getMFAPreference() {
|
||||||
return Promise.resolve(results.Ok('NOMFA'))
|
return Promise.resolve(results.Ok('NOMFA'))
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,7 @@ interface UserAttributes {
|
|||||||
}
|
}
|
||||||
/* eslint-enable @typescript-eslint/naming-convention */
|
/* eslint-enable @typescript-eslint/naming-convention */
|
||||||
|
|
||||||
/**
|
/** The type of multi-factor authentication (MFA) that the user has set up. */
|
||||||
* The type of multi-factor authentication (MFA) that the user has set up.
|
|
||||||
*/
|
|
||||||
export type MfaType = 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | 'TOTP'
|
export type MfaType = 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | 'TOTP'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,9 +242,7 @@ export class Cognito {
|
|||||||
return userInfo.attributes['custom:organizationId'] ?? null
|
return userInfo.attributes['custom:organizationId'] ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets user email from cognito */
|
||||||
* Gets user email from cognito
|
|
||||||
*/
|
|
||||||
async email() {
|
async email() {
|
||||||
// This `any` comes from a third-party API and cannot be avoided.
|
// This `any` comes from a third-party API and cannot be avoided.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
@ -337,9 +333,7 @@ export class Cognito {
|
|||||||
return result.mapErr(intoAmplifyErrorOrThrow).mapErr(intoSignInWithPasswordErrorOrThrow)
|
return result.mapErr(intoAmplifyErrorOrThrow).mapErr(intoSignInWithPasswordErrorOrThrow)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Refresh the current user session. */
|
||||||
* Refresh the current user session.
|
|
||||||
*/
|
|
||||||
async refreshUserSession() {
|
async refreshUserSession() {
|
||||||
const result = await results.Result.wrapAsync(async () => {
|
const result = await results.Result.wrapAsync(async () => {
|
||||||
const currentUser = await currentAuthenticatedUser()
|
const currentUser = await currentAuthenticatedUser()
|
||||||
@ -432,9 +426,7 @@ export class Cognito {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Start the TOTP setup process. Returns the secret and the URL to scan the QR code. */
|
||||||
* Start the TOTP setup process. Returns the secret and the URL to scan the QR code.
|
|
||||||
*/
|
|
||||||
async setupTOTP() {
|
async setupTOTP() {
|
||||||
const email = await this.email()
|
const email = await this.email()
|
||||||
const cognitoUserResult = await currentAuthenticatedUser()
|
const cognitoUserResult = await currentAuthenticatedUser()
|
||||||
@ -472,9 +464,7 @@ export class Cognito {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Set the user's preferred MFA method. */
|
||||||
* Set the user's preferred MFA method.
|
|
||||||
*/
|
|
||||||
async updateMFAPreference(mfaMethod: MfaType) {
|
async updateMFAPreference(mfaMethod: MfaType) {
|
||||||
const cognitoUserResult = await currentAuthenticatedUser()
|
const cognitoUserResult = await currentAuthenticatedUser()
|
||||||
if (cognitoUserResult.ok) {
|
if (cognitoUserResult.ok) {
|
||||||
@ -488,9 +478,7 @@ export class Cognito {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Get the user's preferred MFA method. */
|
||||||
* Get the user's preferred MFA method.
|
|
||||||
*/
|
|
||||||
async getMFAPreference() {
|
async getMFAPreference() {
|
||||||
const cognitoUserResult = await currentAuthenticatedUser()
|
const cognitoUserResult = await currentAuthenticatedUser()
|
||||||
if (cognitoUserResult.ok) {
|
if (cognitoUserResult.ok) {
|
||||||
@ -523,9 +511,7 @@ export class Cognito {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Confirm the sign in with the MFA token. */
|
||||||
* Confirm the sign in with the MFA token.
|
|
||||||
*/
|
|
||||||
async confirmSignIn(
|
async confirmSignIn(
|
||||||
user: amplify.CognitoUser,
|
user: amplify.CognitoUser,
|
||||||
confirmationCode: string,
|
confirmationCode: string,
|
||||||
|
@ -11,9 +11,7 @@ import { createContext, useContext, useId } from 'react'
|
|||||||
import { twJoin } from '#/utilities/tailwindMerge'
|
import { twJoin } from '#/utilities/tailwindMerge'
|
||||||
import invariant from 'tiny-invariant'
|
import invariant from 'tiny-invariant'
|
||||||
|
|
||||||
/**
|
/** Props for {@link AnimatedBackground}. */
|
||||||
* Props for {@link AnimatedBackground}.
|
|
||||||
*/
|
|
||||||
interface AnimatedBackgroundProps extends PropsWithChildren {
|
interface AnimatedBackgroundProps extends PropsWithChildren {
|
||||||
readonly value: string
|
readonly value: string
|
||||||
readonly transition?: Transition
|
readonly transition?: Transition
|
||||||
@ -37,9 +35,7 @@ const DEFAULT_TRANSITION: Transition = {
|
|||||||
velocity: 12,
|
velocity: 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** `<AnimatedBackground />` component visually highlights selected items by sliding a background into view when hovered over or clicked. */
|
||||||
* `<AnimatedBackground />` component visually highlights selected items by sliding a background into view when hovered over or clicked.
|
|
||||||
*/
|
|
||||||
export function AnimatedBackground(props: AnimatedBackgroundProps) {
|
export function AnimatedBackground(props: AnimatedBackgroundProps) {
|
||||||
const { value, transition = DEFAULT_TRANSITION, children } = props
|
const { value, transition = DEFAULT_TRANSITION, children } = props
|
||||||
const layoutId = useId()
|
const layoutId = useId()
|
||||||
@ -51,18 +47,14 @@ export function AnimatedBackground(props: AnimatedBackgroundProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for {@link AnimatedBackground.Item}. */
|
||||||
* Props for {@link AnimatedBackground.Item}.
|
|
||||||
*/
|
|
||||||
interface AnimatedBackgroundItemProps extends PropsWithChildren {
|
interface AnimatedBackgroundItemProps extends PropsWithChildren {
|
||||||
readonly value: string
|
readonly value: string
|
||||||
readonly className?: string
|
readonly className?: string
|
||||||
readonly animationClassName?: string
|
readonly animationClassName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Item within an {@link AnimatedBackground}. */
|
||||||
* Item within an {@link AnimatedBackground}.
|
|
||||||
*/
|
|
||||||
AnimatedBackground.Item = function AnimatedBackgroundItem(props: AnimatedBackgroundItemProps) {
|
AnimatedBackground.Item = function AnimatedBackgroundItem(props: AnimatedBackgroundItemProps) {
|
||||||
const context = useContext(AnimatedBackgroundContext)
|
const context = useContext(AnimatedBackgroundContext)
|
||||||
invariant(context, 'useAnimatedBackground must be used within an AnimatedBackgroundProvider')
|
invariant(context, 'useAnimatedBackground must be used within an AnimatedBackgroundProvider')
|
||||||
|
@ -61,9 +61,7 @@ export interface AlertProps
|
|||||||
extends PropsWithChildren,
|
extends PropsWithChildren,
|
||||||
VariantProps<typeof ALERT_STYLES>,
|
VariantProps<typeof ALERT_STYLES>,
|
||||||
HTMLAttributes<HTMLDivElement> {
|
HTMLAttributes<HTMLDivElement> {
|
||||||
/**
|
/** The icon to display in the Alert */
|
||||||
* The icon to display in the Alert
|
|
||||||
*/
|
|
||||||
readonly icon?: React.ReactElement | string | null | undefined
|
readonly icon?: React.ReactElement | string | null | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,39 +22,29 @@ export type ButtonProps =
|
|||||||
| (BaseButtonProps<aria.ButtonRenderProps> & Omit<aria.ButtonProps, 'onPress'> & PropsWithoutHref)
|
| (BaseButtonProps<aria.ButtonRenderProps> & Omit<aria.ButtonProps, 'onPress'> & PropsWithoutHref)
|
||||||
| (BaseButtonProps<aria.LinkRenderProps> & Omit<aria.LinkProps, 'onPress'> & PropsWithHref)
|
| (BaseButtonProps<aria.LinkRenderProps> & Omit<aria.LinkProps, 'onPress'> & PropsWithHref)
|
||||||
|
|
||||||
/**
|
/** Props for a button with an href. */
|
||||||
* Props for a button with an href.
|
|
||||||
*/
|
|
||||||
interface PropsWithHref {
|
interface PropsWithHref {
|
||||||
readonly href: string
|
readonly href: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for a button without an href. */
|
||||||
* Props for a button without an href.
|
|
||||||
*/
|
|
||||||
interface PropsWithoutHref {
|
interface PropsWithoutHref {
|
||||||
readonly href?: never
|
readonly href?: never
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Base props for a button. */
|
||||||
* Base props for a button.
|
|
||||||
*/
|
|
||||||
export interface BaseButtonProps<Render>
|
export interface BaseButtonProps<Render>
|
||||||
extends Omit<VariantProps<typeof BUTTON_STYLES>, 'iconOnly'> {
|
extends Omit<VariantProps<typeof BUTTON_STYLES>, 'iconOnly'> {
|
||||||
/** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */
|
/** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */
|
||||||
readonly tooltip?: React.ReactElement | string | false | null
|
readonly tooltip?: React.ReactElement | string | false | null
|
||||||
readonly tooltipPlacement?: aria.Placement
|
readonly tooltipPlacement?: aria.Placement
|
||||||
/**
|
/** The icon to display in the button */
|
||||||
* The icon to display in the button
|
|
||||||
*/
|
|
||||||
readonly icon?:
|
readonly icon?:
|
||||||
| React.ReactElement
|
| React.ReactElement
|
||||||
| string
|
| string
|
||||||
| ((render: Render) => React.ReactElement | string | null)
|
| ((render: Render) => React.ReactElement | string | null)
|
||||||
| null
|
| null
|
||||||
/**
|
/** When `true`, icon will be shown only when hovered. */
|
||||||
* When `true`, icon will be shown only when hovered.
|
|
||||||
*/
|
|
||||||
readonly showIconOnHover?: boolean
|
readonly showIconOnHover?: boolean
|
||||||
/**
|
/**
|
||||||
* Handler that is called when the press is released over the target.
|
* Handler that is called when the press is released over the target.
|
||||||
|
@ -33,9 +33,7 @@ import type { TestIdProps } from '../types'
|
|||||||
import { useCheckboxContext } from './CheckboxContext'
|
import { useCheckboxContext } from './CheckboxContext'
|
||||||
import { CheckboxGroup } from './CheckboxGroup'
|
import { CheckboxGroup } from './CheckboxGroup'
|
||||||
|
|
||||||
/**
|
/** Props for the {@link Checkbox} component. */
|
||||||
* Props for the {@link Checkbox} component.
|
|
||||||
*/
|
|
||||||
export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> = Omit<
|
export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> = Omit<
|
||||||
VariantProps<typeof CHECKBOX_STYLES>,
|
VariantProps<typeof CHECKBOX_STYLES>,
|
||||||
'isDisabled' | 'isInvalid'
|
'isDisabled' | 'isInvalid'
|
||||||
@ -46,18 +44,14 @@ export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<S
|
|||||||
readonly checkboxRef?: MutableRefObject<HTMLInputElement>
|
readonly checkboxRef?: MutableRefObject<HTMLInputElement>
|
||||||
} & (CheckboxGroupCheckboxProps | StandaloneCheckboxProps<Schema, TFieldName>)
|
} & (CheckboxGroupCheckboxProps | StandaloneCheckboxProps<Schema, TFieldName>)
|
||||||
|
|
||||||
/**
|
/** Props for the {@link Checkbox} component when used inside a {@link CheckboxGroup}. */
|
||||||
* Props for the {@link Checkbox} component when used inside a {@link CheckboxGroup}.
|
|
||||||
*/
|
|
||||||
interface CheckboxGroupCheckboxProps extends AriaCheckboxProps {
|
interface CheckboxGroupCheckboxProps extends AriaCheckboxProps {
|
||||||
readonly value: string
|
readonly value: string
|
||||||
readonly form?: never
|
readonly form?: never
|
||||||
readonly name?: never
|
readonly name?: never
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for the {@link Checkbox} component when used outside of a {@link CheckboxGroup}. */
|
||||||
* Props for the {@link Checkbox} component when used outside of a {@link CheckboxGroup}.
|
|
||||||
*/
|
|
||||||
type StandaloneCheckboxProps<
|
type StandaloneCheckboxProps<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
@ -123,9 +117,7 @@ export const TICK_VARIANTS: Variants = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected. */
|
||||||
* Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const Checkbox = forwardRef(function Checkbox<
|
export const Checkbox = forwardRef(function Checkbox<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file */
|
||||||
* @file
|
|
||||||
*/
|
|
||||||
import { useEventCallback } from '#/hooks/eventCallbackHooks'
|
import { useEventCallback } from '#/hooks/eventCallbackHooks'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import { createContext, useContext, useMemo, useState } from 'react'
|
import { createContext, useContext, useMemo, useState } from 'react'
|
||||||
@ -8,9 +6,7 @@ import type { StoreApi } from 'zustand'
|
|||||||
import { createStore } from 'zustand'
|
import { createStore } from 'zustand'
|
||||||
import type { TSchema, UseFormRegisterReturn } from '../Form'
|
import type { TSchema, UseFormRegisterReturn } from '../Form'
|
||||||
|
|
||||||
/**
|
/** Context for the checkbox. */
|
||||||
* Context for the checkbox.
|
|
||||||
*/
|
|
||||||
interface CheckboxContextType {
|
interface CheckboxContextType {
|
||||||
readonly store: StoreApi<CheckGroupPropsState>
|
readonly store: StoreApi<CheckGroupPropsState>
|
||||||
readonly addSelected: (selected: string) => void
|
readonly addSelected: (selected: string) => void
|
||||||
@ -25,9 +21,7 @@ const CheckboxContext = createContext<CheckboxContextType>({
|
|||||||
toggleSelected: () => {},
|
toggleSelected: () => {},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Gets the context for the checkbox. */
|
||||||
* Gets the context for the checkbox.
|
|
||||||
*/
|
|
||||||
export function useCheckboxContext() {
|
export function useCheckboxContext() {
|
||||||
return useContext(CheckboxContext)
|
return useContext(CheckboxContext)
|
||||||
}
|
}
|
||||||
@ -50,9 +44,7 @@ export function useCheckboxGroupState() {
|
|||||||
*/
|
*/
|
||||||
type CheckGroupPropsState = CheckBoxGroupPropsStateInsideGroup | CheckBoxGroupPropsStateOutsideGroup
|
type CheckGroupPropsState = CheckBoxGroupPropsStateInsideGroup | CheckBoxGroupPropsStateOutsideGroup
|
||||||
|
|
||||||
/**
|
/** Checkbox group state when the checkbox is inside a group. */
|
||||||
* Checkbox group state when the checkbox is inside a group.
|
|
||||||
*/
|
|
||||||
interface CheckBoxGroupPropsStateInsideGroup {
|
interface CheckBoxGroupPropsStateInsideGroup {
|
||||||
readonly insideGroup: true
|
readonly insideGroup: true
|
||||||
readonly selected: Set<string>
|
readonly selected: Set<string>
|
||||||
@ -60,16 +52,12 @@ interface CheckBoxGroupPropsStateInsideGroup {
|
|||||||
readonly field: UseFormRegisterReturn<TSchema>
|
readonly field: UseFormRegisterReturn<TSchema>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Checkbox group state when the checkbox is not inside a group. */
|
||||||
* Checkbox group state when the checkbox is not inside a group.
|
|
||||||
*/
|
|
||||||
interface CheckBoxGroupPropsStateOutsideGroup {
|
interface CheckBoxGroupPropsStateOutsideGroup {
|
||||||
readonly insideGroup: false
|
readonly insideGroup: false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for {@link CheckboxGroupProvider}. */
|
||||||
* Props for {@link CheckboxGroupProvider}.
|
|
||||||
*/
|
|
||||||
export interface CheckboxGroupProviderProps extends PropsWithChildren {
|
export interface CheckboxGroupProviderProps extends PropsWithChildren {
|
||||||
readonly name: string
|
readonly name: string
|
||||||
readonly onChange: (selected: string[]) => void
|
readonly onChange: (selected: string[]) => void
|
||||||
@ -77,9 +65,7 @@ export interface CheckboxGroupProviderProps extends PropsWithChildren {
|
|||||||
readonly defaultValue?: string[] | undefined
|
readonly defaultValue?: string[] | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Checkbox group provider used to manage the state of a group of checkboxes. */
|
||||||
* Checkbox group provider used to manage the state of a group of checkboxes.
|
|
||||||
*/
|
|
||||||
export function CheckboxGroupProvider(props: CheckboxGroupProviderProps) {
|
export function CheckboxGroupProvider(props: CheckboxGroupProviderProps) {
|
||||||
const { children, onChange, name, field, defaultValue = [] } = props
|
const { children, onChange, name, field, defaultValue = [] } = props
|
||||||
|
|
||||||
|
@ -16,9 +16,7 @@ import { Form, type FieldPath, type FieldProps, type FieldStateProps, type TSche
|
|||||||
import type { TestIdProps } from '../types'
|
import type { TestIdProps } from '../types'
|
||||||
import { CheckboxGroupProvider } from './CheckboxContext'
|
import { CheckboxGroupProvider } from './CheckboxContext'
|
||||||
|
|
||||||
/**
|
/** Props for the {@link CheckboxGroupProps} component. */
|
||||||
* Props for the {@link CheckboxGroupProps} component.
|
|
||||||
*/
|
|
||||||
export interface CheckboxGroupProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
export interface CheckboxGroupProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
||||||
extends FieldStateProps<AriaCheckboxGroupProps, Schema, TFieldName>,
|
extends FieldStateProps<AriaCheckboxGroupProps, Schema, TFieldName>,
|
||||||
FieldProps,
|
FieldProps,
|
||||||
@ -36,9 +34,7 @@ const CHECKBOX_GROUP_STYLES = tv({
|
|||||||
variants: { fullWidth: { true: 'w-full' } },
|
variants: { fullWidth: { true: 'w-full' } },
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A CheckboxGroup allows users to select one or more items from a list of choices. */
|
||||||
* A CheckboxGroup allows users to select one or more items from a list of choices.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const CheckboxGroup = forwardRef(
|
export const CheckboxGroup = forwardRef(
|
||||||
<Schema extends TSchema, TFieldName extends FieldPath<Schema>>(
|
<Schema extends TSchema, TFieldName extends FieldPath<Schema>>(
|
||||||
|
@ -12,14 +12,10 @@ import * as eventCallback from '#/hooks/eventCallbackHooks'
|
|||||||
import * as button from '../Button'
|
import * as button from '../Button'
|
||||||
import * as dialogProvider from './DialogProvider'
|
import * as dialogProvider from './DialogProvider'
|
||||||
|
|
||||||
/**
|
/** Props for {@link Close} component. */
|
||||||
* Props for {@link Close} component.
|
|
||||||
*/
|
|
||||||
export type CloseProps = button.ButtonProps
|
export type CloseProps = button.ButtonProps
|
||||||
|
|
||||||
/**
|
/** Close button for a dialog. */
|
||||||
* Close button for a dialog.
|
|
||||||
*/
|
|
||||||
export function Close(props: CloseProps) {
|
export function Close(props: CloseProps) {
|
||||||
const dialogContext = dialogProvider.useDialogContext()
|
const dialogContext = dialogProvider.useDialogContext()
|
||||||
|
|
||||||
|
@ -23,9 +23,7 @@ import { DIALOG_BACKGROUND } from './variants'
|
|||||||
// =================
|
// =================
|
||||||
// === Constants ===
|
// === Constants ===
|
||||||
// =================
|
// =================
|
||||||
/**
|
/** Props for the {@link Dialog} component. */
|
||||||
* Props for the {@link Dialog} component.
|
|
||||||
*/
|
|
||||||
export interface DialogProps
|
export interface DialogProps
|
||||||
extends types.DialogProps,
|
extends types.DialogProps,
|
||||||
Omit<VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {}
|
Omit<VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {}
|
||||||
@ -177,9 +175,7 @@ export function Dialog(props: DialogProps) {
|
|||||||
|
|
||||||
const [isScrolledToTop, setIsScrolledToTop] = React.useState(true)
|
const [isScrolledToTop, setIsScrolledToTop] = React.useState(true)
|
||||||
|
|
||||||
/**
|
/** Handles the scroll event on the dialog content. */
|
||||||
* Handles the scroll event on the dialog content.
|
|
||||||
*/
|
|
||||||
const handleScroll = (scrollTop: number) => {
|
const handleScroll = (scrollTop: number) => {
|
||||||
React.startTransition(() => {
|
React.startTransition(() => {
|
||||||
if (scrollTop > 0) {
|
if (scrollTop > 0) {
|
||||||
|
@ -5,28 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
/**
|
/** The context value for a dialog. */
|
||||||
* The context value for a dialog.
|
|
||||||
*/
|
|
||||||
export interface DialogContextValue {
|
export interface DialogContextValue {
|
||||||
readonly close: () => void
|
readonly close: () => void
|
||||||
readonly dialogId: string
|
readonly dialogId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The context for a dialog. */
|
||||||
* The context for a dialog.
|
|
||||||
*/
|
|
||||||
const DialogContext = React.createContext<DialogContextValue | null>(null)
|
const DialogContext = React.createContext<DialogContextValue | null>(null)
|
||||||
|
|
||||||
/**
|
/** The provider for a dialog. */
|
||||||
* The provider for a dialog.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const DialogProvider = DialogContext.Provider
|
export const DialogProvider = DialogContext.Provider
|
||||||
|
|
||||||
/**
|
/** Custom hook to get the dialog context. */
|
||||||
* Custom hook to get the dialog context.
|
|
||||||
*/
|
|
||||||
export function useDialogContext() {
|
export function useDialogContext() {
|
||||||
return React.useContext(DialogContext)
|
return React.useContext(DialogContext)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file This file provides the DialogStackProvider component and related functionality. */
|
||||||
* @file This file provides the DialogStackProvider component and related functionality.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
@ -8,17 +6,13 @@ import invariant from 'tiny-invariant'
|
|||||||
|
|
||||||
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
||||||
|
|
||||||
/**
|
/** DialogStackItem represents an item in the dialog stack. */
|
||||||
* DialogStackItem represents an item in the dialog stack.
|
|
||||||
*/
|
|
||||||
export interface DialogStackItem {
|
export interface DialogStackItem {
|
||||||
readonly id: string
|
readonly id: string
|
||||||
readonly type: 'dialog-fullscreen' | 'dialog' | 'popover'
|
readonly type: 'dialog-fullscreen' | 'dialog' | 'popover'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** DialogStackContextType represents the context for the dialog stack. */
|
||||||
* DialogStackContextType represents the context for the dialog stack.
|
|
||||||
*/
|
|
||||||
export interface DialogStackContextType {
|
export interface DialogStackContextType {
|
||||||
readonly stack: DialogStackItem[]
|
readonly stack: DialogStackItem[]
|
||||||
readonly dialogsStack: DialogStackItem[]
|
readonly dialogsStack: DialogStackItem[]
|
||||||
@ -28,9 +22,7 @@ export interface DialogStackContextType {
|
|||||||
|
|
||||||
const DialogStackContext = React.createContext<DialogStackContextType | null>(null)
|
const DialogStackContext = React.createContext<DialogStackContextType | null>(null)
|
||||||
|
|
||||||
/**
|
/** DialogStackProvider is a React component that provides the dialog stack context to its children. */
|
||||||
* DialogStackProvider is a React component that provides the dialog stack context to its children.
|
|
||||||
*/
|
|
||||||
export function DialogStackProvider(props: React.PropsWithChildren) {
|
export function DialogStackProvider(props: React.PropsWithChildren) {
|
||||||
const { children } = props
|
const { children } = props
|
||||||
|
|
||||||
@ -72,9 +64,7 @@ updated properly.`)
|
|||||||
return <DialogStackContext.Provider value={value}>{children}</DialogStackContext.Provider>
|
return <DialogStackContext.Provider value={value}>{children}</DialogStackContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** DialogStackRegistrar is a React component that registers a dialog in the dialog stack. */
|
||||||
* DialogStackRegistrar is a React component that registers a dialog in the dialog stack.
|
|
||||||
*/
|
|
||||||
export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackItem>) {
|
export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackItem>) {
|
||||||
const { children, id: idRaw, type: typeRaw } = props
|
const { children, id: idRaw, type: typeRaw } = props
|
||||||
const idRef = React.useRef(idRaw)
|
const idRef = React.useRef(idRaw)
|
||||||
@ -100,16 +90,12 @@ export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackI
|
|||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for {@link useDialogStackState} */
|
||||||
* Props for {@link useDialogStackState}
|
|
||||||
*/
|
|
||||||
export interface UseDialogStackStateProps {
|
export interface UseDialogStackStateProps {
|
||||||
readonly id: string
|
readonly id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** useDialogStackState is a custom hook that provides the state of the dialog stack. */
|
||||||
* useDialogStackState is a custom hook that provides the state of the dialog stack.
|
|
||||||
*/
|
|
||||||
export function useDialogStackState(props: UseDialogStackStateProps) {
|
export function useDialogStackState(props: UseDialogStackStateProps) {
|
||||||
const ctx = React.useContext(DialogStackContext)
|
const ctx = React.useContext(DialogStackContext)
|
||||||
|
|
||||||
|
@ -10,21 +10,15 @@ import { useOverlayTriggerState } from 'react-stately'
|
|||||||
|
|
||||||
const PLACEHOLDER = <div />
|
const PLACEHOLDER = <div />
|
||||||
|
|
||||||
/**
|
/** Props passed to the render function of a {@link DialogTrigger}. */
|
||||||
* Props passed to the render function of a {@link DialogTrigger}.
|
|
||||||
*/
|
|
||||||
export interface DialogTriggerRenderProps {
|
export interface DialogTriggerRenderProps {
|
||||||
readonly isOpen: boolean
|
readonly isOpen: boolean
|
||||||
readonly close: () => void
|
readonly close: () => void
|
||||||
readonly open: () => void
|
readonly open: () => void
|
||||||
}
|
}
|
||||||
/**
|
/** Props for a {@link DialogTrigger}. */
|
||||||
* Props for a {@link DialogTrigger}.
|
|
||||||
*/
|
|
||||||
export interface DialogTriggerProps extends Omit<aria.DialogTriggerProps, 'children'> {
|
export interface DialogTriggerProps extends Omit<aria.DialogTriggerProps, 'children'> {
|
||||||
/**
|
/** The trigger element. */
|
||||||
* The trigger element.
|
|
||||||
*/
|
|
||||||
readonly children: [
|
readonly children: [
|
||||||
React.ReactElement,
|
React.ReactElement,
|
||||||
React.ReactElement | ((props: DialogTriggerRenderProps) => React.ReactElement),
|
React.ReactElement | ((props: DialogTriggerRenderProps) => React.ReactElement),
|
||||||
|
@ -23,16 +23,12 @@ const IGNORE_INTERACT_OUTSIDE_ELEMENTS = [
|
|||||||
|
|
||||||
const IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR = `:is(${IGNORE_INTERACT_OUTSIDE_ELEMENTS.join(', ')})`
|
const IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR = `:is(${IGNORE_INTERACT_OUTSIDE_ELEMENTS.join(', ')})`
|
||||||
|
|
||||||
/**
|
/** Check if the element is a part of a component that should ignore the interact outside event */
|
||||||
* Check if the element is a part of a component that should ignore the interact outside event
|
|
||||||
*/
|
|
||||||
export function shouldIgnoreInteractOutside(element: HTMLElement) {
|
export function shouldIgnoreInteractOutside(element: HTMLElement) {
|
||||||
return element.closest(IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR)
|
return element.closest(IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for {@link useInteractOutside} */
|
||||||
* Props for {@link useInteractOutside}
|
|
||||||
*/
|
|
||||||
export interface UseInteractOutsideProps {
|
export interface UseInteractOutsideProps {
|
||||||
readonly ref: React.RefObject<HTMLElement>
|
readonly ref: React.RefObject<HTMLElement>
|
||||||
readonly id: string
|
readonly id: string
|
||||||
@ -40,9 +36,7 @@ export interface UseInteractOutsideProps {
|
|||||||
readonly isDisabled?: boolean
|
readonly isDisabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Hook that handles the interact outside event for the dialog */
|
||||||
* Hook that handles the interact outside event for the dialog
|
|
||||||
*/
|
|
||||||
export function useInteractOutside(props: UseInteractOutsideProps) {
|
export function useInteractOutside(props: UseInteractOutsideProps) {
|
||||||
const { ref, id, onInteractOutside, isDisabled = false } = props
|
const { ref, id, onInteractOutside, isDisabled = false } = props
|
||||||
const shouldCloseOnInteractOutsideRef = React.useRef(false)
|
const shouldCloseOnInteractOutsideRef = React.useRef(false)
|
||||||
|
@ -14,9 +14,7 @@ import * as text from '../../Text'
|
|||||||
import { Form } from '../Form'
|
import { Form } from '../Form'
|
||||||
import type * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
/**
|
/** Props for Field component */
|
||||||
* Props for Field component
|
|
||||||
*/
|
|
||||||
export interface FieldComponentProps<Schema extends types.TSchema>
|
export interface FieldComponentProps<Schema extends types.TSchema>
|
||||||
extends VariantProps<typeof FIELD_STYLES>,
|
extends VariantProps<typeof FIELD_STYLES>,
|
||||||
types.FieldProps {
|
types.FieldProps {
|
||||||
@ -29,16 +27,12 @@ export interface FieldComponentProps<Schema extends types.TSchema>
|
|||||||
readonly style?: React.CSSProperties | undefined
|
readonly style?: React.CSSProperties | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for Field variants */
|
||||||
* Props for Field variants
|
|
||||||
*/
|
|
||||||
export interface FieldVariantProps {
|
export interface FieldVariantProps {
|
||||||
readonly fieldVariants?: VariantProps<typeof FIELD_STYLES>['variants'] | undefined
|
readonly fieldVariants?: VariantProps<typeof FIELD_STYLES>['variants'] | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for Field children */
|
||||||
* Props for Field children
|
|
||||||
*/
|
|
||||||
export interface FieldChildrenRenderProps {
|
export interface FieldChildrenRenderProps {
|
||||||
readonly isInvalid: boolean
|
readonly isInvalid: boolean
|
||||||
readonly isDirty: boolean
|
readonly isDirty: boolean
|
||||||
@ -65,9 +59,7 @@ export const FIELD_STYLES = tv({
|
|||||||
defaultVariants: { fullWidth: true },
|
defaultVariants: { fullWidth: true },
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Field component */
|
||||||
* Field component
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const Field = forwardRef(function Field<Schema extends types.TSchema>(
|
export const Field = forwardRef(function Field<Schema extends types.TSchema>(
|
||||||
props: FieldComponentProps<Schema>,
|
props: FieldComponentProps<Schema>,
|
||||||
|
@ -13,18 +13,14 @@ import * as reactAriaComponents from '#/components/AriaComponents'
|
|||||||
import * as formContext from './FormProvider'
|
import * as formContext from './FormProvider'
|
||||||
import type * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
/**
|
/** Props for the FormError component. */
|
||||||
* Props for the FormError component.
|
|
||||||
*/
|
|
||||||
export interface FormErrorProps extends Omit<reactAriaComponents.AlertProps, 'children'> {
|
export interface FormErrorProps extends Omit<reactAriaComponents.AlertProps, 'children'> {
|
||||||
// We do not need to know the form fields.
|
// We do not need to know the form fields.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
readonly form?: types.FormInstance<any>
|
readonly form?: types.FormInstance<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Form error component. */
|
||||||
* Form error component.
|
|
||||||
*/
|
|
||||||
export function FormError(props: FormErrorProps) {
|
export function FormError(props: FormErrorProps) {
|
||||||
const { size = 'large', variant = 'error', rounded = 'large', ...alertProps } = props
|
const { size = 'large', variant = 'error', rounded = 'large', ...alertProps } = props
|
||||||
|
|
||||||
@ -33,9 +29,7 @@ export function FormError(props: FormErrorProps) {
|
|||||||
const { errors } = formState
|
const { errors } = formState
|
||||||
const { getText } = textProvider.useText()
|
const { getText } = textProvider.useText()
|
||||||
|
|
||||||
/**
|
/** Get the error message. */
|
||||||
* Get the error message.
|
|
||||||
*/
|
|
||||||
const getSubmitError = (): string | null => {
|
const getSubmitError = (): string | null => {
|
||||||
const formErrors = errors.root
|
const formErrors = errors.root
|
||||||
|
|
||||||
|
@ -9,9 +9,7 @@ import invariant from 'tiny-invariant'
|
|||||||
import type * as types from './types'
|
import type * as types from './types'
|
||||||
import type { FormInstance, FormInstanceValidated } from './types'
|
import type { FormInstance, FormInstanceValidated } from './types'
|
||||||
|
|
||||||
/**
|
/** Context type for the form provider. */
|
||||||
* Context type for the form provider.
|
|
||||||
*/
|
|
||||||
interface FormContextType<Schema extends types.TSchema> {
|
interface FormContextType<Schema extends types.TSchema> {
|
||||||
readonly form: types.UseFormReturn<Schema>
|
readonly form: types.UseFormReturn<Schema>
|
||||||
}
|
}
|
||||||
@ -20,9 +18,7 @@ interface FormContextType<Schema extends types.TSchema> {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const FormContext = createContext<FormContextType<any> | null>(null)
|
const FormContext = createContext<FormContextType<any> | null>(null)
|
||||||
|
|
||||||
/**
|
/** Provides the form instance to the component tree. */
|
||||||
* Provides the form instance to the component tree.
|
|
||||||
*/
|
|
||||||
export function FormProvider<Schema extends types.TSchema>(
|
export function FormProvider<Schema extends types.TSchema>(
|
||||||
props: FormContextType<Schema> & PropsWithChildren,
|
props: FormContextType<Schema> & PropsWithChildren,
|
||||||
) {
|
) {
|
||||||
@ -36,9 +32,7 @@ export function FormProvider<Schema extends types.TSchema>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the form instance from the context. */
|
||||||
* Returns the form instance from the context.
|
|
||||||
*/
|
|
||||||
export function useFormContext<Schema extends types.TSchema>(
|
export function useFormContext<Schema extends types.TSchema>(
|
||||||
form?: FormInstanceValidated<Schema>,
|
form?: FormInstanceValidated<Schema>,
|
||||||
): FormInstance<Schema> {
|
): FormInstance<Schema> {
|
||||||
@ -56,9 +50,7 @@ export function useFormContext<Schema extends types.TSchema>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the form instance from the context, or null if the context is not available. */
|
||||||
* Returns the form instance from the context, or null if the context is not available.
|
|
||||||
*/
|
|
||||||
export function useOptionalFormContext<
|
export function useOptionalFormContext<
|
||||||
Form extends FormInstanceValidated<Schema> | undefined,
|
Form extends FormInstanceValidated<Schema> | undefined,
|
||||||
Schema extends types.TSchema,
|
Schema extends types.TSchema,
|
||||||
|
@ -11,9 +11,7 @@ import { useText } from '#/providers/TextProvider'
|
|||||||
import * as formContext from './FormProvider'
|
import * as formContext from './FormProvider'
|
||||||
import type * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
/**
|
/** Props for the Reset component. */
|
||||||
* Props for the Reset component.
|
|
||||||
*/
|
|
||||||
export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'> {
|
export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'> {
|
||||||
/**
|
/**
|
||||||
* Connects the submit button to a form.
|
* Connects the submit button to a form.
|
||||||
@ -28,9 +26,7 @@ export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'>
|
|||||||
readonly action?: 'cancel' | 'reset'
|
readonly action?: 'cancel' | 'reset'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Reset button for forms. */
|
||||||
* Reset button for forms.
|
|
||||||
*/
|
|
||||||
export function Reset(props: ResetProps): React.JSX.Element {
|
export function Reset(props: ResetProps): React.JSX.Element {
|
||||||
const { getText } = useText()
|
const { getText } = useText()
|
||||||
const {
|
const {
|
||||||
|
@ -11,9 +11,7 @@ import { useText } from '#/providers/TextProvider'
|
|||||||
import { useFormContext } from './FormProvider'
|
import { useFormContext } from './FormProvider'
|
||||||
import type { FormInstance } from './types'
|
import type { FormInstance } from './types'
|
||||||
|
|
||||||
/**
|
/** Additional props for the Submit component. */
|
||||||
* Additional props for the Submit component.
|
|
||||||
*/
|
|
||||||
interface SubmitButtonBaseProps {
|
interface SubmitButtonBaseProps {
|
||||||
readonly variant?: ButtonProps['variant']
|
readonly variant?: ButtonProps['variant']
|
||||||
/**
|
/**
|
||||||
@ -29,9 +27,7 @@ interface SubmitButtonBaseProps {
|
|||||||
readonly action?: 'cancel' | 'submit' | 'update'
|
readonly action?: 'cancel' | 'submit' | 'update'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for the Submit component. */
|
||||||
* Props for the Submit component.
|
|
||||||
*/
|
|
||||||
export type SubmitProps = Omit<ButtonProps, 'formnovalidate' | 'href' | 'variant'> &
|
export type SubmitProps = Omit<ButtonProps, 'formnovalidate' | 'href' | 'variant'> &
|
||||||
SubmitButtonBaseProps
|
SubmitButtonBaseProps
|
||||||
|
|
||||||
|
@ -24,17 +24,13 @@ export type TransformedValues<Schema extends TSchema | undefined> =
|
|||||||
*/
|
*/
|
||||||
export type FieldPath<Schema extends TSchema> = reactHookForm.FieldPath<FieldValues<Schema>>
|
export type FieldPath<Schema extends TSchema> = reactHookForm.FieldPath<FieldValues<Schema>>
|
||||||
|
|
||||||
/**
|
/** Schema type */
|
||||||
* Schema type
|
|
||||||
*/
|
|
||||||
export type TSchema =
|
export type TSchema =
|
||||||
| z.AnyZodObject
|
| z.AnyZodObject
|
||||||
| z.ZodEffects<z.AnyZodObject>
|
| z.ZodEffects<z.AnyZodObject>
|
||||||
| z.ZodEffects<z.ZodEffects<z.AnyZodObject>>
|
| z.ZodEffects<z.ZodEffects<z.AnyZodObject>>
|
||||||
|
|
||||||
/**
|
/** OnSubmitCallbacks type. */
|
||||||
* OnSubmitCallbacks type.
|
|
||||||
*/
|
|
||||||
export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void> {
|
export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void> {
|
||||||
readonly onSubmit?:
|
readonly onSubmit?:
|
||||||
| ((
|
| ((
|
||||||
@ -67,9 +63,7 @@ export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void>
|
|||||||
| undefined
|
| undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for the useForm hook. */
|
||||||
* Props for the useForm hook.
|
|
||||||
*/
|
|
||||||
export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
|
export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
|
||||||
extends Omit<
|
extends Omit<
|
||||||
reactHookForm.UseFormProps<FieldValues<Schema>>,
|
reactHookForm.UseFormProps<FieldValues<Schema>>,
|
||||||
@ -83,16 +77,12 @@ export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
|
|||||||
*/
|
*/
|
||||||
readonly canSubmitOffline?: boolean
|
readonly canSubmitOffline?: boolean
|
||||||
|
|
||||||
/**
|
/** Debug name for the form. Use it to identify the form in the tanstack query devtools. */
|
||||||
* Debug name for the form. Use it to identify the form in the tanstack query devtools.
|
|
||||||
*/
|
|
||||||
readonly debugName?: string
|
readonly debugName?: string
|
||||||
readonly method?: 'dialog' | (string & {}) | undefined
|
readonly method?: 'dialog' | (string & {}) | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Register function for a form field. */
|
||||||
* Register function for a form field.
|
|
||||||
*/
|
|
||||||
export type UseFormRegister<Schema extends TSchema> = <
|
export type UseFormRegister<Schema extends TSchema> = <
|
||||||
TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
|
||||||
>(
|
>(
|
||||||
@ -100,9 +90,7 @@ export type UseFormRegister<Schema extends TSchema> = <
|
|||||||
options?: reactHookForm.RegisterOptions<FieldValues<Schema>, TFieldName>,
|
options?: reactHookForm.RegisterOptions<FieldValues<Schema>, TFieldName>,
|
||||||
) => UseFormRegisterReturn<Schema, TFieldName>
|
) => UseFormRegisterReturn<Schema, TFieldName>
|
||||||
|
|
||||||
/**
|
/** UseFormRegister return type. */
|
||||||
* UseFormRegister return type.
|
|
||||||
*/
|
|
||||||
export interface UseFormRegisterReturn<
|
export interface UseFormRegisterReturn<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
|
||||||
@ -147,9 +135,7 @@ export type FormState<Schema extends TSchema> = reactHookForm.FormState<FieldVal
|
|||||||
*/
|
*/
|
||||||
export type FormInstance<Schema extends TSchema> = UseFormReturn<Schema>
|
export type FormInstance<Schema extends TSchema> = UseFormReturn<Schema>
|
||||||
|
|
||||||
/**
|
/** Form type interface that check if FieldValues type is compatible with the value type from component */
|
||||||
* Form type interface that check if FieldValues type is compatible with the value type from component
|
|
||||||
*/
|
|
||||||
export interface FormWithValueValidation<
|
export interface FormWithValueValidation<
|
||||||
BaseValueType,
|
BaseValueType,
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
@ -180,9 +166,7 @@ export type FormInstanceValidated<
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
> = FormInstance<Schema> | (any[] & NonNullable<unknown>)
|
> = FormInstance<Schema> | (any[] & NonNullable<unknown>)
|
||||||
|
|
||||||
/**
|
/** Props for the Field component. */
|
||||||
* Props for the Field component.
|
|
||||||
*/
|
|
||||||
// Readonly omitted here to avoid type mismatch with native HTML attributes
|
// Readonly omitted here to avoid type mismatch with native HTML attributes
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export interface FieldProps {
|
export interface FieldProps {
|
||||||
@ -191,27 +175,19 @@ export interface FieldProps {
|
|||||||
readonly description?: React.ReactNode | undefined
|
readonly description?: React.ReactNode | undefined
|
||||||
readonly error?: React.ReactNode | undefined
|
readonly error?: React.ReactNode | undefined
|
||||||
|
|
||||||
/**
|
/** Defines a string value that labels the current element. */
|
||||||
* Defines a string value that labels the current element.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
'aria-label'?: string | undefined
|
'aria-label'?: string | undefined
|
||||||
|
|
||||||
/**
|
/** Identifies the element (or elements) that labels the current element. */
|
||||||
* Identifies the element (or elements) that labels the current element.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
'aria-labelledby'?: string | undefined
|
'aria-labelledby'?: string | undefined
|
||||||
|
|
||||||
/**
|
/** Identifies the element (or elements) that describes the object. */
|
||||||
* Identifies the element (or elements) that describes the object.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
'aria-describedby'?: string | undefined
|
'aria-describedby'?: string | undefined
|
||||||
|
|
||||||
/**
|
/** Identifies the element (or elements) that provide a detailed, extended description for the object. */
|
||||||
* Identifies the element (or elements) that provide a detailed, extended description for the object.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
'aria-details'?: string | undefined
|
'aria-details'?: string | undefined
|
||||||
}
|
}
|
||||||
@ -233,9 +209,7 @@ export interface FormFieldProps<
|
|||||||
readonly isInvalid?: boolean | undefined
|
readonly isInvalid?: boolean | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Field State Props */
|
||||||
* Field State Props
|
|
||||||
*/
|
|
||||||
export type FieldStateProps<
|
export type FieldStateProps<
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
BaseProps extends { value?: unknown },
|
BaseProps extends { value?: unknown },
|
||||||
|
@ -8,9 +8,7 @@ import * as reactHookForm from 'react-hook-form'
|
|||||||
import * as formContext from './FormProvider'
|
import * as formContext from './FormProvider'
|
||||||
import type * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
/**
|
/** Options for {@link useField} hook. */
|
||||||
* Options for {@link useField} hook.
|
|
||||||
*/
|
|
||||||
export interface UseFieldOptions<
|
export interface UseFieldOptions<
|
||||||
BaseValueType,
|
BaseValueType,
|
||||||
Schema extends types.TSchema,
|
Schema extends types.TSchema,
|
||||||
@ -21,9 +19,7 @@ export interface UseFieldOptions<
|
|||||||
readonly defaultValue?: types.FieldValues<Schema>[TFieldName] | undefined
|
readonly defaultValue?: types.FieldValues<Schema>[TFieldName] | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A hook that connects a field to a form state. */
|
||||||
* A hook that connects a field to a form state.
|
|
||||||
*/
|
|
||||||
export function useField<
|
export function useField<
|
||||||
BaseValueType,
|
BaseValueType,
|
||||||
Schema extends types.TSchema,
|
Schema extends types.TSchema,
|
||||||
|
@ -13,9 +13,7 @@ import type {
|
|||||||
TSchema,
|
TSchema,
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
/**
|
/** Options for the useFieldRegister hook. */
|
||||||
* Options for the useFieldRegister hook.
|
|
||||||
*/
|
|
||||||
export type UseFieldRegisterOptions<
|
export type UseFieldRegisterOptions<
|
||||||
BaseValueType extends { value?: unknown },
|
BaseValueType extends { value?: unknown },
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
@ -31,9 +29,7 @@ export type UseFieldRegisterOptions<
|
|||||||
setValueAs?: ((value: unknown) => unknown) | undefined
|
setValueAs?: ((value: unknown) => unknown) | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Registers a field in the form. */
|
||||||
* Registers a field in the form.
|
|
||||||
*/
|
|
||||||
export function useFieldRegister<
|
export function useFieldRegister<
|
||||||
BaseValueType extends { value?: unknown },
|
BaseValueType extends { value?: unknown },
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
@ -62,9 +58,7 @@ export function useFieldRegister<
|
|||||||
|
|
||||||
return { fieldProps, formInstance } as const
|
return { fieldProps, formInstance } as const
|
||||||
}
|
}
|
||||||
/**
|
/** Tried to extract validation details from the schema. */
|
||||||
* Tried to extract validation details from the schema.
|
|
||||||
*/
|
|
||||||
// This name is intentional to highlight that this function is unsafe and should be used with caution.
|
// This name is intentional to highlight that this function is unsafe and should be used with caution.
|
||||||
// eslint-disable-next-line camelcase, @typescript-eslint/naming-convention
|
// eslint-disable-next-line camelcase, @typescript-eslint/naming-convention
|
||||||
function unsafe__extractValidationDetailsFromSchema<
|
function unsafe__extractValidationDetailsFromSchema<
|
||||||
|
@ -7,9 +7,7 @@ import { useFormState } from 'react-hook-form'
|
|||||||
import { useFormContext } from './FormProvider'
|
import { useFormContext } from './FormProvider'
|
||||||
import type { FieldPath, FormInstanceValidated, TSchema } from './types'
|
import type { FieldPath, FormInstanceValidated, TSchema } from './types'
|
||||||
|
|
||||||
/**
|
/** Options for the `useFieldState` hook. */
|
||||||
* Options for the `useFieldState` hook.
|
|
||||||
*/
|
|
||||||
export interface UseFieldStateOptions<
|
export interface UseFieldStateOptions<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
@ -18,9 +16,7 @@ export interface UseFieldStateOptions<
|
|||||||
readonly form?: FormInstanceValidated<Schema> | undefined
|
readonly form?: FormInstanceValidated<Schema> | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Hook to get the state of a field. */
|
||||||
* Hook to get the state of a field.
|
|
||||||
*/
|
|
||||||
export function useFieldState<Schema extends TSchema, TFieldName extends FieldPath<Schema>>(
|
export function useFieldState<Schema extends TSchema, TFieldName extends FieldPath<Schema>>(
|
||||||
options: UseFieldStateOptions<Schema, TFieldName>,
|
options: UseFieldStateOptions<Schema, TFieldName>,
|
||||||
) {
|
) {
|
||||||
|
@ -18,9 +18,7 @@ import { useMutation } from '@tanstack/react-query'
|
|||||||
import * as schemaModule from './schema'
|
import * as schemaModule from './schema'
|
||||||
import type * as types from './types'
|
import type * as types from './types'
|
||||||
|
|
||||||
/**
|
/** Maps the value to the event object. */
|
||||||
* Maps the value to the event object.
|
|
||||||
*/
|
|
||||||
function mapValueOnEvent(value: unknown) {
|
function mapValueOnEvent(value: unknown) {
|
||||||
if (typeof value === 'object' && value != null && 'target' in value && 'type' in value) {
|
if (typeof value === 'object' && value != null && 'target' in value && 'type' in value) {
|
||||||
return value
|
return value
|
||||||
@ -241,9 +239,7 @@ export function useForm<Schema extends types.TSchema, SubmitResult = void>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Get the type of arguments passed to the useForm hook */
|
||||||
* Get the type of arguments passed to the useForm hook
|
|
||||||
*/
|
|
||||||
function getArgsType<Schema extends types.TSchema, SubmitResult = void>(
|
function getArgsType<Schema extends types.TSchema, SubmitResult = void>(
|
||||||
args: types.UseFormProps<Schema, SubmitResult>,
|
args: types.UseFormProps<Schema, SubmitResult>,
|
||||||
) {
|
) {
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import * as twv from '#/utilities/tailwindVariants'
|
import * as twv from '#/utilities/tailwindVariants'
|
||||||
|
|
||||||
/**
|
/** Props for form components. */
|
||||||
* Props for form components.
|
|
||||||
*/
|
|
||||||
export type FormStyleProps = twv.VariantProps<typeof FORM_STYLES>
|
export type FormStyleProps = twv.VariantProps<typeof FORM_STYLES>
|
||||||
export const FORM_STYLES = twv.tv({
|
export const FORM_STYLES = twv.tv({
|
||||||
base: 'flex flex-col items-start',
|
base: 'flex flex-col items-start',
|
||||||
|
@ -14,18 +14,14 @@ import type * as styles from './styles'
|
|||||||
|
|
||||||
export type * from './components'
|
export type * from './components'
|
||||||
|
|
||||||
/**
|
/** Props for the Form component */
|
||||||
* Props for the Form component
|
|
||||||
*/
|
|
||||||
export type FormProps<
|
export type FormProps<
|
||||||
Schema extends components.TSchema,
|
Schema extends components.TSchema,
|
||||||
SubmitResult = void,
|
SubmitResult = void,
|
||||||
> = BaseFormProps<Schema> &
|
> = BaseFormProps<Schema> &
|
||||||
(FormPropsWithOptions<Schema, SubmitResult> | FormPropsWithParentForm<Schema>)
|
(FormPropsWithOptions<Schema, SubmitResult> | FormPropsWithParentForm<Schema>)
|
||||||
|
|
||||||
/**
|
/** Base props for the Form component. */
|
||||||
* Base props for the Form component.
|
|
||||||
*/
|
|
||||||
interface BaseFormProps<Schema extends components.TSchema>
|
interface BaseFormProps<Schema extends components.TSchema>
|
||||||
extends Omit<
|
extends Omit<
|
||||||
React.HTMLProps<HTMLFormElement>,
|
React.HTMLProps<HTMLFormElement>,
|
||||||
@ -48,9 +44,7 @@ interface BaseFormProps<Schema extends components.TSchema>
|
|||||||
|
|
||||||
readonly className?: string | ((props: components.UseFormReturn<Schema>) => string)
|
readonly className?: string | ((props: components.UseFormReturn<Schema>) => string)
|
||||||
|
|
||||||
/**
|
/** When set to `dialog`, form submission will close the parent dialog on successful submission. */
|
||||||
* When set to `dialog`, form submission will close the parent dialog on successful submission.
|
|
||||||
*/
|
|
||||||
readonly method?: 'dialog' | (NonNullable<unknown> & string)
|
readonly method?: 'dialog' | (NonNullable<unknown> & string)
|
||||||
|
|
||||||
readonly canSubmitOffline?: boolean
|
readonly canSubmitOffline?: boolean
|
||||||
@ -93,9 +87,7 @@ interface FormPropsWithOptions<Schema extends components.TSchema, SubmitResult =
|
|||||||
readonly form?: never
|
readonly form?: never
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Register function for a form field. */
|
||||||
* Register function for a form field.
|
|
||||||
*/
|
|
||||||
export type UseFormRegister<Schema extends components.TSchema> = <
|
export type UseFormRegister<Schema extends components.TSchema> = <
|
||||||
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
|
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
|
||||||
>(
|
>(
|
||||||
@ -103,9 +95,7 @@ export type UseFormRegister<Schema extends components.TSchema> = <
|
|||||||
options?: reactHookForm.RegisterOptions<components.FieldValues<Schema>, TFieldName>,
|
options?: reactHookForm.RegisterOptions<components.FieldValues<Schema>, TFieldName>,
|
||||||
) => UseFormRegisterReturn<Schema, TFieldName>
|
) => UseFormRegisterReturn<Schema, TFieldName>
|
||||||
|
|
||||||
/**
|
/** UseFormRegister return type. */
|
||||||
* UseFormRegister return type.
|
|
||||||
*/
|
|
||||||
export interface UseFormRegisterReturn<
|
export interface UseFormRegisterReturn<
|
||||||
Schema extends components.TSchema,
|
Schema extends components.TSchema,
|
||||||
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
|
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
|
||||||
@ -119,9 +109,7 @@ export interface UseFormRegisterReturn<
|
|||||||
readonly isInvalid?: boolean
|
readonly isInvalid?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Form Render Props. */
|
||||||
* Form Render Props.
|
|
||||||
*/
|
|
||||||
export type FormStateRenderProps<Schema extends components.TSchema> = Pick<
|
export type FormStateRenderProps<Schema extends components.TSchema> = Pick<
|
||||||
components.FormInstance<Schema>,
|
components.FormInstance<Schema>,
|
||||||
| 'clearErrors'
|
| 'clearErrors'
|
||||||
|
@ -32,9 +32,7 @@ import type { ExtractFunction, VariantProps } from '#/utilities/tailwindVariants
|
|||||||
import { omit } from 'enso-common/src/utilities/data/object'
|
import { omit } from 'enso-common/src/utilities/data/object'
|
||||||
import { INPUT_STYLES } from '../variants'
|
import { INPUT_STYLES } from '../variants'
|
||||||
|
|
||||||
/**
|
/** Props for the Input component. */
|
||||||
* Props for the Input component.
|
|
||||||
*/
|
|
||||||
export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
||||||
extends FieldStateProps<Omit<aria.InputProps, 'children' | 'size'>, Schema, TFieldName>,
|
extends FieldStateProps<Omit<aria.InputProps, 'children' | 'size'>, Schema, TFieldName>,
|
||||||
FieldProps,
|
FieldProps,
|
||||||
@ -53,9 +51,7 @@ export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath
|
|||||||
readonly fieldVariants?: FieldComponentProps<Schema>['variants']
|
readonly fieldVariants?: FieldComponentProps<Schema>['variants']
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Basic input component. Input component is a component that is used to get user input in a text field. */
|
||||||
* Basic input component. Input component is a component that is used to get user input in a text field.
|
|
||||||
*/
|
|
||||||
export const Input = forwardRef(function Input<
|
export const Input = forwardRef(function Input<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
|
@ -79,9 +79,7 @@ export const MULTI_SELECTOR_STYLES = tv({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A horizontal multi-selector. */
|
||||||
* A horizontal multi-selector.
|
|
||||||
*/
|
|
||||||
export const MultiSelector = forwardRef(function MultiSelector<
|
export const MultiSelector = forwardRef(function MultiSelector<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file */
|
||||||
* @file
|
|
||||||
*/
|
|
||||||
import { mergeProps } from '#/components/aria'
|
import { mergeProps } from '#/components/aria'
|
||||||
import { mergeRefs } from '#/utilities/mergeRefs'
|
import { mergeRefs } from '#/utilities/mergeRefs'
|
||||||
import type { VariantProps } from '#/utilities/tailwindVariants'
|
import type { VariantProps } from '#/utilities/tailwindVariants'
|
||||||
@ -23,9 +21,7 @@ import { Separator } from '../../Separator'
|
|||||||
import { TEXT_STYLE } from '../../Text'
|
import { TEXT_STYLE } from '../../Text'
|
||||||
import type { TestIdProps } from '../../types'
|
import type { TestIdProps } from '../../types'
|
||||||
|
|
||||||
/**
|
/** Props for an {@link OTPInput}. */
|
||||||
* Props for an {@link OTPInput}.
|
|
||||||
*/
|
|
||||||
export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
||||||
extends FieldStateProps<Omit<OTPInputProps, 'children' | 'render'>, Schema, TFieldName>,
|
extends FieldStateProps<Omit<OTPInputProps, 'children' | 'render'>, Schema, TFieldName>,
|
||||||
FieldProps,
|
FieldProps,
|
||||||
@ -40,9 +36,7 @@ export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldP
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
readonly submitOnComplete?: boolean
|
readonly submitOnComplete?: boolean
|
||||||
/**
|
/** Callback when the OTP is filled. */
|
||||||
* Callback when the OTP is filled.
|
|
||||||
*/
|
|
||||||
readonly onComplete?: () => void
|
readonly onComplete?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,9 +76,7 @@ const SLOT_STYLES = tv({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Accessible one-time password component with copy paste functionality. */
|
||||||
* Accessible one-time password component with copy paste functionality.
|
|
||||||
*/
|
|
||||||
export const OTPInput = forwardRef(function OTPInput<
|
export const OTPInput = forwardRef(function OTPInput<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
@ -196,9 +188,7 @@ export const OTPInput = forwardRef(function OTPInput<
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Props for a single {@link Slot}. */
|
||||||
* Props for a single {@link Slot}.
|
|
||||||
*/
|
|
||||||
interface SlotProps extends Omit<OTPInputSlotProps, 'isActive'>, VariantProps<typeof SLOT_STYLES> {}
|
interface SlotProps extends Omit<OTPInputSlotProps, 'isActive'>, VariantProps<typeof SLOT_STYLES> {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file A resizable input that uses a content-editable div. */
|
||||||
* @file A resizable input that uses a content-editable div.
|
|
||||||
*/
|
|
||||||
import {
|
import {
|
||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
@ -31,9 +29,7 @@ const CONTENT_EDITABLE_STYLES = tv({
|
|||||||
slots: { placeholder: 'opacity-50 absolute inset-0 pointer-events-none' },
|
slots: { placeholder: 'opacity-50 absolute inset-0 pointer-events-none' },
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Props for a {@link ResizableContentEditableInput}. */
|
||||||
* Props for a {@link ResizableContentEditableInput}.
|
|
||||||
*/
|
|
||||||
export interface ResizableContentEditableInputProps<
|
export interface ResizableContentEditableInputProps<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file A resizable input field. */
|
||||||
* @file A resizable input field.
|
|
||||||
*/
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
||||||
@ -12,17 +10,13 @@ import * as mergeRefs from '#/utilities/mergeRefs'
|
|||||||
import { forwardRef } from '#/utilities/react'
|
import { forwardRef } from '#/utilities/react'
|
||||||
import * as variants from '../variants'
|
import * as variants from '../variants'
|
||||||
|
|
||||||
/**
|
/** Props for a {@link ResizableInput}. */
|
||||||
* Props for a {@link ResizableInput}.
|
|
||||||
*/
|
|
||||||
export interface ResizableInputProps extends aria.TextFieldProps {
|
export interface ResizableInputProps extends aria.TextFieldProps {
|
||||||
readonly placeholder?: string
|
readonly placeholder?: string
|
||||||
readonly description?: React.ReactNode
|
readonly description?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A resizable input field. */
|
||||||
* A resizable input field.
|
|
||||||
*/
|
|
||||||
export const ResizableInput = forwardRef(function ResizableInput(
|
export const ResizableInput = forwardRef(function ResizableInput(
|
||||||
props: ResizableInputProps,
|
props: ResizableInputProps,
|
||||||
ref: React.ForwardedRef<HTMLTextAreaElement>,
|
ref: React.ForwardedRef<HTMLTextAreaElement>,
|
||||||
|
@ -76,9 +76,7 @@ export const SELECTOR_STYLES = tv({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A horizontal selector. */
|
||||||
* A horizontal selector.
|
|
||||||
*/
|
|
||||||
export const Selector = forwardRef(function Selector<
|
export const Selector = forwardRef(function Selector<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
TFieldName extends FieldPath<Schema>,
|
TFieldName extends FieldPath<Schema>,
|
||||||
|
@ -44,16 +44,12 @@ const RADIO_STYLES = twv.tv({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Props for the {@link Radio} component. */
|
||||||
* Props for the {@link Radio} component.
|
|
||||||
*/
|
|
||||||
export interface RadioProps extends aria.RadioProps {
|
export interface RadioProps extends aria.RadioProps {
|
||||||
readonly label?: string
|
readonly label?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A radio button. */
|
||||||
* A radio button.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const Radio = forwardRef(function Radio(
|
export const Radio = forwardRef(function Radio(
|
||||||
props: RadioProps,
|
props: RadioProps,
|
||||||
|
@ -16,9 +16,7 @@ import type { FieldVariantProps } from '../Form'
|
|||||||
import * as formComponent from '../Form'
|
import * as formComponent from '../Form'
|
||||||
import * as radioGroupContext from './RadioGroupContext'
|
import * as radioGroupContext from './RadioGroupContext'
|
||||||
|
|
||||||
/**
|
/** Props for {@link RadioGroup}. */
|
||||||
* Props for {@link RadioGroup}.
|
|
||||||
*/
|
|
||||||
export interface RadioGroupProps<
|
export interface RadioGroupProps<
|
||||||
Schema extends formComponent.TSchema,
|
Schema extends formComponent.TSchema,
|
||||||
TFieldName extends formComponent.FieldPath<Schema>,
|
TFieldName extends formComponent.FieldPath<Schema>,
|
||||||
@ -39,9 +37,7 @@ export const RADIO_GROUP_STYLES = twv.tv({
|
|||||||
variants: { fullWidth: { true: 'w-full' } },
|
variants: { fullWidth: { true: 'w-full' } },
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A radio group component. */
|
||||||
* A radio group component.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const RadioGroup = forwardRef(function RadioGroup<
|
export const RadioGroup = forwardRef(function RadioGroup<
|
||||||
Schema extends formComponent.TSchema,
|
Schema extends formComponent.TSchema,
|
||||||
|
@ -18,9 +18,7 @@ import invariant from 'tiny-invariant'
|
|||||||
|
|
||||||
import * as eventCallback from '#/hooks/eventCallbackHooks'
|
import * as eventCallback from '#/hooks/eventCallbackHooks'
|
||||||
|
|
||||||
/**
|
/** Props for {@link RadioGroupContextProps} */
|
||||||
* Props for {@link RadioGroupContextProps}
|
|
||||||
*/
|
|
||||||
export interface RadioGroupContextProps {
|
export interface RadioGroupContextProps {
|
||||||
/**
|
/**
|
||||||
* Tells if a Radio element is being pressed
|
* Tells if a Radio element is being pressed
|
||||||
@ -28,13 +26,9 @@ export interface RadioGroupContextProps {
|
|||||||
* It's not the same as selected value, instead it stores the value user is clicking on at the moment
|
* It's not the same as selected value, instead it stores the value user is clicking on at the moment
|
||||||
*/
|
*/
|
||||||
readonly pressedRadio: string | null
|
readonly pressedRadio: string | null
|
||||||
/**
|
/** Sets the pressed Radio element */
|
||||||
* Sets the pressed Radio element
|
|
||||||
*/
|
|
||||||
readonly setPressedRadio: (value: string) => void
|
readonly setPressedRadio: (value: string) => void
|
||||||
/**
|
/** Clears the pressed Radio element */
|
||||||
* Clears the pressed Radio element
|
|
||||||
*/
|
|
||||||
readonly clearPressedRadio: () => void
|
readonly clearPressedRadio: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,16 +62,12 @@ export function RadioGroupProvider(props: React.PropsWithChildren) {
|
|||||||
return <RadioGroupContext.Provider value={value}>{children}</RadioGroupContext.Provider>
|
return <RadioGroupContext.Provider value={value}>{children}</RadioGroupContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for {@link useRadioGroupContext} */
|
||||||
* Props for {@link useRadioGroupContext}
|
|
||||||
*/
|
|
||||||
export interface UseRadioGroupContextProps {
|
export interface UseRadioGroupContextProps {
|
||||||
readonly value: string
|
readonly value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Provides useful information about sibling Radio elements within a RadioGroup */
|
||||||
* Provides useful information about sibling Radio elements within a RadioGroup
|
|
||||||
*/
|
|
||||||
export function useRadioGroupContext(props: UseRadioGroupContextProps) {
|
export function useRadioGroupContext(props: UseRadioGroupContextProps) {
|
||||||
const { value } = props
|
const { value } = props
|
||||||
const context = React.useContext(RadioGroupContext)
|
const context = React.useContext(RadioGroupContext)
|
||||||
|
@ -9,18 +9,14 @@ import * as aria from '#/components/aria'
|
|||||||
|
|
||||||
import * as twv from '#/utilities/tailwindVariants'
|
import * as twv from '#/utilities/tailwindVariants'
|
||||||
|
|
||||||
/**
|
/** The props for {@link Separator} component. */
|
||||||
* The props for {@link Separator} component.
|
|
||||||
*/
|
|
||||||
export interface SeparatorProps
|
export interface SeparatorProps
|
||||||
extends aria.SeparatorProps,
|
extends aria.SeparatorProps,
|
||||||
twv.VariantProps<typeof SEPARATOR_STYLES> {
|
twv.VariantProps<typeof SEPARATOR_STYLES> {
|
||||||
readonly className?: string
|
readonly className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The styles for the {@link Separator} component. */
|
||||||
* The styles for the {@link Separator} component.
|
|
||||||
*/
|
|
||||||
export const SEPARATOR_STYLES = twv.tv({
|
export const SEPARATOR_STYLES = twv.tv({
|
||||||
base: 'rounded-full border-none',
|
base: 'rounded-full border-none',
|
||||||
variants: {
|
variants: {
|
||||||
@ -79,9 +75,7 @@ export const SEPARATOR_STYLES = twv.tv({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A separator component. */
|
||||||
* A separator component.
|
|
||||||
*/
|
|
||||||
export function Separator(props: SeparatorProps) {
|
export function Separator(props: SeparatorProps) {
|
||||||
const { orientation = 'horizontal', variant, className, size, ...rest } = props
|
const { orientation = 'horizontal', variant, className, size, ...rest } = props
|
||||||
|
|
||||||
|
@ -16,9 +16,7 @@ import { tv, type VariantProps } from '#/utilities/tailwindVariants'
|
|||||||
import { Form, type FieldPath, type FieldProps, type FieldStateProps, type TSchema } from '../Form'
|
import { Form, type FieldPath, type FieldProps, type FieldStateProps, type TSchema } from '../Form'
|
||||||
import { TEXT_STYLE } from '../Text'
|
import { TEXT_STYLE } from '../Text'
|
||||||
|
|
||||||
/**
|
/** Props for the {@Switch} component. */
|
||||||
* Props for the {@Switch} component.
|
|
||||||
*/
|
|
||||||
export interface SwitchProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
export interface SwitchProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
||||||
extends FieldStateProps<
|
extends FieldStateProps<
|
||||||
Omit<AriaSwitchProps, 'children' | 'size' | 'value'> & { value: boolean },
|
Omit<AriaSwitchProps, 'children' | 'size' | 'value'> & { value: boolean },
|
||||||
@ -59,9 +57,7 @@ export const SWITCH_STYLES = tv({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A switch allows a user to turn a setting on or off. */
|
||||||
* A switch allows a user to turn a setting on or off.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const Switch = forwardRef(function Switch<
|
export const Switch = forwardRef(function Switch<
|
||||||
Schema extends TSchema,
|
Schema extends TSchema,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file Text component */
|
||||||
* @file Text component
|
|
||||||
*/
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
import * as aria from '#/components/aria'
|
import * as aria from '#/components/aria'
|
||||||
@ -12,9 +10,7 @@ import { forwardRef } from '#/utilities/react'
|
|||||||
import * as textProvider from './TextProvider'
|
import * as textProvider from './TextProvider'
|
||||||
import * as visualTooltip from './useVisualTooltip'
|
import * as visualTooltip from './useVisualTooltip'
|
||||||
|
|
||||||
/**
|
/** Props for the Text component */
|
||||||
* Props for the Text component
|
|
||||||
*/
|
|
||||||
export interface TextProps
|
export interface TextProps
|
||||||
extends Omit<aria.TextProps, 'color'>,
|
extends Omit<aria.TextProps, 'color'>,
|
||||||
twv.VariantProps<typeof TEXT_STYLE> {
|
twv.VariantProps<typeof TEXT_STYLE> {
|
||||||
@ -119,9 +115,7 @@ export const TEXT_STYLE = twv.tv({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Text component that supports truncation and show a tooltip on hover when text is truncated */
|
||||||
* Text component that supports truncation and show a tooltip on hover when text is truncated
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HTMLSpanElement>) {
|
export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HTMLSpanElement>) {
|
||||||
const {
|
const {
|
||||||
@ -216,17 +210,13 @@ export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HT
|
|||||||
Group: React.FC<React.PropsWithChildren>
|
Group: React.FC<React.PropsWithChildren>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Heading props */
|
||||||
* Heading props
|
|
||||||
*/
|
|
||||||
export interface HeadingProps extends Omit<TextProps, 'elementType'> {
|
export interface HeadingProps extends Omit<TextProps, 'elementType'> {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||||
readonly level?: '1' | '2' | '3' | '4' | '5' | '6' | 1 | 2 | 3 | 4 | 5 | 6
|
readonly level?: '1' | '2' | '3' | '4' | '5' | '6' | 1 | 2 | 3 | 4 | 5 | 6
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Heading component */
|
||||||
* Heading component
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
const Heading = forwardRef(function Heading(
|
const Heading = forwardRef(function Heading(
|
||||||
props: HeadingProps,
|
props: HeadingProps,
|
||||||
@ -237,9 +227,7 @@ const Heading = forwardRef(function Heading(
|
|||||||
})
|
})
|
||||||
Text.Heading = Heading
|
Text.Heading = Heading
|
||||||
|
|
||||||
/**
|
/** Text group component. It's used to visually group text elements together */
|
||||||
* Text group component. It's used to visually group text elements together
|
|
||||||
*/
|
|
||||||
Text.Group = function TextGroup(props: React.PropsWithChildren) {
|
Text.Group = function TextGroup(props: React.PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<textProvider.TextProvider value={{ isInsideTextComponent: true }}>
|
<textProvider.TextProvider value={{ isInsideTextComponent: true }}>
|
||||||
|
@ -5,13 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
/**
|
/** Context for the Text component. */
|
||||||
* Context for the Text component.
|
|
||||||
*/
|
|
||||||
export interface TextContextType {
|
export interface TextContextType {
|
||||||
/**
|
/** Flag indicating whether the component is inside a Text component. */
|
||||||
* Flag indicating whether the component is inside a Text component.
|
|
||||||
*/
|
|
||||||
readonly isInsideTextComponent: boolean
|
readonly isInsideTextComponent: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,9 +15,7 @@ const TextContext = React.createContext<TextContextType>({
|
|||||||
isInsideTextComponent: false,
|
isInsideTextComponent: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Hook to get the Text context. */
|
||||||
* Hook to get the Text context.
|
|
||||||
*/
|
|
||||||
export function useTextContext(): TextContextType {
|
export function useTextContext(): TextContextType {
|
||||||
return React.useContext(TextContext)
|
return React.useContext(TextContext)
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,7 @@ import * as aria from '#/components/aria'
|
|||||||
import * as ariaComponents from '#/components/AriaComponents'
|
import * as ariaComponents from '#/components/AriaComponents'
|
||||||
import Portal from '#/components/Portal'
|
import Portal from '#/components/Portal'
|
||||||
|
|
||||||
/**
|
/** Props for {@link useVisualTooltip}. */
|
||||||
* Props for {@link useVisualTooltip}.
|
|
||||||
*/
|
|
||||||
export interface VisualTooltipProps
|
export interface VisualTooltipProps
|
||||||
extends Pick<ariaComponents.TooltipProps, 'maxWidth' | 'rounded' | 'size' | 'variant'> {
|
extends Pick<ariaComponents.TooltipProps, 'maxWidth' | 'rounded' | 'size' | 'variant'> {
|
||||||
readonly children: React.ReactNode
|
readonly children: React.ReactNode
|
||||||
@ -41,9 +39,7 @@ export interface VisualTooltipReturn {
|
|||||||
readonly tooltip: JSX.Element | null
|
readonly tooltip: JSX.Element | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The display strategy for the tooltip. */
|
||||||
* The display strategy for the tooltip.
|
|
||||||
*/
|
|
||||||
type DisplayStrategy = 'always' | 'whenOverflowing'
|
type DisplayStrategy = 'always' | 'whenOverflowing'
|
||||||
|
|
||||||
const DEFAULT_OFFSET = 6
|
const DEFAULT_OFFSET = 6
|
||||||
|
@ -8,16 +8,12 @@ import * as React from 'react'
|
|||||||
import { forwardRef } from '#/utilities/react'
|
import { forwardRef } from '#/utilities/react'
|
||||||
import * as twv from '#/utilities/tailwindVariants'
|
import * as twv from '#/utilities/tailwindVariants'
|
||||||
|
|
||||||
/**
|
/** Props for the {@link VisuallyHidden} component. */
|
||||||
* Props for the {@link VisuallyHidden} component.
|
|
||||||
*/
|
|
||||||
export type VisuallyHiddenProps = React.HTMLProps<HTMLElement>
|
export type VisuallyHiddenProps = React.HTMLProps<HTMLElement>
|
||||||
|
|
||||||
export const VISUALLY_HIDDEN_STYLES = twv.tv({ base: 'sr-only' })
|
export const VISUALLY_HIDDEN_STYLES = twv.tv({ base: 'sr-only' })
|
||||||
|
|
||||||
/**
|
/** A component visually hides its children from the screen, but keeps them accessible to screen readers. */
|
||||||
* A component visually hides its children from the screen, but keeps them accessible to screen readers.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const VisuallyHidden = forwardRef(function VisuallyHidden(
|
export const VisuallyHidden = forwardRef(function VisuallyHidden(
|
||||||
props: VisuallyHiddenProps,
|
props: VisuallyHiddenProps,
|
||||||
|
@ -4,13 +4,9 @@
|
|||||||
* Common types for ARIA components.
|
* Common types for ARIA components.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/** Props for adding a test id to a component */
|
||||||
* Props for adding a test id to a component
|
|
||||||
*/
|
|
||||||
export interface TestIdProps {
|
export interface TestIdProps {
|
||||||
/**
|
/** @deprecated Use `testid` instead */
|
||||||
* @deprecated Use `testid` instead
|
|
||||||
*/
|
|
||||||
readonly 'data-testid'?: string | undefined
|
readonly 'data-testid'?: string | undefined
|
||||||
readonly testId?: string | undefined
|
readonly testId?: string | undefined
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,7 @@ import { tv } from '#/utilities/tailwindVariants'
|
|||||||
import type { ReactNode } from 'react'
|
import type { ReactNode } from 'react'
|
||||||
import { TEXT_STYLE } from '../AriaComponents'
|
import { TEXT_STYLE } from '../AriaComponents'
|
||||||
|
|
||||||
/**
|
/** Props for the {@link Badge} component. */
|
||||||
* Props for the {@link Badge} component.
|
|
||||||
*/
|
|
||||||
export interface BadgeProps extends VariantProps<typeof BADGE_STYLES> {
|
export interface BadgeProps extends VariantProps<typeof BADGE_STYLES> {
|
||||||
readonly children?: ReactNode
|
readonly children?: ReactNode
|
||||||
readonly className?: string
|
readonly className?: string
|
||||||
@ -61,9 +59,7 @@ export const BADGE_STYLES = tv({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** Badges are used to highlight an item's status for quick recognition. */
|
||||||
* Badges are used to highlight an item's status for quick recognition.
|
|
||||||
*/
|
|
||||||
export function Badge(props: BadgeProps) {
|
export function Badge(props: BadgeProps) {
|
||||||
const { children, color, rounded, className, variant } = props
|
const { children, color, rounded, className, variant } = props
|
||||||
|
|
||||||
|
@ -48,9 +48,7 @@ import * as backend from '#/services/Backend'
|
|||||||
import LocalStorage, { type LocalStorageData } from '#/utilities/LocalStorage'
|
import LocalStorage, { type LocalStorageData } from '#/utilities/LocalStorage'
|
||||||
import { unsafeEntries } from 'enso-common/src/utilities/data/object'
|
import { unsafeEntries } from 'enso-common/src/utilities/data/object'
|
||||||
|
|
||||||
/**
|
/** A component that provides a UI for toggling paywall features. */
|
||||||
* A component that provides a UI for toggling paywall features.
|
|
||||||
*/
|
|
||||||
export function EnsoDevtools() {
|
export function EnsoDevtools() {
|
||||||
const { getText } = textProvider.useText()
|
const { getText } = textProvider.useText()
|
||||||
const { authQueryKey, session } = authProvider.useAuth()
|
const { authQueryKey, session } = authProvider.useAuth()
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
import type { PaywallFeatureName } from '#/hooks/billing'
|
import type { PaywallFeatureName } from '#/hooks/billing'
|
||||||
import * as zustand from 'zustand'
|
import * as zustand from 'zustand'
|
||||||
|
|
||||||
/**
|
/** Configuration for a paywall feature. */
|
||||||
* Configuration for a paywall feature.
|
|
||||||
*/
|
|
||||||
export interface PaywallDevtoolsFeatureConfiguration {
|
export interface PaywallDevtoolsFeatureConfiguration {
|
||||||
readonly isForceEnabled: boolean | null
|
readonly isForceEnabled: boolean | null
|
||||||
}
|
}
|
||||||
@ -62,9 +60,7 @@ export function useSetEnableVersionChecker() {
|
|||||||
return zustand.useStore(ensoDevtoolsStore, (state) => state.setEnableVersionChecker)
|
return zustand.useStore(ensoDevtoolsStore, (state) => state.setEnableVersionChecker)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A hook that provides access to the paywall devtools. */
|
||||||
* A hook that provides access to the paywall devtools.
|
|
||||||
*/
|
|
||||||
export function usePaywallDevtools() {
|
export function usePaywallDevtools() {
|
||||||
return zustand.useStore(ensoDevtoolsStore, (state) => ({
|
return zustand.useStore(ensoDevtoolsStore, (state) => ({
|
||||||
features: state.paywallFeatures,
|
features: state.paywallFeatures,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file Show the React Query Devtools. */
|
||||||
* @file Show the React Query Devtools.
|
|
||||||
*/
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
import * as reactQuery from '@tanstack/react-query'
|
import * as reactQuery from '@tanstack/react-query'
|
||||||
|
@ -14,14 +14,10 @@ import * as offlineHooks from '#/hooks/offlineHooks'
|
|||||||
|
|
||||||
import * as textProvider from '#/providers/TextProvider'
|
import * as textProvider from '#/providers/TextProvider'
|
||||||
|
|
||||||
/**
|
/** Props for {@link OfflineNotificationManager} */
|
||||||
* Props for {@link OfflineNotificationManager}
|
|
||||||
*/
|
|
||||||
export type OfflineNotificationManagerProps = Readonly<React.PropsWithChildren>
|
export type OfflineNotificationManagerProps = Readonly<React.PropsWithChildren>
|
||||||
|
|
||||||
/**
|
/** Context props for {@link OfflineNotificationManager} */
|
||||||
* Context props for {@link OfflineNotificationManager}
|
|
||||||
*/
|
|
||||||
interface OfflineNotificationManagerContextProps {
|
interface OfflineNotificationManagerContextProps {
|
||||||
readonly isNested: boolean
|
readonly isNested: boolean
|
||||||
readonly toastId?: string
|
readonly toastId?: string
|
||||||
@ -30,9 +26,7 @@ interface OfflineNotificationManagerContextProps {
|
|||||||
const OfflineNotificationManagerContext =
|
const OfflineNotificationManagerContext =
|
||||||
React.createContext<OfflineNotificationManagerContextProps>({ isNested: false })
|
React.createContext<OfflineNotificationManagerContextProps>({ isNested: false })
|
||||||
|
|
||||||
/**
|
/** Offline Notification Manager component. */
|
||||||
* Offline Notification Manager component.
|
|
||||||
*/
|
|
||||||
export function OfflineNotificationManager(props: OfflineNotificationManagerProps) {
|
export function OfflineNotificationManager(props: OfflineNotificationManagerProps) {
|
||||||
const { children } = props
|
const { children } = props
|
||||||
const toastId = 'offline'
|
const toastId = 'offline'
|
||||||
|
@ -17,17 +17,13 @@ import ContextMenuEntryBase from '#/components/ContextMenuEntry'
|
|||||||
|
|
||||||
import * as paywallDialog from './PaywallDialog'
|
import * as paywallDialog from './PaywallDialog'
|
||||||
|
|
||||||
/**
|
/** Props for {@link ContextMenuEntry}. */
|
||||||
* Props for {@link ContextMenuEntry}.
|
|
||||||
*/
|
|
||||||
export interface ContextMenuEntryProps
|
export interface ContextMenuEntryProps
|
||||||
extends Omit<contextMenuEntry.ContextMenuEntryProps, 'doAction' | 'isDisabled'> {
|
extends Omit<contextMenuEntry.ContextMenuEntryProps, 'doAction' | 'isDisabled'> {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A context menu entry that opens a paywall dialog. */
|
||||||
* A context menu entry that opens a paywall dialog.
|
|
||||||
*/
|
|
||||||
export function ContextMenuEntry(props: ContextMenuEntryProps) {
|
export function ContextMenuEntry(props: ContextMenuEntryProps) {
|
||||||
const { feature, ...rest } = props
|
const { feature, ...rest } = props
|
||||||
const { setModal } = modalProvider.useSetModal()
|
const { setModal } = modalProvider.useSetModal()
|
||||||
|
@ -16,9 +16,7 @@ import * as ariaComponents from '#/components/AriaComponents'
|
|||||||
import * as paywall from '#/components/Paywall'
|
import * as paywall from '#/components/Paywall'
|
||||||
import SvgMask from '#/components/SvgMask'
|
import SvgMask from '#/components/SvgMask'
|
||||||
|
|
||||||
/**
|
/** Props for {@link PaywallAlert}. */
|
||||||
* Props for {@link PaywallAlert}.
|
|
||||||
*/
|
|
||||||
export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'children'> {
|
export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'children'> {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
readonly label: string
|
readonly label: string
|
||||||
@ -26,9 +24,7 @@ export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'chil
|
|||||||
readonly upgradeButtonProps?: Omit<paywall.UpgradeButtonProps, 'feature'>
|
readonly upgradeButtonProps?: Omit<paywall.UpgradeButtonProps, 'feature'>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A paywall alert. */
|
||||||
* A paywall alert.
|
|
||||||
*/
|
|
||||||
export function PaywallAlert(props: PaywallAlertProps) {
|
export function PaywallAlert(props: PaywallAlertProps) {
|
||||||
const {
|
const {
|
||||||
label,
|
label,
|
||||||
|
@ -15,16 +15,12 @@ import * as ariaComponents from '#/components/AriaComponents'
|
|||||||
import * as components from './components'
|
import * as components from './components'
|
||||||
import * as upgradeButton from './UpgradeButton'
|
import * as upgradeButton from './UpgradeButton'
|
||||||
|
|
||||||
/**
|
/** Props for a {@link PaywallDialog}. */
|
||||||
* Props for a {@link PaywallDialog}.
|
|
||||||
*/
|
|
||||||
export interface PaywallDialogProps extends ariaComponents.DialogProps {
|
export interface PaywallDialogProps extends ariaComponents.DialogProps {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A dialog that prompts the user to upgrade to a paid plan. */
|
||||||
* A dialog that prompts the user to upgrade to a paid plan.
|
|
||||||
*/
|
|
||||||
export function PaywallDialog(props: PaywallDialogProps) {
|
export function PaywallDialog(props: PaywallDialogProps) {
|
||||||
const { feature, type = 'modal', title, ...dialogProps } = props
|
const { feature, type = 'modal', title, ...dialogProps } = props
|
||||||
|
|
||||||
|
@ -11,18 +11,14 @@ import * as ariaComponents from '#/components/AriaComponents'
|
|||||||
import * as components from './components'
|
import * as components from './components'
|
||||||
import * as paywallDialog from './PaywallDialog'
|
import * as paywallDialog from './PaywallDialog'
|
||||||
|
|
||||||
/**
|
/** Props for a {@link PaywallDialogButton}. */
|
||||||
* Props for a {@link PaywallDialogButton}.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export type PaywallDialogButtonProps = components.PaywallButtonProps & {
|
export type PaywallDialogButtonProps = components.PaywallButtonProps & {
|
||||||
readonly dialogProps?: paywallDialog.PaywallDialogProps
|
readonly dialogProps?: paywallDialog.PaywallDialogProps
|
||||||
readonly dialogTriggerProps?: ariaComponents.DialogTriggerProps
|
readonly dialogTriggerProps?: ariaComponents.DialogTriggerProps
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A button that opens a paywall dialog when clicked */
|
||||||
* A button that opens a paywall dialog when clicked
|
|
||||||
*/
|
|
||||||
export function PaywallDialogButton(props: PaywallDialogButtonProps) {
|
export function PaywallDialogButton(props: PaywallDialogButtonProps) {
|
||||||
const { feature, dialogProps, dialogTriggerProps, ...buttonProps } = props
|
const { feature, dialogProps, dialogTriggerProps, ...buttonProps } = props
|
||||||
|
|
||||||
|
@ -17,17 +17,13 @@ import * as ariaComponents from '#/components/AriaComponents'
|
|||||||
import * as components from './components'
|
import * as components from './components'
|
||||||
import * as upgradeButton from './UpgradeButton'
|
import * as upgradeButton from './UpgradeButton'
|
||||||
|
|
||||||
/**
|
/** Props for a {@link PaywallScreen}. */
|
||||||
* Props for a {@link PaywallScreen}.
|
|
||||||
*/
|
|
||||||
export interface PaywallScreenProps {
|
export interface PaywallScreenProps {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
readonly className?: string
|
readonly className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A screen that shows a paywall. */
|
||||||
* A screen that shows a paywall.
|
|
||||||
*/
|
|
||||||
export function PaywallScreen(props: PaywallScreenProps) {
|
export function PaywallScreen(props: PaywallScreenProps) {
|
||||||
const { feature, className } = props
|
const { feature, className } = props
|
||||||
const { getText } = textProvider.useText()
|
const { getText } = textProvider.useText()
|
||||||
|
@ -13,18 +13,14 @@ import * as textProvider from '#/providers/TextProvider'
|
|||||||
|
|
||||||
import * as ariaComponents from '#/components/AriaComponents'
|
import * as ariaComponents from '#/components/AriaComponents'
|
||||||
|
|
||||||
/**
|
/** Props for an {@link UpgradeButton}. */
|
||||||
* Props for an {@link UpgradeButton}.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export type UpgradeButtonProps = Omit<ariaComponents.ButtonProps, 'variant'> & {
|
export type UpgradeButtonProps = Omit<ariaComponents.ButtonProps, 'variant'> & {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
readonly variant?: ariaComponents.ButtonProps['variant']
|
readonly variant?: ariaComponents.ButtonProps['variant']
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A button that links to the upgrade page. */
|
||||||
* A button that links to the upgrade page.
|
|
||||||
*/
|
|
||||||
export function UpgradeButton(props: UpgradeButtonProps) {
|
export function UpgradeButton(props: UpgradeButtonProps) {
|
||||||
const {
|
const {
|
||||||
feature,
|
feature,
|
||||||
|
@ -16,17 +16,13 @@ import * as textProvider from '#/providers/TextProvider'
|
|||||||
import * as ariaComponents from '#/components/AriaComponents'
|
import * as ariaComponents from '#/components/AriaComponents'
|
||||||
import SvgMask from '#/components/SvgMask'
|
import SvgMask from '#/components/SvgMask'
|
||||||
|
|
||||||
/**
|
/** Props for a {@link PaywallBulletPoints}. */
|
||||||
* Props for a {@link PaywallBulletPoints}.
|
|
||||||
*/
|
|
||||||
export interface PaywallBulletPointsProps {
|
export interface PaywallBulletPointsProps {
|
||||||
readonly bulletPointsTextId: text.TextId
|
readonly bulletPointsTextId: text.TextId
|
||||||
readonly className?: string
|
readonly className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A component that renders a list of bullet points for a paywall. */
|
||||||
* A component that renders a list of bullet points for a paywall.
|
|
||||||
*/
|
|
||||||
export function PaywallBulletPoints(props: PaywallBulletPointsProps) {
|
export function PaywallBulletPoints(props: PaywallBulletPointsProps) {
|
||||||
const { bulletPointsTextId, className } = props
|
const { bulletPointsTextId, className } = props
|
||||||
|
|
||||||
|
@ -13,9 +13,7 @@ import * as textProvider from '#/providers/TextProvider'
|
|||||||
|
|
||||||
import * as ariaComponents from '#/components/AriaComponents'
|
import * as ariaComponents from '#/components/AriaComponents'
|
||||||
|
|
||||||
/**
|
/** Props for {@link PaywallButton}. */
|
||||||
* Props for {@link PaywallButton}.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export type PaywallButtonProps = ariaComponents.ButtonProps & {
|
export type PaywallButtonProps = ariaComponents.ButtonProps & {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
@ -23,9 +21,7 @@ export type PaywallButtonProps = ariaComponents.ButtonProps & {
|
|||||||
readonly showIcon?: boolean
|
readonly showIcon?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A styled button that shows that a feature is behind a paywall */
|
||||||
* A styled button that shows that a feature is behind a paywall
|
|
||||||
*/
|
|
||||||
export function PaywallButton(props: PaywallButtonProps) {
|
export function PaywallButton(props: PaywallButtonProps) {
|
||||||
const { feature, iconOnly = false, showIcon = true, children, ...buttonProps } = props
|
const { feature, iconOnly = false, showIcon = true, children, ...buttonProps } = props
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/**
|
/** @file A lock icon with a label indicating the paywall level required to access a feature. */
|
||||||
* @file A lock icon with a label indicating the paywall level required to access a feature.
|
|
||||||
*/
|
|
||||||
import * as tw from 'tailwind-merge'
|
import * as tw from 'tailwind-merge'
|
||||||
|
|
||||||
import LockIcon from '#/assets/lock.svg'
|
import LockIcon from '#/assets/lock.svg'
|
||||||
@ -12,17 +10,13 @@ import * as textProvider from '#/providers/TextProvider'
|
|||||||
import * as ariaComponents from '#/components/AriaComponents'
|
import * as ariaComponents from '#/components/AriaComponents'
|
||||||
import SvgMask from '#/components/SvgMask'
|
import SvgMask from '#/components/SvgMask'
|
||||||
|
|
||||||
/**
|
/** Props for a {@link PaywallLock}. */
|
||||||
* Props for a {@link PaywallLock}.
|
|
||||||
*/
|
|
||||||
export interface PaywallLockProps {
|
export interface PaywallLockProps {
|
||||||
readonly feature: billingHooks.PaywallFeatureName
|
readonly feature: billingHooks.PaywallFeatureName
|
||||||
readonly className?: string
|
readonly className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A lock icon with a label indicating the paywall level required to access a feature. */
|
||||||
* A lock icon with a label indicating the paywall level required to access a feature.
|
|
||||||
*/
|
|
||||||
export function PaywallLock(props: PaywallLockProps) {
|
export function PaywallLock(props: PaywallLockProps) {
|
||||||
const { feature, className } = props
|
const { feature, className } = props
|
||||||
const { getText } = textProvider.useText()
|
const { getText } = textProvider.useText()
|
||||||
|
@ -8,9 +8,7 @@ import invariant from 'tiny-invariant'
|
|||||||
|
|
||||||
const PortalContext = React.createContext<Element | null>(null)
|
const PortalContext = React.createContext<Element | null>(null)
|
||||||
|
|
||||||
/**
|
/** Allows to access the root element for the Portal component */
|
||||||
* Allows to access the root element for the Portal component
|
|
||||||
*/
|
|
||||||
export function usePortalContext() {
|
export function usePortalContext() {
|
||||||
const root = React.useContext(PortalContext)
|
const root = React.useContext(PortalContext)
|
||||||
|
|
||||||
@ -29,8 +27,6 @@ export function useStrictPortalContext() {
|
|||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Specifies the root element for the Portal component */
|
||||||
* Specifies the root element for the Portal component
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
export const PortalProvider = PortalContext.Provider
|
export const PortalProvider = PortalContext.Provider
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import type * as React from 'react'
|
import type * as React from 'react'
|
||||||
|
|
||||||
/**
|
/** The props for the Portal component */
|
||||||
* The props for the Portal component
|
|
||||||
*/
|
|
||||||
export interface PortalProps {
|
export interface PortalProps {
|
||||||
/**
|
/**
|
||||||
* Ref, where `<Portal />` should render its children
|
* Ref, where `<Portal />` should render its children
|
||||||
@ -19,9 +17,7 @@ export interface PortalProps {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
readonly isDisabled?: boolean
|
readonly isDisabled?: boolean
|
||||||
/**
|
/** Callback, will be called after portal's children mounted */
|
||||||
* Callback, will be called after portal's children mounted
|
|
||||||
*/
|
|
||||||
readonly onMount?: () => void
|
readonly onMount?: () => void
|
||||||
readonly children?: React.ReactNode
|
readonly children?: React.ReactNode
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,7 @@ import type * as stepperState from './useStepperState'
|
|||||||
/** A prop with the given type, or a function to produce a value of the given type. */
|
/** A prop with the given type, or a function to produce a value of the given type. */
|
||||||
type StepProp<T> = T | ((props: RenderStepProps) => T)
|
type StepProp<T> = T | ((props: RenderStepProps) => T)
|
||||||
|
|
||||||
/**
|
/** Props for {@link Step} component. */
|
||||||
* Props for {@link Step} component.
|
|
||||||
*/
|
|
||||||
export interface StepProps extends RenderStepProps {
|
export interface StepProps extends RenderStepProps {
|
||||||
readonly className?: StepProp<string | null | undefined>
|
readonly className?: StepProp<string | null | undefined>
|
||||||
readonly icon?: StepProp<React.ReactElement | string | null | undefined>
|
readonly icon?: StepProp<React.ReactElement | string | null | undefined>
|
||||||
@ -52,9 +50,7 @@ const STEP_STYLES = tv({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/** A step component is used to represent a single step in a stepper component. */
|
||||||
* A step component is used to represent a single step in a stepper component.
|
|
||||||
*/
|
|
||||||
export function Step(props: StepProps) {
|
export function Step(props: StepProps) {
|
||||||
const {
|
const {
|
||||||
index,
|
index,
|
||||||
|
@ -6,18 +6,14 @@ import type { ReactElement, ReactNode } from 'react'
|
|||||||
import { useStepperContext } from './StepperProvider'
|
import { useStepperContext } from './StepperProvider'
|
||||||
import type { RenderChildrenProps } from './types'
|
import type { RenderChildrenProps } from './types'
|
||||||
|
|
||||||
/**
|
/** Props for {@link StepContent} component. */
|
||||||
* Props for {@link StepContent} component.
|
|
||||||
*/
|
|
||||||
export interface StepContentProps {
|
export interface StepContentProps {
|
||||||
readonly index: number
|
readonly index: number
|
||||||
readonly children: ReactNode | ((props: RenderChildrenProps) => ReactNode)
|
readonly children: ReactNode | ((props: RenderChildrenProps) => ReactNode)
|
||||||
readonly forceRender?: boolean
|
readonly forceRender?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Step content component. Renders the step content if the step is current or if `forceRender` is true. */
|
||||||
* Step content component. Renders the step content if the step is current or if `forceRender` is true.
|
|
||||||
*/
|
|
||||||
export function StepContent(props: StepContentProps): ReactElement | null {
|
export function StepContent(props: StepContentProps): ReactElement | null {
|
||||||
const { index, children, forceRender = false } = props
|
const { index, children, forceRender = false } = props
|
||||||
const { currentStep, goToStep, nextStep, previousStep, totalSteps } = useStepperContext()
|
const { currentStep, goToStep, nextStep, previousStep, totalSteps } = useStepperContext()
|
||||||
|
@ -19,9 +19,7 @@ import * as stepperProvider from './StepperProvider'
|
|||||||
import type { BaseRenderProps, RenderChildrenProps, RenderStepProps } from './types'
|
import type { BaseRenderProps, RenderChildrenProps, RenderStepProps } from './types'
|
||||||
import * as stepperState from './useStepperState'
|
import * as stepperState from './useStepperState'
|
||||||
|
|
||||||
/**
|
/** Props for {@link Stepper} component. */
|
||||||
* Props for {@link Stepper} component.
|
|
||||||
*/
|
|
||||||
export interface StepperProps {
|
export interface StepperProps {
|
||||||
readonly state: stepperState.StepperState
|
readonly state: stepperState.StepperState
|
||||||
readonly children: React.ReactNode | ((props: RenderChildrenProps) => React.ReactNode)
|
readonly children: React.ReactNode | ((props: RenderChildrenProps) => React.ReactNode)
|
||||||
@ -48,9 +46,7 @@ const STEPPER_STYLES = tv({
|
|||||||
|
|
||||||
const ANIMATION_OFFSET = 15
|
const ANIMATION_OFFSET = 15
|
||||||
|
|
||||||
/**
|
/** A stepper component is used to indicate progress through a multi-step process. */
|
||||||
* A stepper component is used to indicate progress through a multi-step process.
|
|
||||||
*/
|
|
||||||
export function Stepper(props: StepperProps) {
|
export function Stepper(props: StepperProps) {
|
||||||
const { renderStep, children, state } = props
|
const { renderStep, children, state } = props
|
||||||
|
|
||||||
@ -77,9 +73,7 @@ export function Stepper(props: StepperProps) {
|
|||||||
|
|
||||||
const style = typeof props.style === 'function' ? props.style(baseRenderProps) : props.style
|
const style = typeof props.style === 'function' ? props.style(baseRenderProps) : props.style
|
||||||
|
|
||||||
/**
|
/** Render children of the stepper component. */
|
||||||
* Render children of the stepper component.
|
|
||||||
*/
|
|
||||||
const renderChildren = () => {
|
const renderChildren = () => {
|
||||||
const renderProps = {
|
const renderProps = {
|
||||||
currentStep,
|
currentStep,
|
||||||
|
@ -9,9 +9,7 @@ import invariant from 'tiny-invariant'
|
|||||||
|
|
||||||
import type { StepperState } from './useStepperState'
|
import type { StepperState } from './useStepperState'
|
||||||
|
|
||||||
/**
|
/** StepperProvider props */
|
||||||
* StepperProvider props
|
|
||||||
*/
|
|
||||||
export interface StepperContextType {
|
export interface StepperContextType {
|
||||||
readonly currentStep: number
|
readonly currentStep: number
|
||||||
readonly goToStep: (step: number) => void
|
readonly goToStep: (step: number) => void
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
* Types for the stepper component.
|
* Types for the stepper component.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/** Render props for the stepper component. */
|
||||||
* Render props for the stepper component.
|
|
||||||
*/
|
|
||||||
export interface BaseRenderProps {
|
export interface BaseRenderProps {
|
||||||
readonly goToStep: (step: number) => void
|
readonly goToStep: (step: number) => void
|
||||||
readonly nextStep: () => void
|
readonly nextStep: () => void
|
||||||
@ -15,21 +13,15 @@ export interface BaseRenderProps {
|
|||||||
readonly totalSteps: number
|
readonly totalSteps: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Render props for rendering children of the stepper component. */
|
||||||
* Render props for rendering children of the stepper component.
|
|
||||||
*/
|
|
||||||
export interface RenderChildrenProps extends BaseRenderProps {
|
export interface RenderChildrenProps extends BaseRenderProps {
|
||||||
readonly isFirst: boolean
|
readonly isFirst: boolean
|
||||||
readonly isLast: boolean
|
readonly isLast: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Render props for lazy rendering of steps. */
|
||||||
* Render props for lazy rendering of steps.
|
|
||||||
*/
|
|
||||||
export interface RenderStepProps extends BaseRenderProps {
|
export interface RenderStepProps extends BaseRenderProps {
|
||||||
/**
|
/** The index of the step, starting from 0. */
|
||||||
* The index of the step, starting from 0.
|
|
||||||
*/
|
|
||||||
readonly index: number
|
readonly index: number
|
||||||
readonly isCurrent: boolean
|
readonly isCurrent: boolean
|
||||||
readonly isCompleted: boolean
|
readonly isCompleted: boolean
|
||||||
|
@ -9,30 +9,20 @@ import invariant from 'tiny-invariant'
|
|||||||
|
|
||||||
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
||||||
|
|
||||||
/**
|
/** Direction of the stepper */
|
||||||
* Direction of the stepper
|
|
||||||
*/
|
|
||||||
type Direction = 'back-none' | 'back' | 'forward-none' | 'forward' | 'initial'
|
type Direction = 'back-none' | 'back' | 'forward-none' | 'forward' | 'initial'
|
||||||
|
|
||||||
/**
|
/** Props for {@link useStepperState} */
|
||||||
* Props for {@link useStepperState}
|
|
||||||
*/
|
|
||||||
export interface StepperStateProps {
|
export interface StepperStateProps {
|
||||||
/**
|
/** The default step to start on (0-indexed) */
|
||||||
* The default step to start on (0-indexed)
|
|
||||||
*/
|
|
||||||
readonly defaultStep?: number
|
readonly defaultStep?: number
|
||||||
/**
|
/** The number of steps in the stepper (amount of steps is 1-indexed) */
|
||||||
* The number of steps in the stepper (amount of steps is 1-indexed)
|
|
||||||
*/
|
|
||||||
readonly steps: number
|
readonly steps: number
|
||||||
readonly onStepChange?: (step: number, direction: 'back' | 'forward') => void
|
readonly onStepChange?: (step: number, direction: 'back' | 'forward') => void
|
||||||
readonly onCompleted?: () => void
|
readonly onCompleted?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** State for a stepper component */
|
||||||
* State for a stepper component
|
|
||||||
*/
|
|
||||||
export interface StepperState {
|
export interface StepperState {
|
||||||
readonly currentStep: number
|
readonly currentStep: number
|
||||||
readonly onStepChange: (step: number) => void
|
readonly onStepChange: (step: number) => void
|
||||||
@ -43,9 +33,7 @@ export interface StepperState {
|
|||||||
readonly percentComplete: number
|
readonly percentComplete: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Result of {@link useStepperState} */
|
||||||
* Result of {@link useStepperState}
|
|
||||||
*/
|
|
||||||
export interface UseStepperStateResult {
|
export interface UseStepperStateResult {
|
||||||
readonly stepperState: StepperState
|
readonly stepperState: StepperState
|
||||||
readonly direction: Direction
|
readonly direction: Direction
|
||||||
|
@ -18,9 +18,7 @@ import * as result from '#/components/Result'
|
|||||||
|
|
||||||
import * as loader from './Loader'
|
import * as loader from './Loader'
|
||||||
|
|
||||||
/**
|
/** Props for {@link Suspense} component. */
|
||||||
* Props for {@link Suspense} component.
|
|
||||||
*/
|
|
||||||
export interface SuspenseProps extends React.SuspenseProps {
|
export interface SuspenseProps extends React.SuspenseProps {
|
||||||
readonly loaderProps?: loader.LoaderProps
|
readonly loaderProps?: loader.LoaderProps
|
||||||
readonly offlineFallback?: React.ReactNode
|
readonly offlineFallback?: React.ReactNode
|
||||||
|
@ -7,9 +7,7 @@ import { useInteractOutside } from '#/components/aria'
|
|||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import { useEventCallback } from './eventCallbackHooks'
|
import { useEventCallback } from './eventCallbackHooks'
|
||||||
|
|
||||||
/**
|
/** Props for the {@link useAutoFocus} hook. */
|
||||||
* Props for the {@link useAutoFocus} hook.
|
|
||||||
*/
|
|
||||||
export interface UseAutoFocusProps {
|
export interface UseAutoFocusProps {
|
||||||
readonly ref: React.RefObject<HTMLElement>
|
readonly ref: React.RefObject<HTMLElement>
|
||||||
readonly disabled?: boolean | undefined
|
readonly disabled?: boolean | undefined
|
||||||
|
@ -8,9 +8,7 @@ import type * as text from 'enso-common/src/text'
|
|||||||
|
|
||||||
import * as backend from '#/services/Backend'
|
import * as backend from '#/services/Backend'
|
||||||
|
|
||||||
/**
|
/** Registered paywall features. */
|
||||||
* Registered paywall features.
|
|
||||||
*/
|
|
||||||
export const PAYWALL_FEATURES = {
|
export const PAYWALL_FEATURES = {
|
||||||
userGroups: 'userGroups',
|
userGroups: 'userGroups',
|
||||||
userGroupsFull: 'userGroupsFull',
|
userGroupsFull: 'userGroupsFull',
|
||||||
@ -20,14 +18,10 @@ export const PAYWALL_FEATURES = {
|
|||||||
shareFull: 'shareFull',
|
shareFull: 'shareFull',
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/**
|
/** Paywall features. */
|
||||||
* Paywall features.
|
|
||||||
*/
|
|
||||||
export type PaywallFeatureName = keyof typeof PAYWALL_FEATURES
|
export type PaywallFeatureName = keyof typeof PAYWALL_FEATURES
|
||||||
|
|
||||||
/**
|
/** Paywall level names */
|
||||||
* Paywall level names
|
|
||||||
*/
|
|
||||||
export type PaywallLevelName = backend.Plan | 'free'
|
export type PaywallLevelName = backend.Plan | 'free'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,9 +36,7 @@ export type PaywallLevelValue =
|
|||||||
| (2 & { readonly name: PaywallLevelName; readonly label: text.TextId })
|
| (2 & { readonly name: PaywallLevelName; readonly label: text.TextId })
|
||||||
| (3 & { readonly name: PaywallLevelName; readonly label: text.TextId })
|
| (3 & { readonly name: PaywallLevelName; readonly label: text.TextId })
|
||||||
|
|
||||||
/**
|
/** Paywall levels configuration. */
|
||||||
* Paywall levels configuration.
|
|
||||||
*/
|
|
||||||
export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = {
|
export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = {
|
||||||
free: Object.assign(0, { name: 'free', label: 'freePlanName' } as const),
|
free: Object.assign(0, { name: 'free', label: 'freePlanName' } as const),
|
||||||
[backend.Plan.solo]: Object.assign(1, {
|
[backend.Plan.solo]: Object.assign(1, {
|
||||||
@ -61,14 +53,10 @@ export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = {
|
|||||||
} as const),
|
} as const),
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Possible paywall unlock states for a user account. */
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type PaywallLevel = (typeof PAYWALL_LEVELS)[keyof typeof PAYWALL_LEVELS]
|
export type PaywallLevel = (typeof PAYWALL_LEVELS)[keyof typeof PAYWALL_LEVELS]
|
||||||
|
|
||||||
/**
|
/** Paywall feature labels. */
|
||||||
* Paywall feature labels.
|
|
||||||
*/
|
|
||||||
const PAYWALL_FEATURES_LABELS: Record<PaywallFeatureName, text.TextId> = {
|
const PAYWALL_FEATURES_LABELS: Record<PaywallFeatureName, text.TextId> = {
|
||||||
userGroups: 'userGroupsFeatureLabel',
|
userGroups: 'userGroupsFeatureLabel',
|
||||||
userGroupsFull: 'userGroupsFullFeatureLabel',
|
userGroupsFull: 'userGroupsFullFeatureLabel',
|
||||||
@ -88,18 +76,14 @@ const PAYWALL_FEATURE_META = {
|
|||||||
shareFull: undefined,
|
shareFull: undefined,
|
||||||
} satisfies { [K in PaywallFeatureName]: unknown }
|
} satisfies { [K in PaywallFeatureName]: unknown }
|
||||||
|
|
||||||
/**
|
/** Basic feature configuration. */
|
||||||
* Basic feature configuration.
|
|
||||||
*/
|
|
||||||
interface BasicFeatureConfiguration {
|
interface BasicFeatureConfiguration {
|
||||||
readonly level: PaywallLevel
|
readonly level: PaywallLevel
|
||||||
readonly bulletPointsTextId: `${PaywallFeatureName}FeatureBulletPoints`
|
readonly bulletPointsTextId: `${PaywallFeatureName}FeatureBulletPoints`
|
||||||
readonly descriptionTextId: `${PaywallFeatureName}FeatureDescription`
|
readonly descriptionTextId: `${PaywallFeatureName}FeatureDescription`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Feature configuration. */
|
||||||
* Feature configuration.
|
|
||||||
*/
|
|
||||||
export type FeatureConfiguration<Key extends PaywallFeatureName = PaywallFeatureName> =
|
export type FeatureConfiguration<Key extends PaywallFeatureName = PaywallFeatureName> =
|
||||||
BasicFeatureConfiguration & {
|
BasicFeatureConfiguration & {
|
||||||
readonly name: Key
|
readonly name: Key
|
||||||
@ -140,23 +124,17 @@ const PAYWALL_CONFIGURATION: Record<PaywallFeatureName, BasicFeatureConfiguratio
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Map a plan to a paywall level. */
|
||||||
* Map a plan to a paywall level.
|
|
||||||
*/
|
|
||||||
export function mapPlanOnPaywall(plan: backend.Plan | undefined): PaywallLevel {
|
export function mapPlanOnPaywall(plan: backend.Plan | undefined): PaywallLevel {
|
||||||
return plan != null ? PAYWALL_LEVELS[plan] : PAYWALL_LEVELS.free
|
return plan != null ? PAYWALL_LEVELS[plan] : PAYWALL_LEVELS.free
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Check if a given string is a valid feature name. */
|
||||||
* Check if a given string is a valid feature name.
|
|
||||||
*/
|
|
||||||
export function isFeatureName(name: string): name is PaywallFeatureName {
|
export function isFeatureName(name: string): name is PaywallFeatureName {
|
||||||
return name in PAYWALL_FEATURES
|
return name in PAYWALL_FEATURES
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Get the configuration for a given feature. */
|
||||||
* Get the configuration for a given feature.
|
|
||||||
*/
|
|
||||||
export function getFeatureConfiguration<Key extends PaywallFeatureName>(
|
export function getFeatureConfiguration<Key extends PaywallFeatureName>(
|
||||||
feature: Key,
|
feature: Key,
|
||||||
): FeatureConfiguration<Key> {
|
): FeatureConfiguration<Key> {
|
||||||
|
@ -8,9 +8,7 @@ import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
|||||||
|
|
||||||
import * as paywallFeaturesConfiguration from './FeaturesConfiguration'
|
import * as paywallFeaturesConfiguration from './FeaturesConfiguration'
|
||||||
|
|
||||||
/**
|
/** A hook that provides access to the paywall features configuration. */
|
||||||
* A hook that provides access to the paywall features configuration.
|
|
||||||
*/
|
|
||||||
export function usePaywallFeatures() {
|
export function usePaywallFeatures() {
|
||||||
const getFeature = eventCallbackHooks.useEventCallback(
|
const getFeature = eventCallbackHooks.useEventCallback(
|
||||||
<Key extends paywallFeaturesConfiguration.PaywallFeatureName>(feature: Key) => {
|
<Key extends paywallFeaturesConfiguration.PaywallFeatureName>(feature: Key) => {
|
||||||
|
@ -12,17 +12,13 @@ import type * as backend from '#/services/Backend'
|
|||||||
import * as paywallConfiguration from './FeaturesConfiguration'
|
import * as paywallConfiguration from './FeaturesConfiguration'
|
||||||
import * as paywallFeatures from './paywallFeaturesHooks'
|
import * as paywallFeatures from './paywallFeaturesHooks'
|
||||||
|
|
||||||
/**
|
/** Props for the {@link usePaywall} hook. */
|
||||||
* Props for the {@link usePaywall} hook.
|
|
||||||
*/
|
|
||||||
export interface UsePaywallProps {
|
export interface UsePaywallProps {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
readonly plan?: backend.Plan | undefined
|
readonly plan?: backend.Plan | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** A hook that provides paywall-related functionality. */
|
||||||
* A hook that provides paywall-related functionality.
|
|
||||||
*/
|
|
||||||
export function usePaywall(props: UsePaywallProps) {
|
export function usePaywall(props: UsePaywallProps) {
|
||||||
const { plan } = props
|
const { plan } = props
|
||||||
|
|
||||||
|
@ -13,9 +13,7 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
|
|||||||
|
|
||||||
import * as textProvider from '#/providers/TextProvider'
|
import * as textProvider from '#/providers/TextProvider'
|
||||||
|
|
||||||
/**
|
/** Props for the useCopy hook. */
|
||||||
* Props for the useCopy hook.
|
|
||||||
*/
|
|
||||||
export interface UseCopyProps {
|
export interface UseCopyProps {
|
||||||
readonly copyText: string
|
readonly copyText: string
|
||||||
readonly onCopy?: () => void
|
readonly onCopy?: () => void
|
||||||
@ -24,9 +22,7 @@ export interface UseCopyProps {
|
|||||||
|
|
||||||
const DEFAULT_TIMEOUT = 2000
|
const DEFAULT_TIMEOUT = 2000
|
||||||
|
|
||||||
/**
|
/** A hook for copying text to the clipboard. */
|
||||||
* A hook for copying text to the clipboard.
|
|
||||||
*/
|
|
||||||
export function useCopy(props: UseCopyProps) {
|
export function useCopy(props: UseCopyProps) {
|
||||||
const { copyText, onCopy, successToastMessage = true } = props
|
const { copyText, onCopy, successToastMessage = true } = props
|
||||||
|
|
||||||
|
@ -8,9 +8,7 @@ import * as React from 'react'
|
|||||||
import * as callbackHooks from './eventCallbackHooks'
|
import * as callbackHooks from './eventCallbackHooks'
|
||||||
import * as unmountEffect from './unmountHooks'
|
import * as unmountEffect from './unmountHooks'
|
||||||
|
|
||||||
/**
|
/** Wrap a callback into debounce function */
|
||||||
* Wrap a callback into debounce function
|
|
||||||
*/
|
|
||||||
export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>(
|
export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>(
|
||||||
callback: Fn,
|
callback: Fn,
|
||||||
deps: React.DependencyList,
|
deps: React.DependencyList,
|
||||||
@ -83,9 +81,8 @@ export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>(
|
|||||||
}, [stableCallback, delay, maxWait, ...deps])
|
}, [stableCallback, delay, maxWait, ...deps])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The type of a wrapped function that has been debounced. */
|
||||||
*
|
export type DebouncedFunction<Fn extends (...args: never[]) => unknown> = (
|
||||||
*/
|
this: ThisParameterType<Fn>,
|
||||||
export interface DebouncedFunction<Fn extends (...args: never[]) => unknown> {
|
...args: Parameters<Fn>
|
||||||
(this: ThisParameterType<Fn>, ...args: Parameters<Fn>): void
|
) => void
|
||||||
}
|
|
||||||
|
@ -9,9 +9,7 @@ import * as React from 'react'
|
|||||||
import * as debouncedCallback from './debounceCallbackHooks'
|
import * as debouncedCallback from './debounceCallbackHooks'
|
||||||
import * as eventCallbackHooks from './eventCallbackHooks'
|
import * as eventCallbackHooks from './eventCallbackHooks'
|
||||||
|
|
||||||
/**
|
/** A hook that returns a stateful value, and a function to update it that will debounce updates. */
|
||||||
* A hook that returns a stateful value, and a function to update it that will debounce updates.
|
|
||||||
*/
|
|
||||||
export function useDebounceState<S>(
|
export function useDebounceState<S>(
|
||||||
initialState: S | (() => S),
|
initialState: S | (() => S),
|
||||||
delay: number,
|
delay: number,
|
||||||
|
@ -5,9 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import * as debounceState from './debounceStateHooks'
|
import * as debounceState from './debounceStateHooks'
|
||||||
|
|
||||||
/**
|
/** Debounce a value. */
|
||||||
* Debounce a value.
|
|
||||||
*/
|
|
||||||
export function useDebounceValue<T>(value: T, delay: number, maxWait?: number) {
|
export function useDebounceValue<T>(value: T, delay: number, maxWait?: number) {
|
||||||
const [debouncedValue, setDebouncedValue] = debounceState.useDebounceState(value, delay, maxWait)
|
const [debouncedValue, setDebouncedValue] = debounceState.useDebounceState(value, delay, maxWait)
|
||||||
|
|
||||||
|
@ -34,9 +34,7 @@ export function useMounted(callback: () => void) {
|
|||||||
}, [stableCallback])
|
}, [stableCallback])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns a function that returns `true` if the component renders for the first time. */
|
||||||
* Returns a function that returns `true` if the component renders for the first time.
|
|
||||||
*/
|
|
||||||
export function useIsFirstRender() {
|
export function useIsFirstRender() {
|
||||||
const isFirstMount = useRef(true)
|
const isFirstMount = useRef(true)
|
||||||
const stableCallbackTrue = useEventCallback(() => true)
|
const stableCallbackTrue = useEventCallback(() => true)
|
||||||
|
@ -9,9 +9,7 @@ import * as reactQuery from '@tanstack/react-query'
|
|||||||
|
|
||||||
import * as eventCallback from '#/hooks/eventCallbackHooks'
|
import * as eventCallback from '#/hooks/eventCallbackHooks'
|
||||||
|
|
||||||
/**
|
/** Hook to get the offline status */
|
||||||
* Hook to get the offline status
|
|
||||||
*/
|
|
||||||
export function useOffline() {
|
export function useOffline() {
|
||||||
const isOnline = React.useSyncExternalStore(
|
const isOnline = React.useSyncExternalStore(
|
||||||
reactQuery.onlineManager.subscribe.bind(reactQuery.onlineManager),
|
reactQuery.onlineManager.subscribe.bind(reactQuery.onlineManager),
|
||||||
@ -22,17 +20,13 @@ export function useOffline() {
|
|||||||
return { isOffline: !isOnline }
|
return { isOffline: !isOnline }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Props for the {@link useOfflineChange} hook */
|
||||||
* Props for the {@link useOfflineChange} hook
|
|
||||||
*/
|
|
||||||
export interface UseOfflineChangeProps {
|
export interface UseOfflineChangeProps {
|
||||||
readonly triggerImmediate?: boolean | 'if-offline' | 'if-online'
|
readonly triggerImmediate?: boolean | 'if-offline' | 'if-online'
|
||||||
readonly isDisabled?: boolean
|
readonly isDisabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Hook to subscribe to online/offline changes */
|
||||||
* Hook to subscribe to online/offline changes
|
|
||||||
*/
|
|
||||||
export function useOfflineChange(
|
export function useOfflineChange(
|
||||||
callback: (isOffline: boolean) => void,
|
callback: (isOffline: boolean) => void,
|
||||||
props: UseOfflineChangeProps = {},
|
props: UseOfflineChangeProps = {},
|
||||||
|
@ -18,9 +18,7 @@ import * as safeJsonParse from '#/utilities/safeJsonParse'
|
|||||||
// === SearchParamsStateReturnType ===
|
// === SearchParamsStateReturnType ===
|
||||||
// ===================================
|
// ===================================
|
||||||
|
|
||||||
/**
|
/** The return type of the `useSearchParamsState` hook. */
|
||||||
* The return type of the `useSearchParamsState` hook.
|
|
||||||
*/
|
|
||||||
type SearchParamsStateReturnType<T> = Readonly<
|
type SearchParamsStateReturnType<T> = Readonly<
|
||||||
[
|
[
|
||||||
value: T,
|
value: T,
|
||||||
@ -29,9 +27,7 @@ type SearchParamsStateReturnType<T> = Readonly<
|
|||||||
]
|
]
|
||||||
>
|
>
|
||||||
|
|
||||||
/**
|
/** Set options for the `set` function. */
|
||||||
* Set options for the `set` function.
|
|
||||||
*/
|
|
||||||
export interface SearchParamsSetOptions {
|
export interface SearchParamsSetOptions {
|
||||||
readonly replace?: boolean
|
readonly replace?: boolean
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user