mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 20:01:34 +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"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run"
|
||||
"test": "vitest run",
|
||||
"lint": "eslint . --max-warnings=0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/query-core": "5.54.1",
|
||||
|
@ -4,17 +4,19 @@
|
||||
// === 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 {
|
||||
/** The JWT token to save. */
|
||||
/** The user's JWT token. */
|
||||
readonly accessToken: string
|
||||
/** The Cognito app integration client id. */
|
||||
/** The ID for the Cognito app integration. */
|
||||
readonly clientId: string
|
||||
/** The refresh token taken from authorization flow. */
|
||||
/** The refresh token taken from the authorization flow. */
|
||||
readonly refreshToken: string
|
||||
/** The Cognito url to refresh the token. */
|
||||
/** The Cognito URL to refresh the token. */
|
||||
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
|
||||
}
|
||||
|
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. */
|
||||
|
||||
/** 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.
|
||||
* 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>
|
||||
|
||||
/** An object containing app configuration to inject.
|
||||
/**
|
||||
* An object containing app configuration to inject.
|
||||
*
|
||||
* This includes:
|
||||
* - the base URL for backend endpoints
|
||||
* - the WebSocket URL for the chatbot
|
||||
* - 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>
|
||||
|
||||
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
|
||||
|
@ -1,6 +1,7 @@
|
||||
/** @file Functions for managing app configuration. */
|
||||
import * as fs from 'node:fs/promises'
|
||||
import * as path from 'node:path'
|
||||
import * as process from 'node:process'
|
||||
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 buildInfo = await (async () => {
|
||||
try {
|
||||
return await import('../../../../build.json', { with: { type: 'json' } })
|
||||
return await import('../../../build.json', { with: { type: 'json' } })
|
||||
} catch {
|
||||
return { commit: '', version: '', engineVersion: '', name: '' }
|
||||
}
|
||||
})()
|
||||
try {
|
||||
const file = await fs.readFile(filePath, { encoding: 'utf-8' })
|
||||
// eslint-disable-next-line jsdoc/valid-types
|
||||
/** @type {readonly (readonly [string, string])[]} */
|
||||
let entries = file.split('\n').flatMap(line => {
|
||||
if (/^\s*$|^.s*#/.test(line)) {
|
||||
@ -47,14 +47,10 @@ export async function readEnvironmentFromFile() {
|
||||
if (!isProduction || entries.length > 0) {
|
||||
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
|
||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
||||
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
|
||||
} 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
|
||||
// @ts-expect-error This is the only file where `process.env` should be written to.
|
||||
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
|
||||
const expectedKeys = Object.keys(DUMMY_DEFINES)
|
||||
.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. */
|
||||
export function loadTestEnvironmentVariables() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
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. */
|
||||
export const INDENT_SIZE: number
|
||||
|
||||
/** Get the environment variable value.
|
||||
/**
|
||||
* Get the environment variable value.
|
||||
* @param name - The name 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
|
||||
|
||||
/** 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.
|
||||
* @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
|
||||
|
||||
/** 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.
|
||||
* @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
|
||||
|
||||
/** Get the common prefix of the two strings.
|
||||
/**
|
||||
* Get the common prefix of the two strings.
|
||||
* @param a - the first string.
|
||||
* @param b - the second string.
|
||||
* @returns The common prefix. */
|
||||
* @returns The common prefix.
|
||||
*/
|
||||
export function getCommonPrefix(a: string, b: string): string
|
||||
|
@ -23,8 +23,10 @@ export enum Platform {
|
||||
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() {
|
||||
if (isOnWindowsPhone()) {
|
||||
// MUST be before Android and Windows.
|
||||
@ -96,8 +98,10 @@ export enum Browser {
|
||||
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 {
|
||||
if (isOnElectron()) {
|
||||
return Browser.electron
|
||||
@ -117,10 +121,12 @@ export function browser(): Browser {
|
||||
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)
|
||||
* 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() {
|
||||
return /electron/i.test(navigator.userAgent)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ window.dataLayer = window.dataLayer || []
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ export function event(name: string, params?: object) {
|
||||
}
|
||||
|
||||
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('config', GOOGLE_ANALYTICS_TAG)
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* circular dependency in our packages. */
|
||||
* circular dependency in our packages.
|
||||
*/
|
||||
|
||||
// ========================
|
||||
// === 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
|
||||
* `enso://authentication/register?code=...&state=...` uses this scheme. */
|
||||
* `enso://authentication/register?code=...&state=...` uses this scheme.
|
||||
*/
|
||||
export const DEEP_LINK_SCHEME: string
|
||||
|
||||
/** Name of the product. */
|
||||
@ -21,12 +25,16 @@ export const PRODUCT_NAME: string
|
||||
/** Company name, used as the copyright holder. */
|
||||
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
|
||||
|
||||
/** 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,
|
||||
* 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][]
|
||||
|
@ -10,9 +10,7 @@ import * as vueQuery from '@tanstack/vue-query'
|
||||
import * as idbKeyval from 'idb-keyval'
|
||||
|
||||
declare module '@tanstack/query-core' {
|
||||
/**
|
||||
* Query client with additional methods.
|
||||
*/
|
||||
/** Query client with additional methods. */
|
||||
interface QueryClient {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
readonly clearWithPersister: () => Promise<void>
|
||||
/**
|
||||
* Clear the cache stored in the persister storage.
|
||||
*/
|
||||
/** Clear the cache stored in the persister storage. */
|
||||
readonly nukePersister: () => Promise<void>
|
||||
}
|
||||
/**
|
||||
* Specifies the invalidation behavior of a mutation.
|
||||
*/
|
||||
/** Specifies the invalidation behavior of a mutation. */
|
||||
interface Register {
|
||||
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[]
|
||||
/**
|
||||
* 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'
|
||||
|
||||
/**
|
||||
* Create a new Tanstack Query client.
|
||||
*/
|
||||
/** Create a new Tanstack Query client. */
|
||||
export function createQueryClient(): QueryClient {
|
||||
const store = idbKeyval.createStore('enso', 'query-persist-cache')
|
||||
queryCore.onlineManager.setOnline(navigator.onLine)
|
||||
|
@ -29,8 +29,10 @@ export const UserGroupId = newtype.newtypeConstructor<UserGroupId>()
|
||||
export type DirectoryId = newtype.Newtype<string, '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 const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
|
||||
|
||||
@ -38,8 +40,10 @@ export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
|
||||
export type EmptyAssetId = newtype.Newtype<string, '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 const ErrorAssetId = newtype.newtypeConstructor<ErrorAssetId>()
|
||||
|
||||
@ -74,9 +78,7 @@ export type AssetId = IdType[keyof IdType]
|
||||
export type CheckoutSessionId = newtype.Newtype<string, '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 const SubscriptionId = newtype.newtypeConstructor<SubscriptionId>()
|
||||
|
||||
@ -129,14 +131,18 @@ export function isUserGroupId(id: string): id is UserGroupId {
|
||||
|
||||
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) {
|
||||
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() {
|
||||
return UserGroupId(`${PLACEHOLDER_USER_GROUP_PREFIX}${uniqueString.uniqueString()}`)
|
||||
}
|
||||
@ -153,17 +159,21 @@ export enum BackendType {
|
||||
|
||||
/** Metadata uniquely identifying a user inside an organization. */
|
||||
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
|
||||
/** The name of the parent organization. */
|
||||
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
|
||||
* 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,
|
||||
* 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 name: string
|
||||
readonly email: EmailAddress
|
||||
@ -173,8 +183,10 @@ export interface UserInfo {
|
||||
|
||||
/** A user in the application. These are the primary owners of a project. */
|
||||
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 isOrganizationAdmin: boolean
|
||||
readonly rootDirectoryId: DirectoryId
|
||||
@ -200,11 +212,15 @@ export enum ProjectState {
|
||||
provisioned = 'Provisioned',
|
||||
opened = 'Opened',
|
||||
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',
|
||||
/** 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',
|
||||
}
|
||||
|
||||
@ -374,11 +390,13 @@ export interface Label {
|
||||
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.
|
||||
* 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 {
|
||||
backend = 'Backend',
|
||||
ide = 'Ide',
|
||||
@ -458,9 +476,7 @@ export interface ResourceUsage {
|
||||
readonly storage: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata for a subscription.
|
||||
*/
|
||||
/** Metadata for a subscription. */
|
||||
export interface Subscription {
|
||||
readonly id?: SubscriptionId
|
||||
readonly plan?: Plan
|
||||
@ -566,7 +582,7 @@ export interface UpdatedDirectory {
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
export interface CopiedAsset {
|
||||
@ -711,8 +727,10 @@ export enum AssetType {
|
||||
secret = 'secret',
|
||||
datalink = 'datalink',
|
||||
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',
|
||||
/** A special {@link AssetType} representing a directory listing that is empty. */
|
||||
specialEmpty = 'specialEmpty',
|
||||
@ -732,8 +750,10 @@ export interface IdType {
|
||||
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>> = {
|
||||
// This is a sequence of numbers, not magic numbers. `1000` is an arbitrary number
|
||||
// that are higher than the number of possible asset types.
|
||||
@ -753,22 +773,28 @@ export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
|
||||
// === 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 {
|
||||
readonly id: AssetId
|
||||
readonly title: string
|
||||
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 permissions: readonly AssetPermission[] | null
|
||||
readonly labels: readonly LabelName[] | 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 {
|
||||
readonly type: 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}>. */
|
||||
export interface DirectoryAsset extends Asset<AssetType.directory> {}
|
||||
export type DirectoryAsset = Asset<AssetType.directory>
|
||||
|
||||
/** 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}>. */
|
||||
export interface FileAsset extends Asset<AssetType.file> {}
|
||||
export type FileAsset = Asset<AssetType.file>
|
||||
|
||||
/** 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}>. */
|
||||
export interface SecretAsset extends Asset<AssetType.secret> {}
|
||||
export type SecretAsset = Asset<AssetType.secret>
|
||||
|
||||
/** 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}>. */
|
||||
export interface SpecialEmptyAsset extends Asset<AssetType.specialEmpty> {}
|
||||
export type SpecialEmptyAsset = Asset<AssetType.specialEmpty>
|
||||
|
||||
/** 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 {
|
||||
return {
|
||||
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 {
|
||||
return {
|
||||
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 {
|
||||
return {
|
||||
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 {
|
||||
return {
|
||||
type: AssetType.specialError,
|
||||
@ -1011,8 +1045,10 @@ export interface AssetVersions {
|
||||
// === 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) {
|
||||
const relativePermissionPrecedence =
|
||||
permissions.PERMISSION_ACTION_PRECEDENCE[a.permission] -
|
||||
@ -1126,8 +1162,10 @@ export interface CreateProjectRequestBody {
|
||||
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 {
|
||||
readonly projectName: string | null
|
||||
readonly ami: Ami | null
|
||||
@ -1256,8 +1294,10 @@ export function compareAssets(a: AnyAsset, b: AnyAsset) {
|
||||
// === 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>) {
|
||||
return asset.id
|
||||
}
|
||||
@ -1313,16 +1353,16 @@ export function stripProjectExtension(name: string) {
|
||||
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) {
|
||||
const [, basename, extension] = name.match(/^(.*)[.](tar[.]gz|zip|enso-project)$/) ?? []
|
||||
return { basename: basename ?? name, extension: extension ?? '' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Network error class.
|
||||
*/
|
||||
/** Network error class. */
|
||||
export class NetworkError extends Error {
|
||||
/**
|
||||
* Create a new instance of the {@link NetworkError} class.
|
||||
@ -1336,9 +1376,7 @@ export class NetworkError extends Error {
|
||||
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 {}
|
||||
|
||||
// ===============
|
||||
|
@ -15,15 +15,19 @@ export function shallowEqual<T>(a: readonly T[], b: readonly T[]) {
|
||||
// === 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 {
|
||||
const arrayOfUnknown: unknown[] = array
|
||||
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>) {
|
||||
const set: Set<unknown> = array instanceof Set ? array : new Set<T>(array)
|
||||
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. */
|
||||
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) {
|
||||
const index = array.findIndex(predicate)
|
||||
array.splice(index === NOT_FOUND ? array.length : index, 0, ...items)
|
||||
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
|
||||
* returns `true`. */
|
||||
* returns `true`.
|
||||
*/
|
||||
export function splicedBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
||||
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) {
|
||||
const index = array.findIndex(predicate)
|
||||
array.splice(index === NOT_FOUND ? array.length : index + 1, 0, ...items)
|
||||
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
|
||||
* returns `true`. */
|
||||
* returns `true`.
|
||||
*/
|
||||
export function splicedAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
|
||||
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
|
||||
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) {
|
||||
return new Date(dateTime.getFullYear(), dateTime.getMonth(), dateTime.getDate())
|
||||
}
|
||||
|
@ -12,8 +12,10 @@ type NewtypeVariant<TypeName extends string> = {
|
||||
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`.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
type MutableNewtypeVariant<TypeName extends string> = {
|
||||
@ -21,7 +23,8 @@ type MutableNewtypeVariant<TypeName extends string> = {
|
||||
_$type: TypeName
|
||||
}
|
||||
|
||||
/** Used to create a "branded type",
|
||||
/**
|
||||
* Used to create a "branded type",
|
||||
* which contains a property that only exists at compile time.
|
||||
*
|
||||
* `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.
|
||||
* Note however because TypeScript is structurally typed,
|
||||
* 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
|
||||
|
||||
/** 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>> =
|
||||
T extends infer U & NewtypeVariant<T['_$type']> ?
|
||||
U extends infer V & MutableNewtypeVariant<T['_$type']> ?
|
||||
@ -51,9 +57,11 @@ type NotNewtype = {
|
||||
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
|
||||
* 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>>() {
|
||||
// This cast is unsafe.
|
||||
// `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. */
|
||||
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 {
|
||||
for (const [key, value] of Object.entries(update)) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
@ -57,8 +59,10 @@ export function unsafeMutable<T extends object>(object: T): { -readonly [K in ke
|
||||
// === 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>(
|
||||
object: T,
|
||||
): readonly { [K in keyof T]: readonly [K, T[K]] }[keyof T][] {
|
||||
@ -83,8 +87,10 @@ export function unsafeRemoveUndefined<T extends object>(
|
||||
// === 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>(
|
||||
object: Record<K, V>,
|
||||
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>> = {
|
||||
[Permission.owner]: PermissionAction.own,
|
||||
[Permission.admin]: PermissionAction.admin,
|
||||
@ -123,8 +125,10 @@ export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAc
|
||||
[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>> = {
|
||||
[Permission.owner]: 'ownerPermissionType',
|
||||
[Permission.admin]: 'adminPermissionType',
|
||||
@ -181,13 +185,13 @@ interface BasePermissions<T extends Permission> {
|
||||
}
|
||||
|
||||
/** Owner permissions for an asset. */
|
||||
interface OwnerPermissions extends BasePermissions<Permission.owner> {}
|
||||
type OwnerPermissions = BasePermissions<Permission.owner>
|
||||
|
||||
/** Admin permissions for an asset. */
|
||||
interface AdminPermissions extends BasePermissions<Permission.admin> {}
|
||||
type AdminPermissions = BasePermissions<Permission.admin>
|
||||
|
||||
/** Editor permissions for an asset. */
|
||||
interface EditPermissions extends BasePermissions<Permission.edit> {}
|
||||
type EditPermissions = BasePermissions<Permission.edit>
|
||||
|
||||
/** Reader permissions for an asset. */
|
||||
interface ReadPermissions extends BasePermissions<Permission.read> {
|
||||
|
@ -42,7 +42,7 @@ const guiTabCases = [
|
||||
v.test.each([
|
||||
{ group: 'Dashboard', cases: dashboardTabCases },
|
||||
{ group: 'GUI', cases: guiTabCases },
|
||||
])('Tab clip path: $group', ({ group, cases }) => {
|
||||
])('Tab clip path: $group', ({ cases }) => {
|
||||
cases.forEach(({ input, expected }) => {
|
||||
const result = tabBar.tabClipPath(input.bounds, input.radius, (input as TabClipPathInput)?.side)
|
||||
v.expect(result).toBe(expected)
|
||||
|
@ -2,8 +2,8 @@
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "es2023"],
|
||||
"allowJs": false,
|
||||
"checkJs": false,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"skipLibCheck": false
|
||||
},
|
||||
"include": ["./src/", "./src/text/english.json", "../types/"]
|
||||
|
@ -12,9 +12,7 @@ export default class EditorPageActions extends PageActions {
|
||||
get goToPage(): Omit<goToPageActions.GoToPageActions, 'editor'> {
|
||||
return goToPageActions.goToPageActions(this.step.bind(this))
|
||||
}
|
||||
/**
|
||||
* Waits for the editor to load.
|
||||
*/
|
||||
/** Waits for the editor to load. */
|
||||
waitForEditorToLoad(): EditorPageActions {
|
||||
return this.step('wait for the editor to load', async () => {
|
||||
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)
|
||||
})
|
||||
|
||||
/**
|
||||
* 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 }) => {
|
||||
await initGraph(page)
|
||||
|
||||
@ -57,9 +55,7 @@ test('Connect an node to a port', async ({ page }) => {
|
||||
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 }) => {
|
||||
await initGraph(page)
|
||||
|
||||
|
@ -34,9 +34,7 @@ test('Existence of edges between nodes', async ({ page }) => {
|
||||
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) {
|
||||
await actions.goToGraph(page)
|
||||
await actions.dragNodeByBinding(page, 'ten', 400, 0)
|
||||
|
@ -5,9 +5,7 @@ import { mockExpressionUpdate } from './expressionUpdates'
|
||||
import * as locate 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) {
|
||||
await actions.goToGraph(page)
|
||||
await mockExpressionUpdate(page, 'aggregated', { type: 'Standard.Table.Table.Table' })
|
||||
|
@ -21,7 +21,7 @@
|
||||
"build": "vite build",
|
||||
"build-cloud": "cross-env CLOUD_BUILD=true corepack pnpm run build",
|
||||
"preview": "vite preview",
|
||||
"lint": "cross-env eslint . --max-warnings=0",
|
||||
"lint": "eslint . --max-warnings=0",
|
||||
"format": "prettier --version && prettier --write src/ && eslint . --fix",
|
||||
"dev:vite": "vite",
|
||||
"test": "corepack pnpm run /^^^^test:.*/",
|
||||
|
@ -7,9 +7,7 @@ export const DOCUMENTS = getDocumentsPath()
|
||||
|
||||
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 {
|
||||
if (process.platform === 'linux') {
|
||||
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 {
|
||||
const xdgDocumentsPath = getXdgDocumentsPath()
|
||||
|
||||
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 {
|
||||
const out = childProcess.spawnSync('xdg-user-dir', ['DOCUMENTS'], {
|
||||
timeout: CHILD_PROCESS_TIMEOUT,
|
||||
@ -54,9 +48,7 @@ function getMacOsDocumentsPath(): string {
|
||||
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 {
|
||||
const out = childProcess.spawnSync(
|
||||
'reg',
|
||||
|
@ -45,23 +45,17 @@ export const SUPPORT_EMAIL = 'cloud@enso.org'
|
||||
/** Return the `mailto:` URL for contacting support. */
|
||||
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 {
|
||||
return SUBSCRIBE_PATH + '?plan=' + plan
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mailto URL for contacting sales.
|
||||
*/
|
||||
/** Return the mailto URL for contacting sales. */
|
||||
export function getSalesEmail(): string {
|
||||
return 'mailto:contact@enso.org'
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Subscription URL for contacting sales.
|
||||
*/
|
||||
/** Build a Subscription URL for contacting sales. */
|
||||
export function getContactSalesURL(): string {
|
||||
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() {
|
||||
return Promise.resolve(results.Ok(null))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns MFA preference for the current user.
|
||||
*/
|
||||
/** Returns MFA preference for the current user. */
|
||||
async getMFAPreference() {
|
||||
return Promise.resolve(results.Ok('NOMFA'))
|
||||
}
|
||||
|
@ -74,9 +74,7 @@ interface UserAttributes {
|
||||
}
|
||||
/* 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'
|
||||
|
||||
/**
|
||||
@ -244,9 +242,7 @@ export class Cognito {
|
||||
return userInfo.attributes['custom:organizationId'] ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets user email from cognito
|
||||
*/
|
||||
/** Gets user email from cognito */
|
||||
async email() {
|
||||
// This `any` comes from a third-party API and cannot be avoided.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
@ -337,9 +333,7 @@ export class Cognito {
|
||||
return result.mapErr(intoAmplifyErrorOrThrow).mapErr(intoSignInWithPasswordErrorOrThrow)
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the current user session.
|
||||
*/
|
||||
/** Refresh the current user session. */
|
||||
async refreshUserSession() {
|
||||
const result = await results.Result.wrapAsync(async () => {
|
||||
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() {
|
||||
const email = await this.email()
|
||||
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) {
|
||||
const cognitoUserResult = await currentAuthenticatedUser()
|
||||
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() {
|
||||
const cognitoUserResult = await currentAuthenticatedUser()
|
||||
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(
|
||||
user: amplify.CognitoUser,
|
||||
confirmationCode: string,
|
||||
|
@ -11,9 +11,7 @@ import { createContext, useContext, useId } from 'react'
|
||||
import { twJoin } from '#/utilities/tailwindMerge'
|
||||
import invariant from 'tiny-invariant'
|
||||
|
||||
/**
|
||||
* Props for {@link AnimatedBackground}.
|
||||
*/
|
||||
/** Props for {@link AnimatedBackground}. */
|
||||
interface AnimatedBackgroundProps extends PropsWithChildren {
|
||||
readonly value: string
|
||||
readonly transition?: Transition
|
||||
@ -37,9 +35,7 @@ const DEFAULT_TRANSITION: Transition = {
|
||||
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) {
|
||||
const { value, transition = DEFAULT_TRANSITION, children } = props
|
||||
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 {
|
||||
readonly value: string
|
||||
readonly className?: string
|
||||
readonly animationClassName?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Item within an {@link AnimatedBackground}.
|
||||
*/
|
||||
/** Item within an {@link AnimatedBackground}. */
|
||||
AnimatedBackground.Item = function AnimatedBackgroundItem(props: AnimatedBackgroundItemProps) {
|
||||
const context = useContext(AnimatedBackgroundContext)
|
||||
invariant(context, 'useAnimatedBackground must be used within an AnimatedBackgroundProvider')
|
||||
|
@ -61,9 +61,7 @@ export interface AlertProps
|
||||
extends PropsWithChildren,
|
||||
VariantProps<typeof ALERT_STYLES>,
|
||||
HTMLAttributes<HTMLDivElement> {
|
||||
/**
|
||||
* The icon to display in the Alert
|
||||
*/
|
||||
/** The icon to display in the Alert */
|
||||
readonly icon?: React.ReactElement | string | null | undefined
|
||||
}
|
||||
|
||||
|
@ -22,39 +22,29 @@ export type ButtonProps =
|
||||
| (BaseButtonProps<aria.ButtonRenderProps> & Omit<aria.ButtonProps, 'onPress'> & PropsWithoutHref)
|
||||
| (BaseButtonProps<aria.LinkRenderProps> & Omit<aria.LinkProps, 'onPress'> & PropsWithHref)
|
||||
|
||||
/**
|
||||
* Props for a button with an href.
|
||||
*/
|
||||
/** Props for a button with an href. */
|
||||
interface PropsWithHref {
|
||||
readonly href: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for a button without an href.
|
||||
*/
|
||||
/** Props for a button without an href. */
|
||||
interface PropsWithoutHref {
|
||||
readonly href?: never
|
||||
}
|
||||
|
||||
/**
|
||||
* Base props for a button.
|
||||
*/
|
||||
/** Base props for a button. */
|
||||
export interface BaseButtonProps<Render>
|
||||
extends Omit<VariantProps<typeof BUTTON_STYLES>, 'iconOnly'> {
|
||||
/** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */
|
||||
readonly tooltip?: React.ReactElement | string | false | null
|
||||
readonly tooltipPlacement?: aria.Placement
|
||||
/**
|
||||
* The icon to display in the button
|
||||
*/
|
||||
/** The icon to display in the button */
|
||||
readonly icon?:
|
||||
| React.ReactElement
|
||||
| string
|
||||
| ((render: Render) => React.ReactElement | string | null)
|
||||
| null
|
||||
/**
|
||||
* When `true`, icon will be shown only when hovered.
|
||||
*/
|
||||
/** When `true`, icon will be shown only when hovered. */
|
||||
readonly showIconOnHover?: boolean
|
||||
/**
|
||||
* 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 { 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<
|
||||
VariantProps<typeof CHECKBOX_STYLES>,
|
||||
'isDisabled' | 'isInvalid'
|
||||
@ -46,18 +44,14 @@ export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<S
|
||||
readonly checkboxRef?: MutableRefObject<HTMLInputElement>
|
||||
} & (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 {
|
||||
readonly value: string
|
||||
readonly form?: 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<
|
||||
Schema extends TSchema,
|
||||
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
|
||||
export const Checkbox = forwardRef(function Checkbox<
|
||||
Schema extends TSchema,
|
||||
|
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
/** @file */
|
||||
import { useEventCallback } from '#/hooks/eventCallbackHooks'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import { createContext, useContext, useMemo, useState } from 'react'
|
||||
@ -8,9 +6,7 @@ import type { StoreApi } from 'zustand'
|
||||
import { createStore } from 'zustand'
|
||||
import type { TSchema, UseFormRegisterReturn } from '../Form'
|
||||
|
||||
/**
|
||||
* Context for the checkbox.
|
||||
*/
|
||||
/** Context for the checkbox. */
|
||||
interface CheckboxContextType {
|
||||
readonly store: StoreApi<CheckGroupPropsState>
|
||||
readonly addSelected: (selected: string) => void
|
||||
@ -25,9 +21,7 @@ const CheckboxContext = createContext<CheckboxContextType>({
|
||||
toggleSelected: () => {},
|
||||
})
|
||||
|
||||
/**
|
||||
* Gets the context for the checkbox.
|
||||
*/
|
||||
/** Gets the context for the checkbox. */
|
||||
export function useCheckboxContext() {
|
||||
return useContext(CheckboxContext)
|
||||
}
|
||||
@ -50,9 +44,7 @@ export function useCheckboxGroupState() {
|
||||
*/
|
||||
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 {
|
||||
readonly insideGroup: true
|
||||
readonly selected: Set<string>
|
||||
@ -60,16 +52,12 @@ interface CheckBoxGroupPropsStateInsideGroup {
|
||||
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 {
|
||||
readonly insideGroup: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for {@link CheckboxGroupProvider}.
|
||||
*/
|
||||
/** Props for {@link CheckboxGroupProvider}. */
|
||||
export interface CheckboxGroupProviderProps extends PropsWithChildren {
|
||||
readonly name: string
|
||||
readonly onChange: (selected: string[]) => void
|
||||
@ -77,9 +65,7 @@ export interface CheckboxGroupProviderProps extends PropsWithChildren {
|
||||
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) {
|
||||
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 { 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>>
|
||||
extends FieldStateProps<AriaCheckboxGroupProps, Schema, TFieldName>,
|
||||
FieldProps,
|
||||
@ -36,9 +34,7 @@ const CHECKBOX_GROUP_STYLES = tv({
|
||||
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
|
||||
export const CheckboxGroup = forwardRef(
|
||||
<Schema extends TSchema, TFieldName extends FieldPath<Schema>>(
|
||||
|
@ -12,14 +12,10 @@ import * as eventCallback from '#/hooks/eventCallbackHooks'
|
||||
import * as button from '../Button'
|
||||
import * as dialogProvider from './DialogProvider'
|
||||
|
||||
/**
|
||||
* Props for {@link Close} component.
|
||||
*/
|
||||
/** Props for {@link Close} component. */
|
||||
export type CloseProps = button.ButtonProps
|
||||
|
||||
/**
|
||||
* Close button for a dialog.
|
||||
*/
|
||||
/** Close button for a dialog. */
|
||||
export function Close(props: CloseProps) {
|
||||
const dialogContext = dialogProvider.useDialogContext()
|
||||
|
||||
|
@ -23,9 +23,7 @@ import { DIALOG_BACKGROUND } from './variants'
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
/**
|
||||
* Props for the {@link Dialog} component.
|
||||
*/
|
||||
/** Props for the {@link Dialog} component. */
|
||||
export interface DialogProps
|
||||
extends types.DialogProps,
|
||||
Omit<VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {}
|
||||
@ -177,9 +175,7 @@ export function Dialog(props: DialogProps) {
|
||||
|
||||
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) => {
|
||||
React.startTransition(() => {
|
||||
if (scrollTop > 0) {
|
||||
|
@ -5,28 +5,20 @@
|
||||
*/
|
||||
import * as React from 'react'
|
||||
|
||||
/**
|
||||
* The context value for a dialog.
|
||||
*/
|
||||
/** The context value for a dialog. */
|
||||
export interface DialogContextValue {
|
||||
readonly close: () => void
|
||||
readonly dialogId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The context for a dialog.
|
||||
*/
|
||||
/** The context for a dialog. */
|
||||
const DialogContext = React.createContext<DialogContextValue | null>(null)
|
||||
|
||||
/**
|
||||
* The provider for a dialog.
|
||||
*/
|
||||
/** The provider for a dialog. */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export const DialogProvider = DialogContext.Provider
|
||||
|
||||
/**
|
||||
* Custom hook to get the dialog context.
|
||||
*/
|
||||
/** Custom hook to get the dialog context. */
|
||||
export function useDialogContext() {
|
||||
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'
|
||||
|
||||
@ -8,17 +6,13 @@ import invariant from 'tiny-invariant'
|
||||
|
||||
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 {
|
||||
readonly id: string
|
||||
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 {
|
||||
readonly stack: DialogStackItem[]
|
||||
readonly dialogsStack: DialogStackItem[]
|
||||
@ -28,9 +22,7 @@ export interface DialogStackContextType {
|
||||
|
||||
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) {
|
||||
const { children } = props
|
||||
|
||||
@ -72,9 +64,7 @@ updated properly.`)
|
||||
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>) {
|
||||
const { children, id: idRaw, type: typeRaw } = props
|
||||
const idRef = React.useRef(idRaw)
|
||||
@ -100,16 +90,12 @@ export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackI
|
||||
return children
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for {@link useDialogStackState}
|
||||
*/
|
||||
/** Props for {@link useDialogStackState} */
|
||||
export interface UseDialogStackStateProps {
|
||||
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) {
|
||||
const ctx = React.useContext(DialogStackContext)
|
||||
|
||||
|
@ -10,21 +10,15 @@ import { useOverlayTriggerState } from 'react-stately'
|
||||
|
||||
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 {
|
||||
readonly isOpen: boolean
|
||||
readonly close: () => void
|
||||
readonly open: () => void
|
||||
}
|
||||
/**
|
||||
* Props for a {@link DialogTrigger}.
|
||||
*/
|
||||
/** Props for a {@link DialogTrigger}. */
|
||||
export interface DialogTriggerProps extends Omit<aria.DialogTriggerProps, 'children'> {
|
||||
/**
|
||||
* The trigger element.
|
||||
*/
|
||||
/** The trigger element. */
|
||||
readonly children: [
|
||||
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(', ')})`
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return element.closest(IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR)
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for {@link useInteractOutside}
|
||||
*/
|
||||
/** Props for {@link useInteractOutside} */
|
||||
export interface UseInteractOutsideProps {
|
||||
readonly ref: React.RefObject<HTMLElement>
|
||||
readonly id: string
|
||||
@ -40,9 +36,7 @@ export interface UseInteractOutsideProps {
|
||||
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) {
|
||||
const { ref, id, onInteractOutside, isDisabled = false } = props
|
||||
const shouldCloseOnInteractOutsideRef = React.useRef(false)
|
||||
|
@ -14,9 +14,7 @@ import * as text from '../../Text'
|
||||
import { Form } from '../Form'
|
||||
import type * as types from './types'
|
||||
|
||||
/**
|
||||
* Props for Field component
|
||||
*/
|
||||
/** Props for Field component */
|
||||
export interface FieldComponentProps<Schema extends types.TSchema>
|
||||
extends VariantProps<typeof FIELD_STYLES>,
|
||||
types.FieldProps {
|
||||
@ -29,16 +27,12 @@ export interface FieldComponentProps<Schema extends types.TSchema>
|
||||
readonly style?: React.CSSProperties | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for Field variants
|
||||
*/
|
||||
/** Props for Field variants */
|
||||
export interface FieldVariantProps {
|
||||
readonly fieldVariants?: VariantProps<typeof FIELD_STYLES>['variants'] | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for Field children
|
||||
*/
|
||||
/** Props for Field children */
|
||||
export interface FieldChildrenRenderProps {
|
||||
readonly isInvalid: boolean
|
||||
readonly isDirty: boolean
|
||||
@ -65,9 +59,7 @@ export const FIELD_STYLES = tv({
|
||||
defaultVariants: { fullWidth: true },
|
||||
})
|
||||
|
||||
/**
|
||||
* Field component
|
||||
*/
|
||||
/** Field component */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export const Field = forwardRef(function Field<Schema extends types.TSchema>(
|
||||
props: FieldComponentProps<Schema>,
|
||||
|
@ -13,18 +13,14 @@ import * as reactAriaComponents from '#/components/AriaComponents'
|
||||
import * as formContext from './FormProvider'
|
||||
import type * as types from './types'
|
||||
|
||||
/**
|
||||
* Props for the FormError component.
|
||||
*/
|
||||
/** Props for the FormError component. */
|
||||
export interface FormErrorProps extends Omit<reactAriaComponents.AlertProps, 'children'> {
|
||||
// We do not need to know the form fields.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
readonly form?: types.FormInstance<any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Form error component.
|
||||
*/
|
||||
/** Form error component. */
|
||||
export function FormError(props: FormErrorProps) {
|
||||
const { size = 'large', variant = 'error', rounded = 'large', ...alertProps } = props
|
||||
|
||||
@ -33,9 +29,7 @@ export function FormError(props: FormErrorProps) {
|
||||
const { errors } = formState
|
||||
const { getText } = textProvider.useText()
|
||||
|
||||
/**
|
||||
* Get the error message.
|
||||
*/
|
||||
/** Get the error message. */
|
||||
const getSubmitError = (): string | null => {
|
||||
const formErrors = errors.root
|
||||
|
||||
|
@ -9,9 +9,7 @@ import invariant from 'tiny-invariant'
|
||||
import type * as types 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> {
|
||||
readonly form: types.UseFormReturn<Schema>
|
||||
}
|
||||
@ -20,9 +18,7 @@ interface FormContextType<Schema extends types.TSchema> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
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>(
|
||||
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>(
|
||||
form?: FormInstanceValidated<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<
|
||||
Form extends FormInstanceValidated<Schema> | undefined,
|
||||
Schema extends types.TSchema,
|
||||
|
@ -11,9 +11,7 @@ import { useText } from '#/providers/TextProvider'
|
||||
import * as formContext from './FormProvider'
|
||||
import type * as types from './types'
|
||||
|
||||
/**
|
||||
* Props for the Reset component.
|
||||
*/
|
||||
/** Props for the Reset component. */
|
||||
export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'> {
|
||||
/**
|
||||
* Connects the submit button to a form.
|
||||
@ -28,9 +26,7 @@ export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'>
|
||||
readonly action?: 'cancel' | 'reset'
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset button for forms.
|
||||
*/
|
||||
/** Reset button for forms. */
|
||||
export function Reset(props: ResetProps): React.JSX.Element {
|
||||
const { getText } = useText()
|
||||
const {
|
||||
|
@ -11,9 +11,7 @@ import { useText } from '#/providers/TextProvider'
|
||||
import { useFormContext } from './FormProvider'
|
||||
import type { FormInstance } from './types'
|
||||
|
||||
/**
|
||||
* Additional props for the Submit component.
|
||||
*/
|
||||
/** Additional props for the Submit component. */
|
||||
interface SubmitButtonBaseProps {
|
||||
readonly variant?: ButtonProps['variant']
|
||||
/**
|
||||
@ -29,9 +27,7 @@ interface SubmitButtonBaseProps {
|
||||
readonly action?: 'cancel' | 'submit' | 'update'
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the Submit component.
|
||||
*/
|
||||
/** Props for the Submit component. */
|
||||
export type SubmitProps = Omit<ButtonProps, 'formnovalidate' | 'href' | 'variant'> &
|
||||
SubmitButtonBaseProps
|
||||
|
||||
|
@ -24,17 +24,13 @@ export type TransformedValues<Schema extends TSchema | undefined> =
|
||||
*/
|
||||
export type FieldPath<Schema extends TSchema> = reactHookForm.FieldPath<FieldValues<Schema>>
|
||||
|
||||
/**
|
||||
* Schema type
|
||||
*/
|
||||
/** Schema type */
|
||||
export type TSchema =
|
||||
| z.AnyZodObject
|
||||
| z.ZodEffects<z.AnyZodObject>
|
||||
| z.ZodEffects<z.ZodEffects<z.AnyZodObject>>
|
||||
|
||||
/**
|
||||
* OnSubmitCallbacks type.
|
||||
*/
|
||||
/** OnSubmitCallbacks type. */
|
||||
export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void> {
|
||||
readonly onSubmit?:
|
||||
| ((
|
||||
@ -67,9 +63,7 @@ export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void>
|
||||
| undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the useForm hook.
|
||||
*/
|
||||
/** Props for the useForm hook. */
|
||||
export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
|
||||
extends Omit<
|
||||
reactHookForm.UseFormProps<FieldValues<Schema>>,
|
||||
@ -83,16 +77,12 @@ export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
|
||||
*/
|
||||
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 method?: 'dialog' | (string & {}) | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Register function for a form field.
|
||||
*/
|
||||
/** Register function for a form field. */
|
||||
export type UseFormRegister<Schema extends TSchema> = <
|
||||
TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
|
||||
>(
|
||||
@ -100,9 +90,7 @@ export type UseFormRegister<Schema extends TSchema> = <
|
||||
options?: reactHookForm.RegisterOptions<FieldValues<Schema>, TFieldName>,
|
||||
) => UseFormRegisterReturn<Schema, TFieldName>
|
||||
|
||||
/**
|
||||
* UseFormRegister return type.
|
||||
*/
|
||||
/** UseFormRegister return type. */
|
||||
export interface UseFormRegisterReturn<
|
||||
Schema extends TSchema,
|
||||
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>
|
||||
|
||||
/**
|
||||
* 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<
|
||||
BaseValueType,
|
||||
Schema extends TSchema,
|
||||
@ -180,9 +166,7 @@ export type FormInstanceValidated<
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
> = 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
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export interface FieldProps {
|
||||
@ -191,27 +175,19 @@ export interface FieldProps {
|
||||
readonly description?: 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
|
||||
'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
|
||||
'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
|
||||
'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
|
||||
'aria-details'?: string | undefined
|
||||
}
|
||||
@ -233,9 +209,7 @@ export interface FormFieldProps<
|
||||
readonly isInvalid?: boolean | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Field State Props
|
||||
*/
|
||||
/** Field State Props */
|
||||
export type FieldStateProps<
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
BaseProps extends { value?: unknown },
|
||||
|
@ -8,9 +8,7 @@ import * as reactHookForm from 'react-hook-form'
|
||||
import * as formContext from './FormProvider'
|
||||
import type * as types from './types'
|
||||
|
||||
/**
|
||||
* Options for {@link useField} hook.
|
||||
*/
|
||||
/** Options for {@link useField} hook. */
|
||||
export interface UseFieldOptions<
|
||||
BaseValueType,
|
||||
Schema extends types.TSchema,
|
||||
@ -21,9 +19,7 @@ export interface UseFieldOptions<
|
||||
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<
|
||||
BaseValueType,
|
||||
Schema extends types.TSchema,
|
||||
|
@ -13,9 +13,7 @@ import type {
|
||||
TSchema,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* Options for the useFieldRegister hook.
|
||||
*/
|
||||
/** Options for the useFieldRegister hook. */
|
||||
export type UseFieldRegisterOptions<
|
||||
BaseValueType extends { value?: unknown },
|
||||
Schema extends TSchema,
|
||||
@ -31,9 +29,7 @@ export type UseFieldRegisterOptions<
|
||||
setValueAs?: ((value: unknown) => unknown) | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a field in the form.
|
||||
*/
|
||||
/** Registers a field in the form. */
|
||||
export function useFieldRegister<
|
||||
BaseValueType extends { value?: unknown },
|
||||
Schema extends TSchema,
|
||||
@ -62,9 +58,7 @@ export function useFieldRegister<
|
||||
|
||||
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.
|
||||
// eslint-disable-next-line camelcase, @typescript-eslint/naming-convention
|
||||
function unsafe__extractValidationDetailsFromSchema<
|
||||
|
@ -7,9 +7,7 @@ import { useFormState } from 'react-hook-form'
|
||||
import { useFormContext } from './FormProvider'
|
||||
import type { FieldPath, FormInstanceValidated, TSchema } from './types'
|
||||
|
||||
/**
|
||||
* Options for the `useFieldState` hook.
|
||||
*/
|
||||
/** Options for the `useFieldState` hook. */
|
||||
export interface UseFieldStateOptions<
|
||||
Schema extends TSchema,
|
||||
TFieldName extends FieldPath<Schema>,
|
||||
@ -18,9 +16,7 @@ export interface UseFieldStateOptions<
|
||||
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>>(
|
||||
options: UseFieldStateOptions<Schema, TFieldName>,
|
||||
) {
|
||||
|
@ -18,9 +18,7 @@ import { useMutation } from '@tanstack/react-query'
|
||||
import * as schemaModule from './schema'
|
||||
import type * as types from './types'
|
||||
|
||||
/**
|
||||
* Maps the value to the event object.
|
||||
*/
|
||||
/** Maps the value to the event object. */
|
||||
function mapValueOnEvent(value: unknown) {
|
||||
if (typeof value === 'object' && value != null && 'target' in value && 'type' in 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>(
|
||||
args: types.UseFormProps<Schema, SubmitResult>,
|
||||
) {
|
||||
|
@ -5,9 +5,7 @@
|
||||
*/
|
||||
import * as twv from '#/utilities/tailwindVariants'
|
||||
|
||||
/**
|
||||
* Props for form components.
|
||||
*/
|
||||
/** Props for form components. */
|
||||
export type FormStyleProps = twv.VariantProps<typeof FORM_STYLES>
|
||||
export const FORM_STYLES = twv.tv({
|
||||
base: 'flex flex-col items-start',
|
||||
|
@ -14,18 +14,14 @@ import type * as styles from './styles'
|
||||
|
||||
export type * from './components'
|
||||
|
||||
/**
|
||||
* Props for the Form component
|
||||
*/
|
||||
/** Props for the Form component */
|
||||
export type FormProps<
|
||||
Schema extends components.TSchema,
|
||||
SubmitResult = void,
|
||||
> = BaseFormProps<Schema> &
|
||||
(FormPropsWithOptions<Schema, SubmitResult> | FormPropsWithParentForm<Schema>)
|
||||
|
||||
/**
|
||||
* Base props for the Form component.
|
||||
*/
|
||||
/** Base props for the Form component. */
|
||||
interface BaseFormProps<Schema extends components.TSchema>
|
||||
extends Omit<
|
||||
React.HTMLProps<HTMLFormElement>,
|
||||
@ -48,9 +44,7 @@ interface BaseFormProps<Schema extends components.TSchema>
|
||||
|
||||
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 canSubmitOffline?: boolean
|
||||
@ -93,9 +87,7 @@ interface FormPropsWithOptions<Schema extends components.TSchema, SubmitResult =
|
||||
readonly form?: never
|
||||
}
|
||||
|
||||
/**
|
||||
* Register function for a form field.
|
||||
*/
|
||||
/** Register function for a form field. */
|
||||
export type UseFormRegister<Schema extends components.TSchema> = <
|
||||
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>,
|
||||
) => UseFormRegisterReturn<Schema, TFieldName>
|
||||
|
||||
/**
|
||||
* UseFormRegister return type.
|
||||
*/
|
||||
/** UseFormRegister return type. */
|
||||
export interface UseFormRegisterReturn<
|
||||
Schema extends components.TSchema,
|
||||
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
|
||||
@ -119,9 +109,7 @@ export interface UseFormRegisterReturn<
|
||||
readonly isInvalid?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Form Render Props.
|
||||
*/
|
||||
/** Form Render Props. */
|
||||
export type FormStateRenderProps<Schema extends components.TSchema> = Pick<
|
||||
components.FormInstance<Schema>,
|
||||
| 'clearErrors'
|
||||
|
@ -32,9 +32,7 @@ import type { ExtractFunction, VariantProps } from '#/utilities/tailwindVariants
|
||||
import { omit } from 'enso-common/src/utilities/data/object'
|
||||
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>>
|
||||
extends FieldStateProps<Omit<aria.InputProps, 'children' | 'size'>, Schema, TFieldName>,
|
||||
FieldProps,
|
||||
@ -53,9 +51,7 @@ export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath
|
||||
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<
|
||||
Schema extends TSchema,
|
||||
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<
|
||||
Schema extends TSchema,
|
||||
TFieldName extends FieldPath<Schema>,
|
||||
|
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
/** @file */
|
||||
import { mergeProps } from '#/components/aria'
|
||||
import { mergeRefs } from '#/utilities/mergeRefs'
|
||||
import type { VariantProps } from '#/utilities/tailwindVariants'
|
||||
@ -23,9 +21,7 @@ import { Separator } from '../../Separator'
|
||||
import { TEXT_STYLE } from '../../Text'
|
||||
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>>
|
||||
extends FieldStateProps<Omit<OTPInputProps, 'children' | 'render'>, Schema, TFieldName>,
|
||||
FieldProps,
|
||||
@ -40,9 +36,7 @@ export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldP
|
||||
* @default true
|
||||
*/
|
||||
readonly submitOnComplete?: boolean
|
||||
/**
|
||||
* Callback when the OTP is filled.
|
||||
*/
|
||||
/** Callback when the OTP is filled. */
|
||||
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<
|
||||
Schema extends TSchema,
|
||||
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> {}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
useEffect,
|
||||
useRef,
|
||||
@ -31,9 +29,7 @@ const CONTENT_EDITABLE_STYLES = tv({
|
||||
slots: { placeholder: 'opacity-50 absolute inset-0 pointer-events-none' },
|
||||
})
|
||||
|
||||
/**
|
||||
* Props for a {@link ResizableContentEditableInput}.
|
||||
*/
|
||||
/** Props for a {@link ResizableContentEditableInput}. */
|
||||
export interface ResizableContentEditableInputProps<
|
||||
Schema extends TSchema,
|
||||
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 eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
||||
@ -12,17 +10,13 @@ import * as mergeRefs from '#/utilities/mergeRefs'
|
||||
import { forwardRef } from '#/utilities/react'
|
||||
import * as variants from '../variants'
|
||||
|
||||
/**
|
||||
* Props for a {@link ResizableInput}.
|
||||
*/
|
||||
/** Props for a {@link ResizableInput}. */
|
||||
export interface ResizableInputProps extends aria.TextFieldProps {
|
||||
readonly placeholder?: string
|
||||
readonly description?: React.ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* A resizable input field.
|
||||
*/
|
||||
/** A resizable input field. */
|
||||
export const ResizableInput = forwardRef(function ResizableInput(
|
||||
props: ResizableInputProps,
|
||||
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<
|
||||
Schema extends TSchema,
|
||||
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 {
|
||||
readonly label?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A radio button.
|
||||
*/
|
||||
/** A radio button. */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export const Radio = forwardRef(function Radio(
|
||||
props: RadioProps,
|
||||
|
@ -16,9 +16,7 @@ import type { FieldVariantProps } from '../Form'
|
||||
import * as formComponent from '../Form'
|
||||
import * as radioGroupContext from './RadioGroupContext'
|
||||
|
||||
/**
|
||||
* Props for {@link RadioGroup}.
|
||||
*/
|
||||
/** Props for {@link RadioGroup}. */
|
||||
export interface RadioGroupProps<
|
||||
Schema extends formComponent.TSchema,
|
||||
TFieldName extends formComponent.FieldPath<Schema>,
|
||||
@ -39,9 +37,7 @@ export const RADIO_GROUP_STYLES = twv.tv({
|
||||
variants: { fullWidth: { true: 'w-full' } },
|
||||
})
|
||||
|
||||
/**
|
||||
* A radio group component.
|
||||
*/
|
||||
/** A radio group component. */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export const RadioGroup = forwardRef(function RadioGroup<
|
||||
Schema extends formComponent.TSchema,
|
||||
|
@ -18,9 +18,7 @@ import invariant from 'tiny-invariant'
|
||||
|
||||
import * as eventCallback from '#/hooks/eventCallbackHooks'
|
||||
|
||||
/**
|
||||
* Props for {@link RadioGroupContextProps}
|
||||
*/
|
||||
/** Props for {@link RadioGroupContextProps} */
|
||||
export interface RadioGroupContextProps {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
readonly pressedRadio: string | null
|
||||
/**
|
||||
* Sets the pressed Radio element
|
||||
*/
|
||||
/** Sets the pressed Radio element */
|
||||
readonly setPressedRadio: (value: string) => void
|
||||
/**
|
||||
* Clears the pressed Radio element
|
||||
*/
|
||||
/** Clears the pressed Radio element */
|
||||
readonly clearPressedRadio: () => void
|
||||
}
|
||||
|
||||
@ -68,16 +62,12 @@ export function RadioGroupProvider(props: React.PropsWithChildren) {
|
||||
return <RadioGroupContext.Provider value={value}>{children}</RadioGroupContext.Provider>
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for {@link useRadioGroupContext}
|
||||
*/
|
||||
/** Props for {@link useRadioGroupContext} */
|
||||
export interface UseRadioGroupContextProps {
|
||||
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) {
|
||||
const { value } = props
|
||||
const context = React.useContext(RadioGroupContext)
|
||||
|
@ -9,18 +9,14 @@ import * as aria from '#/components/aria'
|
||||
|
||||
import * as twv from '#/utilities/tailwindVariants'
|
||||
|
||||
/**
|
||||
* The props for {@link Separator} component.
|
||||
*/
|
||||
/** The props for {@link Separator} component. */
|
||||
export interface SeparatorProps
|
||||
extends aria.SeparatorProps,
|
||||
twv.VariantProps<typeof SEPARATOR_STYLES> {
|
||||
readonly className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The styles for the {@link Separator} component.
|
||||
*/
|
||||
/** The styles for the {@link Separator} component. */
|
||||
export const SEPARATOR_STYLES = twv.tv({
|
||||
base: 'rounded-full border-none',
|
||||
variants: {
|
||||
@ -79,9 +75,7 @@ export const SEPARATOR_STYLES = twv.tv({
|
||||
],
|
||||
})
|
||||
|
||||
/**
|
||||
* A separator component.
|
||||
*/
|
||||
/** A separator component. */
|
||||
export function Separator(props: SeparatorProps) {
|
||||
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 { TEXT_STYLE } from '../Text'
|
||||
|
||||
/**
|
||||
* Props for the {@Switch} component.
|
||||
*/
|
||||
/** Props for the {@Switch} component. */
|
||||
export interface SwitchProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
|
||||
extends FieldStateProps<
|
||||
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
|
||||
export const Switch = forwardRef(function Switch<
|
||||
Schema extends TSchema,
|
||||
|
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @file Text component
|
||||
*/
|
||||
/** @file Text component */
|
||||
import * as React from 'react'
|
||||
|
||||
import * as aria from '#/components/aria'
|
||||
@ -12,9 +10,7 @@ import { forwardRef } from '#/utilities/react'
|
||||
import * as textProvider from './TextProvider'
|
||||
import * as visualTooltip from './useVisualTooltip'
|
||||
|
||||
/**
|
||||
* Props for the Text component
|
||||
*/
|
||||
/** Props for the Text component */
|
||||
export interface TextProps
|
||||
extends Omit<aria.TextProps, 'color'>,
|
||||
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
|
||||
export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HTMLSpanElement>) {
|
||||
const {
|
||||
@ -216,17 +210,13 @@ export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HT
|
||||
Group: React.FC<React.PropsWithChildren>
|
||||
}
|
||||
|
||||
/**
|
||||
* Heading props
|
||||
*/
|
||||
/** Heading props */
|
||||
export interface HeadingProps extends Omit<TextProps, 'elementType'> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
||||
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
|
||||
const Heading = forwardRef(function Heading(
|
||||
props: HeadingProps,
|
||||
@ -237,9 +227,7 @@ const Heading = forwardRef(function 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) {
|
||||
return (
|
||||
<textProvider.TextProvider value={{ isInsideTextComponent: true }}>
|
||||
|
@ -5,13 +5,9 @@
|
||||
*/
|
||||
import * as React from 'react'
|
||||
|
||||
/**
|
||||
* Context for the Text component.
|
||||
*/
|
||||
/** Context for the Text component. */
|
||||
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
|
||||
}
|
||||
|
||||
@ -19,9 +15,7 @@ const TextContext = React.createContext<TextContextType>({
|
||||
isInsideTextComponent: false,
|
||||
})
|
||||
|
||||
/**
|
||||
* Hook to get the Text context.
|
||||
*/
|
||||
/** Hook to get the Text context. */
|
||||
export function useTextContext(): TextContextType {
|
||||
return React.useContext(TextContext)
|
||||
}
|
||||
|
@ -12,9 +12,7 @@ import * as aria from '#/components/aria'
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
import Portal from '#/components/Portal'
|
||||
|
||||
/**
|
||||
* Props for {@link useVisualTooltip}.
|
||||
*/
|
||||
/** Props for {@link useVisualTooltip}. */
|
||||
export interface VisualTooltipProps
|
||||
extends Pick<ariaComponents.TooltipProps, 'maxWidth' | 'rounded' | 'size' | 'variant'> {
|
||||
readonly children: React.ReactNode
|
||||
@ -41,9 +39,7 @@ export interface VisualTooltipReturn {
|
||||
readonly tooltip: JSX.Element | null
|
||||
}
|
||||
|
||||
/**
|
||||
* The display strategy for the tooltip.
|
||||
*/
|
||||
/** The display strategy for the tooltip. */
|
||||
type DisplayStrategy = 'always' | 'whenOverflowing'
|
||||
|
||||
const DEFAULT_OFFSET = 6
|
||||
|
@ -8,16 +8,12 @@ import * as React from 'react'
|
||||
import { forwardRef } from '#/utilities/react'
|
||||
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 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
|
||||
export const VisuallyHidden = forwardRef(function VisuallyHidden(
|
||||
props: VisuallyHiddenProps,
|
||||
|
@ -4,13 +4,9 @@
|
||||
* 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 {
|
||||
/**
|
||||
* @deprecated Use `testid` instead
|
||||
*/
|
||||
/** @deprecated Use `testid` instead */
|
||||
readonly 'data-testid'?: string | undefined
|
||||
readonly testId?: string | undefined
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ import { tv } from '#/utilities/tailwindVariants'
|
||||
import type { ReactNode } from 'react'
|
||||
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> {
|
||||
readonly children?: ReactNode
|
||||
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) {
|
||||
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 { 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() {
|
||||
const { getText } = textProvider.useText()
|
||||
const { authQueryKey, session } = authProvider.useAuth()
|
||||
|
@ -5,9 +5,7 @@
|
||||
import type { PaywallFeatureName } from '#/hooks/billing'
|
||||
import * as zustand from 'zustand'
|
||||
|
||||
/**
|
||||
* Configuration for a paywall feature.
|
||||
*/
|
||||
/** Configuration for a paywall feature. */
|
||||
export interface PaywallDevtoolsFeatureConfiguration {
|
||||
readonly isForceEnabled: boolean | null
|
||||
}
|
||||
@ -62,9 +60,7 @@ export function useSetEnableVersionChecker() {
|
||||
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() {
|
||||
return zustand.useStore(ensoDevtoolsStore, (state) => ({
|
||||
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 reactQuery from '@tanstack/react-query'
|
||||
|
@ -14,14 +14,10 @@ import * as offlineHooks from '#/hooks/offlineHooks'
|
||||
|
||||
import * as textProvider from '#/providers/TextProvider'
|
||||
|
||||
/**
|
||||
* Props for {@link OfflineNotificationManager}
|
||||
*/
|
||||
/** Props for {@link OfflineNotificationManager} */
|
||||
export type OfflineNotificationManagerProps = Readonly<React.PropsWithChildren>
|
||||
|
||||
/**
|
||||
* Context props for {@link OfflineNotificationManager}
|
||||
*/
|
||||
/** Context props for {@link OfflineNotificationManager} */
|
||||
interface OfflineNotificationManagerContextProps {
|
||||
readonly isNested: boolean
|
||||
readonly toastId?: string
|
||||
@ -30,9 +26,7 @@ interface OfflineNotificationManagerContextProps {
|
||||
const OfflineNotificationManagerContext =
|
||||
React.createContext<OfflineNotificationManagerContextProps>({ isNested: false })
|
||||
|
||||
/**
|
||||
* Offline Notification Manager component.
|
||||
*/
|
||||
/** Offline Notification Manager component. */
|
||||
export function OfflineNotificationManager(props: OfflineNotificationManagerProps) {
|
||||
const { children } = props
|
||||
const toastId = 'offline'
|
||||
|
@ -17,17 +17,13 @@ import ContextMenuEntryBase from '#/components/ContextMenuEntry'
|
||||
|
||||
import * as paywallDialog from './PaywallDialog'
|
||||
|
||||
/**
|
||||
* Props for {@link ContextMenuEntry}.
|
||||
*/
|
||||
/** Props for {@link ContextMenuEntry}. */
|
||||
export interface ContextMenuEntryProps
|
||||
extends Omit<contextMenuEntry.ContextMenuEntryProps, 'doAction' | 'isDisabled'> {
|
||||
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) {
|
||||
const { feature, ...rest } = props
|
||||
const { setModal } = modalProvider.useSetModal()
|
||||
|
@ -16,9 +16,7 @@ import * as ariaComponents from '#/components/AriaComponents'
|
||||
import * as paywall from '#/components/Paywall'
|
||||
import SvgMask from '#/components/SvgMask'
|
||||
|
||||
/**
|
||||
* Props for {@link PaywallAlert}.
|
||||
*/
|
||||
/** Props for {@link PaywallAlert}. */
|
||||
export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'children'> {
|
||||
readonly feature: billingHooks.PaywallFeatureName
|
||||
readonly label: string
|
||||
@ -26,9 +24,7 @@ export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'chil
|
||||
readonly upgradeButtonProps?: Omit<paywall.UpgradeButtonProps, 'feature'>
|
||||
}
|
||||
|
||||
/**
|
||||
* A paywall alert.
|
||||
*/
|
||||
/** A paywall alert. */
|
||||
export function PaywallAlert(props: PaywallAlertProps) {
|
||||
const {
|
||||
label,
|
||||
|
@ -15,16 +15,12 @@ import * as ariaComponents from '#/components/AriaComponents'
|
||||
import * as components from './components'
|
||||
import * as upgradeButton from './UpgradeButton'
|
||||
|
||||
/**
|
||||
* Props for a {@link PaywallDialog}.
|
||||
*/
|
||||
/** Props for a {@link PaywallDialog}. */
|
||||
export interface PaywallDialogProps extends ariaComponents.DialogProps {
|
||||
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) {
|
||||
const { feature, type = 'modal', title, ...dialogProps } = props
|
||||
|
||||
|
@ -11,18 +11,14 @@ import * as ariaComponents from '#/components/AriaComponents'
|
||||
import * as components from './components'
|
||||
import * as paywallDialog from './PaywallDialog'
|
||||
|
||||
/**
|
||||
* Props for a {@link PaywallDialogButton}.
|
||||
*/
|
||||
/** Props for a {@link PaywallDialogButton}. */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export type PaywallDialogButtonProps = components.PaywallButtonProps & {
|
||||
readonly dialogProps?: paywallDialog.PaywallDialogProps
|
||||
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) {
|
||||
const { feature, dialogProps, dialogTriggerProps, ...buttonProps } = props
|
||||
|
||||
|
@ -17,17 +17,13 @@ import * as ariaComponents from '#/components/AriaComponents'
|
||||
import * as components from './components'
|
||||
import * as upgradeButton from './UpgradeButton'
|
||||
|
||||
/**
|
||||
* Props for a {@link PaywallScreen}.
|
||||
*/
|
||||
/** Props for a {@link PaywallScreen}. */
|
||||
export interface PaywallScreenProps {
|
||||
readonly feature: billingHooks.PaywallFeatureName
|
||||
readonly className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A screen that shows a paywall.
|
||||
*/
|
||||
/** A screen that shows a paywall. */
|
||||
export function PaywallScreen(props: PaywallScreenProps) {
|
||||
const { feature, className } = props
|
||||
const { getText } = textProvider.useText()
|
||||
|
@ -13,18 +13,14 @@ import * as textProvider from '#/providers/TextProvider'
|
||||
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
|
||||
/**
|
||||
* Props for an {@link UpgradeButton}.
|
||||
*/
|
||||
/** Props for an {@link UpgradeButton}. */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export type UpgradeButtonProps = Omit<ariaComponents.ButtonProps, 'variant'> & {
|
||||
readonly feature: billingHooks.PaywallFeatureName
|
||||
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) {
|
||||
const {
|
||||
feature,
|
||||
|
@ -16,17 +16,13 @@ import * as textProvider from '#/providers/TextProvider'
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
import SvgMask from '#/components/SvgMask'
|
||||
|
||||
/**
|
||||
* Props for a {@link PaywallBulletPoints}.
|
||||
*/
|
||||
/** Props for a {@link PaywallBulletPoints}. */
|
||||
export interface PaywallBulletPointsProps {
|
||||
readonly bulletPointsTextId: text.TextId
|
||||
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) {
|
||||
const { bulletPointsTextId, className } = props
|
||||
|
||||
|
@ -13,9 +13,7 @@ import * as textProvider from '#/providers/TextProvider'
|
||||
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
|
||||
/**
|
||||
* Props for {@link PaywallButton}.
|
||||
*/
|
||||
/** Props for {@link PaywallButton}. */
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
export type PaywallButtonProps = ariaComponents.ButtonProps & {
|
||||
readonly feature: billingHooks.PaywallFeatureName
|
||||
@ -23,9 +21,7 @@ export type PaywallButtonProps = ariaComponents.ButtonProps & {
|
||||
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) {
|
||||
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 LockIcon from '#/assets/lock.svg'
|
||||
@ -12,17 +10,13 @@ import * as textProvider from '#/providers/TextProvider'
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
import SvgMask from '#/components/SvgMask'
|
||||
|
||||
/**
|
||||
* Props for a {@link PaywallLock}.
|
||||
*/
|
||||
/** Props for a {@link PaywallLock}. */
|
||||
export interface PaywallLockProps {
|
||||
readonly feature: billingHooks.PaywallFeatureName
|
||||
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) {
|
||||
const { feature, className } = props
|
||||
const { getText } = textProvider.useText()
|
||||
|
@ -8,9 +8,7 @@ import invariant from 'tiny-invariant'
|
||||
|
||||
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() {
|
||||
const root = React.useContext(PortalContext)
|
||||
|
||||
@ -29,8 +27,6 @@ export function useStrictPortalContext() {
|
||||
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
|
||||
export const PortalProvider = PortalContext.Provider
|
||||
|
@ -4,9 +4,7 @@
|
||||
*/
|
||||
import type * as React from 'react'
|
||||
|
||||
/**
|
||||
* The props for the Portal component
|
||||
*/
|
||||
/** The props for the Portal component */
|
||||
export interface PortalProps {
|
||||
/**
|
||||
* Ref, where `<Portal />` should render its children
|
||||
@ -19,9 +17,7 @@ export interface PortalProps {
|
||||
* @default false
|
||||
*/
|
||||
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 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. */
|
||||
type StepProp<T> = T | ((props: RenderStepProps) => T)
|
||||
|
||||
/**
|
||||
* Props for {@link Step} component.
|
||||
*/
|
||||
/** Props for {@link Step} component. */
|
||||
export interface StepProps extends RenderStepProps {
|
||||
readonly className?: StepProp<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) {
|
||||
const {
|
||||
index,
|
||||
|
@ -6,18 +6,14 @@ import type { ReactElement, ReactNode } from 'react'
|
||||
import { useStepperContext } from './StepperProvider'
|
||||
import type { RenderChildrenProps } from './types'
|
||||
|
||||
/**
|
||||
* Props for {@link StepContent} component.
|
||||
*/
|
||||
/** Props for {@link StepContent} component. */
|
||||
export interface StepContentProps {
|
||||
readonly index: number
|
||||
readonly children: ReactNode | ((props: RenderChildrenProps) => ReactNode)
|
||||
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 {
|
||||
const { index, children, forceRender = false } = props
|
||||
const { currentStep, goToStep, nextStep, previousStep, totalSteps } = useStepperContext()
|
||||
|
@ -19,9 +19,7 @@ import * as stepperProvider from './StepperProvider'
|
||||
import type { BaseRenderProps, RenderChildrenProps, RenderStepProps } from './types'
|
||||
import * as stepperState from './useStepperState'
|
||||
|
||||
/**
|
||||
* Props for {@link Stepper} component.
|
||||
*/
|
||||
/** Props for {@link Stepper} component. */
|
||||
export interface StepperProps {
|
||||
readonly state: stepperState.StepperState
|
||||
readonly children: React.ReactNode | ((props: RenderChildrenProps) => React.ReactNode)
|
||||
@ -48,9 +46,7 @@ const STEPPER_STYLES = tv({
|
||||
|
||||
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) {
|
||||
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
|
||||
|
||||
/**
|
||||
* Render children of the stepper component.
|
||||
*/
|
||||
/** Render children of the stepper component. */
|
||||
const renderChildren = () => {
|
||||
const renderProps = {
|
||||
currentStep,
|
||||
|
@ -9,9 +9,7 @@ import invariant from 'tiny-invariant'
|
||||
|
||||
import type { StepperState } from './useStepperState'
|
||||
|
||||
/**
|
||||
* StepperProvider props
|
||||
*/
|
||||
/** StepperProvider props */
|
||||
export interface StepperContextType {
|
||||
readonly currentStep: number
|
||||
readonly goToStep: (step: number) => void
|
||||
|
@ -4,9 +4,7 @@
|
||||
* Types for the stepper component.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render props for the stepper component.
|
||||
*/
|
||||
/** Render props for the stepper component. */
|
||||
export interface BaseRenderProps {
|
||||
readonly goToStep: (step: number) => void
|
||||
readonly nextStep: () => void
|
||||
@ -15,21 +13,15 @@ export interface BaseRenderProps {
|
||||
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 {
|
||||
readonly isFirst: boolean
|
||||
readonly isLast: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Render props for lazy rendering of steps.
|
||||
*/
|
||||
/** Render props for lazy rendering of steps. */
|
||||
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 isCurrent: boolean
|
||||
readonly isCompleted: boolean
|
||||
|
@ -9,30 +9,20 @@ import invariant from 'tiny-invariant'
|
||||
|
||||
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
||||
|
||||
/**
|
||||
* Direction of the stepper
|
||||
*/
|
||||
/** Direction of the stepper */
|
||||
type Direction = 'back-none' | 'back' | 'forward-none' | 'forward' | 'initial'
|
||||
|
||||
/**
|
||||
* Props for {@link useStepperState}
|
||||
*/
|
||||
/** Props for {@link useStepperState} */
|
||||
export interface StepperStateProps {
|
||||
/**
|
||||
* The default step to start on (0-indexed)
|
||||
*/
|
||||
/** The default step to start on (0-indexed) */
|
||||
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 onStepChange?: (step: number, direction: 'back' | 'forward') => void
|
||||
readonly onCompleted?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* State for a stepper component
|
||||
*/
|
||||
/** State for a stepper component */
|
||||
export interface StepperState {
|
||||
readonly currentStep: number
|
||||
readonly onStepChange: (step: number) => void
|
||||
@ -43,9 +33,7 @@ export interface StepperState {
|
||||
readonly percentComplete: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of {@link useStepperState}
|
||||
*/
|
||||
/** Result of {@link useStepperState} */
|
||||
export interface UseStepperStateResult {
|
||||
readonly stepperState: StepperState
|
||||
readonly direction: Direction
|
||||
|
@ -18,9 +18,7 @@ import * as result from '#/components/Result'
|
||||
|
||||
import * as loader from './Loader'
|
||||
|
||||
/**
|
||||
* Props for {@link Suspense} component.
|
||||
*/
|
||||
/** Props for {@link Suspense} component. */
|
||||
export interface SuspenseProps extends React.SuspenseProps {
|
||||
readonly loaderProps?: loader.LoaderProps
|
||||
readonly offlineFallback?: React.ReactNode
|
||||
|
@ -7,9 +7,7 @@ import { useInteractOutside } from '#/components/aria'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useEventCallback } from './eventCallbackHooks'
|
||||
|
||||
/**
|
||||
* Props for the {@link useAutoFocus} hook.
|
||||
*/
|
||||
/** Props for the {@link useAutoFocus} hook. */
|
||||
export interface UseAutoFocusProps {
|
||||
readonly ref: React.RefObject<HTMLElement>
|
||||
readonly disabled?: boolean | undefined
|
||||
|
@ -8,9 +8,7 @@ import type * as text from 'enso-common/src/text'
|
||||
|
||||
import * as backend from '#/services/Backend'
|
||||
|
||||
/**
|
||||
* Registered paywall features.
|
||||
*/
|
||||
/** Registered paywall features. */
|
||||
export const PAYWALL_FEATURES = {
|
||||
userGroups: 'userGroups',
|
||||
userGroupsFull: 'userGroupsFull',
|
||||
@ -20,14 +18,10 @@ export const PAYWALL_FEATURES = {
|
||||
shareFull: 'shareFull',
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Paywall features.
|
||||
*/
|
||||
/** Paywall features. */
|
||||
export type PaywallFeatureName = keyof typeof PAYWALL_FEATURES
|
||||
|
||||
/**
|
||||
* Paywall level names
|
||||
*/
|
||||
/** Paywall level names */
|
||||
export type PaywallLevelName = backend.Plan | 'free'
|
||||
|
||||
/**
|
||||
@ -42,9 +36,7 @@ export type PaywallLevelValue =
|
||||
| (2 & { 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> = {
|
||||
free: Object.assign(0, { name: 'free', label: 'freePlanName' } as const),
|
||||
[backend.Plan.solo]: Object.assign(1, {
|
||||
@ -61,14 +53,10 @@ export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = {
|
||||
} as const),
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/** Possible paywall unlock states for a user account. */
|
||||
export type PaywallLevel = (typeof PAYWALL_LEVELS)[keyof typeof PAYWALL_LEVELS]
|
||||
|
||||
/**
|
||||
* Paywall feature labels.
|
||||
*/
|
||||
/** Paywall feature labels. */
|
||||
const PAYWALL_FEATURES_LABELS: Record<PaywallFeatureName, text.TextId> = {
|
||||
userGroups: 'userGroupsFeatureLabel',
|
||||
userGroupsFull: 'userGroupsFullFeatureLabel',
|
||||
@ -88,18 +76,14 @@ const PAYWALL_FEATURE_META = {
|
||||
shareFull: undefined,
|
||||
} satisfies { [K in PaywallFeatureName]: unknown }
|
||||
|
||||
/**
|
||||
* Basic feature configuration.
|
||||
*/
|
||||
/** Basic feature configuration. */
|
||||
interface BasicFeatureConfiguration {
|
||||
readonly level: PaywallLevel
|
||||
readonly bulletPointsTextId: `${PaywallFeatureName}FeatureBulletPoints`
|
||||
readonly descriptionTextId: `${PaywallFeatureName}FeatureDescription`
|
||||
}
|
||||
|
||||
/**
|
||||
* Feature configuration.
|
||||
*/
|
||||
/** Feature configuration. */
|
||||
export type FeatureConfiguration<Key extends PaywallFeatureName = PaywallFeatureName> =
|
||||
BasicFeatureConfiguration & {
|
||||
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 {
|
||||
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 {
|
||||
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>(
|
||||
feature: Key,
|
||||
): FeatureConfiguration<Key> {
|
||||
|
@ -8,9 +8,7 @@ import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
|
||||
|
||||
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() {
|
||||
const getFeature = eventCallbackHooks.useEventCallback(
|
||||
<Key extends paywallFeaturesConfiguration.PaywallFeatureName>(feature: Key) => {
|
||||
|
@ -12,17 +12,13 @@ import type * as backend from '#/services/Backend'
|
||||
import * as paywallConfiguration from './FeaturesConfiguration'
|
||||
import * as paywallFeatures from './paywallFeaturesHooks'
|
||||
|
||||
/**
|
||||
* Props for the {@link usePaywall} hook.
|
||||
*/
|
||||
/** Props for the {@link usePaywall} hook. */
|
||||
export interface UsePaywallProps {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
readonly plan?: backend.Plan | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook that provides paywall-related functionality.
|
||||
*/
|
||||
/** A hook that provides paywall-related functionality. */
|
||||
export function usePaywall(props: UsePaywallProps) {
|
||||
const { plan } = props
|
||||
|
||||
|
@ -13,9 +13,7 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
|
||||
|
||||
import * as textProvider from '#/providers/TextProvider'
|
||||
|
||||
/**
|
||||
* Props for the useCopy hook.
|
||||
*/
|
||||
/** Props for the useCopy hook. */
|
||||
export interface UseCopyProps {
|
||||
readonly copyText: string
|
||||
readonly onCopy?: () => void
|
||||
@ -24,9 +22,7 @@ export interface UseCopyProps {
|
||||
|
||||
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) {
|
||||
const { copyText, onCopy, successToastMessage = true } = props
|
||||
|
||||
|
@ -8,9 +8,7 @@ import * as React from 'react'
|
||||
import * as callbackHooks from './eventCallbackHooks'
|
||||
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>(
|
||||
callback: Fn,
|
||||
deps: React.DependencyList,
|
||||
@ -83,9 +81,8 @@ export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>(
|
||||
}, [stableCallback, delay, maxWait, ...deps])
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export interface DebouncedFunction<Fn extends (...args: never[]) => unknown> {
|
||||
(this: ThisParameterType<Fn>, ...args: Parameters<Fn>): void
|
||||
}
|
||||
/** The type of a wrapped function that has been debounced. */
|
||||
export type DebouncedFunction<Fn extends (...args: never[]) => unknown> = (
|
||||
this: ThisParameterType<Fn>,
|
||||
...args: Parameters<Fn>
|
||||
) => void
|
||||
|
@ -9,9 +9,7 @@ import * as React from 'react'
|
||||
import * as debouncedCallback from './debounceCallbackHooks'
|
||||
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>(
|
||||
initialState: S | (() => S),
|
||||
delay: number,
|
||||
|
@ -5,9 +5,7 @@
|
||||
*/
|
||||
import * as debounceState from './debounceStateHooks'
|
||||
|
||||
/**
|
||||
* Debounce a value.
|
||||
*/
|
||||
/** Debounce a value. */
|
||||
export function useDebounceValue<T>(value: T, delay: number, maxWait?: number) {
|
||||
const [debouncedValue, setDebouncedValue] = debounceState.useDebounceState(value, delay, maxWait)
|
||||
|
||||
|
@ -34,9 +34,7 @@ export function useMounted(callback: () => void) {
|
||||
}, [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() {
|
||||
const isFirstMount = useRef(true)
|
||||
const stableCallbackTrue = useEventCallback(() => true)
|
||||
|
@ -9,9 +9,7 @@ import * as reactQuery from '@tanstack/react-query'
|
||||
|
||||
import * as eventCallback from '#/hooks/eventCallbackHooks'
|
||||
|
||||
/**
|
||||
* Hook to get the offline status
|
||||
*/
|
||||
/** Hook to get the offline status */
|
||||
export function useOffline() {
|
||||
const isOnline = React.useSyncExternalStore(
|
||||
reactQuery.onlineManager.subscribe.bind(reactQuery.onlineManager),
|
||||
@ -22,17 +20,13 @@ export function useOffline() {
|
||||
return { isOffline: !isOnline }
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the {@link useOfflineChange} hook
|
||||
*/
|
||||
/** Props for the {@link useOfflineChange} hook */
|
||||
export interface UseOfflineChangeProps {
|
||||
readonly triggerImmediate?: boolean | 'if-offline' | 'if-online'
|
||||
readonly isDisabled?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to subscribe to online/offline changes
|
||||
*/
|
||||
/** Hook to subscribe to online/offline changes */
|
||||
export function useOfflineChange(
|
||||
callback: (isOffline: boolean) => void,
|
||||
props: UseOfflineChangeProps = {},
|
||||
|
@ -18,9 +18,7 @@ import * as safeJsonParse from '#/utilities/safeJsonParse'
|
||||
// === SearchParamsStateReturnType ===
|
||||
// ===================================
|
||||
|
||||
/**
|
||||
* The return type of the `useSearchParamsState` hook.
|
||||
*/
|
||||
/** The return type of the `useSearchParamsState` hook. */
|
||||
type SearchParamsStateReturnType<T> = Readonly<
|
||||
[
|
||||
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 {
|
||||
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