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:
somebody1234 2024-10-21 22:56:39 +10:00 committed by GitHub
parent 45ad3a751c
commit 5faddf52f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
231 changed files with 2204 additions and 1726 deletions

View File

@ -26,7 +26,8 @@
"./src/types": "./src/types.d.ts" "./src/types": "./src/types.d.ts"
}, },
"scripts": { "scripts": {
"test": "vitest run" "test": "vitest run",
"lint": "eslint . --max-warnings=0"
}, },
"peerDependencies": { "peerDependencies": {
"@tanstack/query-core": "5.54.1", "@tanstack/query-core": "5.54.1",

View File

@ -4,17 +4,19 @@
// === AccessToken === // === AccessToken ===
// =================== // ===================
/** Payload for the `save-access-token` event that saves an access token to a credentials file. */ /** Credentials to be saved to a credentials file. */
export interface AccessToken { export interface AccessToken {
/** The JWT token to save. */ /** The user's JWT token. */
readonly accessToken: string readonly accessToken: string
/** The Cognito app integration client id. */ /** The ID for the Cognito app integration. */
readonly clientId: string readonly clientId: string
/** The refresh token taken from authorization flow. */ /** The refresh token taken from the authorization flow. */
readonly refreshToken: string readonly refreshToken: string
/** The Cognito url to refresh the token. */ /** The Cognito URL to refresh the token. */
readonly refreshUrl: string readonly refreshUrl: string
/** The when the token will expire. /**
* This is a string representation of a date in ISO 8601 format (e.g. "2021-01-01T00:00:00Z"). */ * The expiry time of the token.
* This is a string representation of a date in ISO 8601 format (e.g. "2021-01-01T00:00:00Z").
*/
readonly expireAt: string readonly expireAt: string
} }

View File

@ -1,17 +1,21 @@
/** @file Functions for managing app configuration. */ /** @file Functions for managing app configuration. */
/** Read environment variables from a file based on the `ENSO_CLOUD_ENV_FILE_NAME` /**
* Read environment variables from a file based on the `ENSO_CLOUD_ENV_FILE_NAME`
* environment variable. Reads from `.env` if the variable is blank or absent. * environment variable. Reads from `.env` if the variable is blank or absent.
* DOES NOT override existing environment variables if the variable is absent. */ * DOES NOT override existing environment variables if the variable is absent.
*/
export function readEnvironmentFromFile(): Promise<void> export function readEnvironmentFromFile(): Promise<void>
/** An object containing app configuration to inject. /**
* An object containing app configuration to inject.
* *
* This includes: * This includes:
* - the base URL for backend endpoints * - the base URL for backend endpoints
* - the WebSocket URL for the chatbot * - the WebSocket URL for the chatbot
* - the unique identifier for the cloud environment, for use in Sentry logs * - the unique identifier for the cloud environment, for use in Sentry logs
* - Stripe, Sentry and Amplify public keys */ * - Stripe, Sentry and Amplify public keys
*/
export function getDefines(serverPort?: number): Record<string, string> export function getDefines(serverPort?: number): Record<string, string>
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */ /** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */

View File

@ -1,6 +1,7 @@
/** @file Functions for managing app configuration. */ /** @file Functions for managing app configuration. */
import * as fs from 'node:fs/promises' import * as fs from 'node:fs/promises'
import * as path from 'node:path' import * as path from 'node:path'
import * as process from 'node:process'
import * as url from 'node:url' import * as url from 'node:url'
// =============================== // ===============================
@ -19,14 +20,13 @@ export async function readEnvironmentFromFile() {
const filePath = path.join(url.fileURLToPath(new URL('../..', import.meta.url)), fileName) const filePath = path.join(url.fileURLToPath(new URL('../..', import.meta.url)), fileName)
const buildInfo = await (async () => { const buildInfo = await (async () => {
try { try {
return await import('../../../../build.json', { with: { type: 'json' } }) return await import('../../../build.json', { with: { type: 'json' } })
} catch { } catch {
return { commit: '', version: '', engineVersion: '', name: '' } return { commit: '', version: '', engineVersion: '', name: '' }
} }
})() })()
try { try {
const file = await fs.readFile(filePath, { encoding: 'utf-8' }) const file = await fs.readFile(filePath, { encoding: 'utf-8' })
// eslint-disable-next-line jsdoc/valid-types
/** @type {readonly (readonly [string, string])[]} */ /** @type {readonly (readonly [string, string])[]} */
let entries = file.split('\n').flatMap(line => { let entries = file.split('\n').flatMap(line => {
if (/^\s*$|^.s*#/.test(line)) { if (/^\s*$|^.s*#/.test(line)) {
@ -47,14 +47,10 @@ export async function readEnvironmentFromFile() {
if (!isProduction || entries.length > 0) { if (!isProduction || entries.length > 0) {
Object.assign(process.env, variables) Object.assign(process.env, variables)
} }
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
} catch (error) { } catch (error) {
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version process.env.ENSO_CLOUD_DASHBOARD_VERSION ??= buildInfo.version
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit process.env.ENSO_CLOUD_DASHBOARD_COMMIT_HASH ??= buildInfo.commit
const expectedKeys = Object.keys(DUMMY_DEFINES) const expectedKeys = Object.keys(DUMMY_DEFINES)
.map(key => key.replace(/^process[.]env[.]/, '')) .map(key => key.replace(/^process[.]env[.]/, ''))
@ -147,7 +143,6 @@ const DUMMY_DEFINES = {
/** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */ /** Load test environment variables, useful for when the Cloud backend is mocked or unnecessary. */
export function loadTestEnvironmentVariables() { export function loadTestEnvironmentVariables() {
for (const [k, v] of Object.entries(DUMMY_DEFINES)) { for (const [k, v] of Object.entries(DUMMY_DEFINES)) {
// @ts-expect-error This is the only file where `process.env` should be written to.
process.env[k.replace(/^process[.]env[.]/, '')] = v process.env[k.replace(/^process[.]env[.]/, '')] = v
} }
} }

View File

@ -3,26 +3,34 @@
/** Indent size for stringifying JSON. */ /** Indent size for stringifying JSON. */
export const INDENT_SIZE: number export const INDENT_SIZE: number
/** Get the environment variable value. /**
* Get the environment variable value.
* @param name - The name of the environment variable. * @param name - The name of the environment variable.
* @returns The value of the environment variable. * @returns The value of the environment variable.
* @throws {Error} If the environment variable is not set. */ * @throws {Error} If the environment variable is not set.
*/
export function requireEnv(name: string): string export function requireEnv(name: string): string
/** Read the path from environment variable and resolve it. /**
* Read the path from environment variable and resolve it.
* @param name - The name of the environment variable. * @param name - The name of the environment variable.
* @returns The resolved path. * @returns The resolved path.
* @throws {Error} If the environment variable is not set. */ * @throws {Error} If the environment variable is not set.
*/
export function requireEnvResolvedPath(name: string): string export function requireEnvResolvedPath(name: string): string
/** Read the path from environment variable and resolve it. Verify that it exists. /**
* Read the path from environment variable and resolve it. Verify that it exists.
* @param name - The name of the environment variable. * @param name - The name of the environment variable.
* @returns The resolved path. * @returns The resolved path.
* @throws {Error} If the environment variable is not set or path does not exist. */ * @throws {Error} If the environment variable is not set or path does not exist.
*/
export function requireEnvPathExist(name: string): string export function requireEnvPathExist(name: string): string
/** Get the common prefix of the two strings. /**
* Get the common prefix of the two strings.
* @param a - the first string. * @param a - the first string.
* @param b - the second string. * @param b - the second string.
* @returns The common prefix. */ * @returns The common prefix.
*/
export function getCommonPrefix(a: string, b: string): string export function getCommonPrefix(a: string, b: string): string

View File

@ -23,8 +23,10 @@ export enum Platform {
android = 'Android', android = 'Android',
} }
/** The platform the app is currently running on. /**
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts. */ * The platform the app is currently running on.
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts.
*/
export function platform() { export function platform() {
if (isOnWindowsPhone()) { if (isOnWindowsPhone()) {
// MUST be before Android and Windows. // MUST be before Android and Windows.
@ -96,8 +98,10 @@ export enum Browser {
opera = 'Opera', opera = 'Opera',
} }
/** Return the platform the app is currently running on. /**
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts. */ * Return the platform the app is currently running on.
* This is used to determine whether `metaKey` or `ctrlKey` is used in shortcuts.
*/
export function browser(): Browser { export function browser(): Browser {
if (isOnElectron()) { if (isOnElectron()) {
return Browser.electron return Browser.electron
@ -117,10 +121,12 @@ export function browser(): Browser {
return Browser.unknown return Browser.unknown
} }
} }
/** Returns `true` if running in Electron, else `false`. /**
* Returns `true` if running in Electron, else `false`.
* This is used to determine whether to use a `MemoryRouter` (stores history in an array) * This is used to determine whether to use a `MemoryRouter` (stores history in an array)
* or a `BrowserRouter` (stores history in the path of the URL). * or a `BrowserRouter` (stores history in the path of the URL).
* It is also used to determine whether to send custom state to Amplify for a workaround. */ * It is also used to determine whether to send custom state to Amplify for a workaround.
*/
export function isOnElectron() { export function isOnElectron() {
return /electron/i.test(navigator.userAgent) return /electron/i.test(navigator.userAgent)
} }

View File

@ -17,7 +17,7 @@ window.dataLayer = window.dataLayer || []
export function gtag(_action: 'config' | 'event' | 'js' | 'set', ..._args: unknown[]) { export function gtag(_action: 'config' | 'event' | 'js' | 'set', ..._args: unknown[]) {
// @ts-expect-error This is explicitly not given types as it is a mistake to acess this // @ts-expect-error This is explicitly not given types as it is a mistake to acess this
// anywhere else. // anywhere else.
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, prefer-rest-params
window.dataLayer.push(arguments) window.dataLayer.push(arguments)
} }
@ -27,7 +27,7 @@ export function event(name: string, params?: object) {
} }
gtag('js', new Date()) gtag('js', new Date())
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase
gtag('set', 'linker', { accept_incoming: true }) gtag('set', 'linker', { accept_incoming: true })
gtag('config', GOOGLE_ANALYTICS_TAG) gtag('config', GOOGLE_ANALYTICS_TAG)
if (GOOGLE_ANALYTICS_TAG === 'G-CLTBJ37MDM') { if (GOOGLE_ANALYTICS_TAG === 'G-CLTBJ37MDM') {

View File

@ -1,18 +1,22 @@
/** @file This module contains metadata about the product and distribution, /**
* @file This module contains metadata about the product and distribution,
* and various other constants that are needed in multiple sibling packages. * and various other constants that are needed in multiple sibling packages.
* *
* Code in this package is used by two or more sibling packages of this package. The code is defined * Code in this package is used by two or more sibling packages of this package. The code is defined
* here when it is not possible for a sibling package to own that code without introducing a * here when it is not possible for a sibling package to own that code without introducing a
* circular dependency in our packages. */ * circular dependency in our packages.
*/
// ======================== // ========================
// === Product metadata === // === Product metadata ===
// ======================== // ========================
/** URL protocol scheme for deep links to authentication flow pages, without the `:` suffix. /**
* URL protocol scheme for deep links to authentication flow pages, without the `:` suffix.
* *
* For example: the deep link URL * For example: the deep link URL
* `enso://authentication/register?code=...&state=...` uses this scheme. */ * `enso://authentication/register?code=...&state=...` uses this scheme.
*/
export const DEEP_LINK_SCHEME: string export const DEEP_LINK_SCHEME: string
/** Name of the product. */ /** Name of the product. */
@ -21,12 +25,16 @@ export const PRODUCT_NAME: string
/** Company name, used as the copyright holder. */ /** Company name, used as the copyright holder. */
export const COMPANY_NAME: string export const COMPANY_NAME: string
/** The domain on which the Cloud Dashboard web app is hosted. /**
* Excludes the protocol (`https://`). */ * The domain on which the Cloud Dashboard web app is hosted.
* Excludes the protocol (`https://`).
*/
export const CLOUD_DASHBOARD_DOMAIN: string export const CLOUD_DASHBOARD_DOMAIN: string
/** COOP, COEP, and CORP headers: https://web.dev/coop-coep/ /**
* COOP, COEP, and CORP headers: https://web.dev/coop-coep/
* *
* These are required to increase the resolution of `performance.now()` timers, * These are required to increase the resolution of `performance.now()` timers,
* making profiling a lot more accurate and consistent. */ * making profiling a lot more accurate and consistent.
*/
export const COOP_COEP_CORP_HEADERS: [header: string, value: string][] export const COOP_COEP_CORP_HEADERS: [header: string, value: string][]

View File

@ -10,9 +10,7 @@ import * as vueQuery from '@tanstack/vue-query'
import * as idbKeyval from 'idb-keyval' import * as idbKeyval from 'idb-keyval'
declare module '@tanstack/query-core' { declare module '@tanstack/query-core' {
/** /** Query client with additional methods. */
* Query client with additional methods.
*/
interface QueryClient { interface QueryClient {
/** /**
* Clear the cache stored in Tanstack Query and the persister storage. * Clear the cache stored in Tanstack Query and the persister storage.
@ -20,19 +18,13 @@ declare module '@tanstack/query-core' {
* Usually you should use `queryClient.invalidateQueries` instead. * Usually you should use `queryClient.invalidateQueries` instead.
*/ */
readonly clearWithPersister: () => Promise<void> readonly clearWithPersister: () => Promise<void>
/** /** Clear the cache stored in the persister storage. */
* Clear the cache stored in the persister storage.
*/
readonly nukePersister: () => Promise<void> readonly nukePersister: () => Promise<void>
} }
/** /** Specifies the invalidation behavior of a mutation. */
* Specifies the invalidation behavior of a mutation.
*/
interface Register { interface Register {
readonly mutationMeta: { readonly mutationMeta: {
/** /** List of query keys to invalidate when the mutation succeeds. */
* List of query keys to invalidate when the mutation succeeds.
*/
readonly invalidates?: queryCore.QueryKey[] readonly invalidates?: queryCore.QueryKey[]
/** /**
* List of query keys to await invalidation before the mutation is considered successful. * List of query keys to await invalidation before the mutation is considered successful.
@ -71,9 +63,7 @@ const DEFAULT_QUERY_PERSIST_TIME_MS = 30 * 24 * 60 * 60 * 1000 // 30 days
const DEFAULT_BUSTER = 'v1.1' const DEFAULT_BUSTER = 'v1.1'
/** /** Create a new Tanstack Query client. */
* Create a new Tanstack Query client.
*/
export function createQueryClient(): QueryClient { export function createQueryClient(): QueryClient {
const store = idbKeyval.createStore('enso', 'query-persist-cache') const store = idbKeyval.createStore('enso', 'query-persist-cache')
queryCore.onlineManager.setOnline(navigator.onLine) queryCore.onlineManager.setOnline(navigator.onLine)

View File

@ -29,8 +29,10 @@ export const UserGroupId = newtype.newtypeConstructor<UserGroupId>()
export type DirectoryId = newtype.Newtype<string, 'DirectoryId'> export type DirectoryId = newtype.Newtype<string, 'DirectoryId'>
export const DirectoryId = newtype.newtypeConstructor<DirectoryId>() export const DirectoryId = newtype.newtypeConstructor<DirectoryId>()
/** Unique identifier for an asset representing the items inside a directory for which the /**
* request to retrive the items has not yet completed. */ * Unique identifier for an asset representing the items inside a directory for which the
* request to retrive the items has not yet completed.
*/
export type LoadingAssetId = newtype.Newtype<string, 'LoadingAssetId'> export type LoadingAssetId = newtype.Newtype<string, 'LoadingAssetId'>
export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>() export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
@ -38,8 +40,10 @@ export const LoadingAssetId = newtype.newtypeConstructor<LoadingAssetId>()
export type EmptyAssetId = newtype.Newtype<string, 'EmptyAssetId'> export type EmptyAssetId = newtype.Newtype<string, 'EmptyAssetId'>
export const EmptyAssetId = newtype.newtypeConstructor<EmptyAssetId>() export const EmptyAssetId = newtype.newtypeConstructor<EmptyAssetId>()
/** Unique identifier for an asset representing the nonexistent children of a directory /**
* that failed to fetch. */ * Unique identifier for an asset representing the nonexistent children of a directory
* that failed to fetch.
*/
export type ErrorAssetId = newtype.Newtype<string, 'ErrorAssetId'> export type ErrorAssetId = newtype.Newtype<string, 'ErrorAssetId'>
export const ErrorAssetId = newtype.newtypeConstructor<ErrorAssetId>() export const ErrorAssetId = newtype.newtypeConstructor<ErrorAssetId>()
@ -74,9 +78,7 @@ export type AssetId = IdType[keyof IdType]
export type CheckoutSessionId = newtype.Newtype<string, 'CheckoutSessionId'> export type CheckoutSessionId = newtype.Newtype<string, 'CheckoutSessionId'>
export const CheckoutSessionId = newtype.newtypeConstructor<CheckoutSessionId>() export const CheckoutSessionId = newtype.newtypeConstructor<CheckoutSessionId>()
/** /** Unique identifier for a subscription. */
* Unique identifier for a subscription.
*/
export type SubscriptionId = newtype.Newtype<string, 'SubscriptionId'> export type SubscriptionId = newtype.Newtype<string, 'SubscriptionId'>
export const SubscriptionId = newtype.newtypeConstructor<SubscriptionId>() export const SubscriptionId = newtype.newtypeConstructor<SubscriptionId>()
@ -129,14 +131,18 @@ export function isUserGroupId(id: string): id is UserGroupId {
const PLACEHOLDER_USER_GROUP_PREFIX = 'usergroup-placeholder-' const PLACEHOLDER_USER_GROUP_PREFIX = 'usergroup-placeholder-'
/** Whether a given {@link UserGroupId} represents a user group that does not yet exist on the /**
* server. */ * Whether a given {@link UserGroupId} represents a user group that does not yet exist on the
* server.
*/
export function isPlaceholderUserGroupId(id: string) { export function isPlaceholderUserGroupId(id: string) {
return id.startsWith(PLACEHOLDER_USER_GROUP_PREFIX) return id.startsWith(PLACEHOLDER_USER_GROUP_PREFIX)
} }
/** Return a new {@link UserGroupId} that represents a placeholder user group that is yet to finish /**
* being created on the backend. */ * Return a new {@link UserGroupId} that represents a placeholder user group that is yet to finish
* being created on the backend.
*/
export function newPlaceholderUserGroupId() { export function newPlaceholderUserGroupId() {
return UserGroupId(`${PLACEHOLDER_USER_GROUP_PREFIX}${uniqueString.uniqueString()}`) return UserGroupId(`${PLACEHOLDER_USER_GROUP_PREFIX}${uniqueString.uniqueString()}`)
} }
@ -153,17 +159,21 @@ export enum BackendType {
/** Metadata uniquely identifying a user inside an organization. */ /** Metadata uniquely identifying a user inside an organization. */
export interface UserInfo { export interface UserInfo {
/** The ID of the parent organization. If this is a sole user, they are implicitly in an /**
* organization consisting of only themselves. */ * The ID of the parent organization. If this is a sole user, they are implicitly in an
* organization consisting of only themselves.
*/
readonly organizationId: OrganizationId readonly organizationId: OrganizationId
/** The name of the parent organization. */ /** The name of the parent organization. */
readonly organizationName?: string readonly organizationName?: string
/** The ID of this user. /**
* The ID of this user.
* *
* The user ID is globally unique. Thus, the user ID is always sufficient to uniquely identify a * The user ID is globally unique. Thus, the user ID is always sufficient to uniquely identify a
* user. The user ID is guaranteed to never change, once assigned. For these reasons, the user ID * user. The user ID is guaranteed to never change, once assigned. For these reasons, the user ID
* should be the preferred way to uniquely refer to a user. That is, when referring to a user, * should be the preferred way to uniquely refer to a user. That is, when referring to a user,
* prefer this field over `name`, `email`, `subject`, or any other mechanism, where possible. */ * prefer this field over `name`, `email`, `subject`, or any other mechanism, where possible.
*/
readonly userId: UserId readonly userId: UserId
readonly name: string readonly name: string
readonly email: EmailAddress readonly email: EmailAddress
@ -173,8 +183,10 @@ export interface UserInfo {
/** A user in the application. These are the primary owners of a project. */ /** A user in the application. These are the primary owners of a project. */
export interface User extends UserInfo { export interface User extends UserInfo {
/** If `false`, this account is awaiting acceptance from an administrator, and endpoints other than /**
* `usersMe` will not work. */ * If `false`, this account is awaiting acceptance from an administrator, and endpoints other than
* `usersMe` will not work.
*/
readonly isEnabled: boolean readonly isEnabled: boolean
readonly isOrganizationAdmin: boolean readonly isOrganizationAdmin: boolean
readonly rootDirectoryId: DirectoryId readonly rootDirectoryId: DirectoryId
@ -200,11 +212,15 @@ export enum ProjectState {
provisioned = 'Provisioned', provisioned = 'Provisioned',
opened = 'Opened', opened = 'Opened',
closed = 'Closed', closed = 'Closed',
/** A frontend-specific state, representing a project that should be displayed as /**
* `openInProgress`, but has not yet been added to the backend. */ * A frontend-specific state, representing a project that should be displayed as
* `openInProgress`, but has not yet been added to the backend.
*/
placeholder = 'Placeholder', placeholder = 'Placeholder',
/** A frontend-specific state, representing a project that should be displayed as `closed`, /**
* but is still in the process of shutting down. */ * A frontend-specific state, representing a project that should be displayed as `closed`,
* but is still in the process of shutting down.
*/
closing = 'Closing', closing = 'Closing',
} }
@ -374,11 +390,13 @@ export interface Label {
readonly color: LChColor readonly color: LChColor
} }
/** Type of application that a {@link Version} applies to. /**
* Type of application that a {@link Version} applies to.
* *
* We keep track of both backend and IDE versions, so that we can update the two independently. * We keep track of both backend and IDE versions, so that we can update the two independently.
* However the format of the version numbers is the same for both, so we can use the same type for * However the format of the version numbers is the same for both, so we can use the same type for
* both. We just need this enum to disambiguate. */ * both. We just need this enum to disambiguate.
*/
export enum VersionType { export enum VersionType {
backend = 'Backend', backend = 'Backend',
ide = 'Ide', ide = 'Ide',
@ -458,9 +476,7 @@ export interface ResourceUsage {
readonly storage: number readonly storage: number
} }
/** /** Metadata for a subscription. */
* Metadata for a subscription.
*/
export interface Subscription { export interface Subscription {
readonly id?: SubscriptionId readonly id?: SubscriptionId
readonly plan?: Plan readonly plan?: Plan
@ -566,7 +582,7 @@ export interface UpdatedDirectory {
} }
/** The type returned from the "create directory" endpoint. */ /** The type returned from the "create directory" endpoint. */
export interface Directory extends DirectoryAsset {} export type Directory = DirectoryAsset
/** The subset of asset fields returned by the "copy asset" endpoint. */ /** The subset of asset fields returned by the "copy asset" endpoint. */
export interface CopiedAsset { export interface CopiedAsset {
@ -711,8 +727,10 @@ export enum AssetType {
secret = 'secret', secret = 'secret',
datalink = 'datalink', datalink = 'datalink',
directory = 'directory', directory = 'directory',
/** A special {@link AssetType} representing the unknown items of a directory, before the /**
* request to retrieve the items completes. */ * A special {@link AssetType} representing the unknown items of a directory, before the
* request to retrieve the items completes.
*/
specialLoading = 'specialLoading', specialLoading = 'specialLoading',
/** A special {@link AssetType} representing a directory listing that is empty. */ /** A special {@link AssetType} representing a directory listing that is empty. */
specialEmpty = 'specialEmpty', specialEmpty = 'specialEmpty',
@ -732,8 +750,10 @@ export interface IdType {
readonly [AssetType.specialError]: ErrorAssetId readonly [AssetType.specialError]: ErrorAssetId
} }
/** Integers (starting from 0) corresponding to the order in which each asset type should appear /**
* in a directory listing. */ * Integers (starting from 0) corresponding to the order in which each asset type should appear
* in a directory listing.
*/
export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = { export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
// This is a sequence of numbers, not magic numbers. `1000` is an arbitrary number // This is a sequence of numbers, not magic numbers. `1000` is an arbitrary number
// that are higher than the number of possible asset types. // that are higher than the number of possible asset types.
@ -753,22 +773,28 @@ export const ASSET_TYPE_ORDER: Readonly<Record<AssetType, number>> = {
// === Asset === // === Asset ===
// ============= // =============
/** Metadata uniquely identifying a directory entry. /**
* These can be Projects, Files, Secrets, or other directories. */ * Metadata uniquely identifying a directory entry.
* These can be Projects, Files, Secrets, or other directories.
*/
export interface BaseAsset { export interface BaseAsset {
readonly id: AssetId readonly id: AssetId
readonly title: string readonly title: string
readonly modifiedAt: dateTime.Rfc3339DateTime readonly modifiedAt: dateTime.Rfc3339DateTime
/** This is defined as a generic {@link AssetId} in the backend, however it is more convenient /**
* (and currently safe) to assume it is always a {@link DirectoryId}. */ * This is defined as a generic {@link AssetId} in the backend, however it is more convenient
* (and currently safe) to assume it is always a {@link DirectoryId}.
*/
readonly parentId: DirectoryId readonly parentId: DirectoryId
readonly permissions: readonly AssetPermission[] | null readonly permissions: readonly AssetPermission[] | null
readonly labels: readonly LabelName[] | null readonly labels: readonly LabelName[] | null
readonly description: string | null readonly description: string | null
} }
/** Metadata uniquely identifying a directory entry. /**
* These can be Projects, Files, Secrets, or other directories. */ * Metadata uniquely identifying a directory entry.
* These can be Projects, Files, Secrets, or other directories.
*/
export interface Asset<Type extends AssetType = AssetType> extends BaseAsset { export interface Asset<Type extends AssetType = AssetType> extends BaseAsset {
readonly type: Type readonly type: Type
readonly id: IdType[Type] readonly id: IdType[Type]
@ -776,31 +802,33 @@ export interface Asset<Type extends AssetType = AssetType> extends BaseAsset {
} }
/** A convenience alias for {@link Asset}<{@link AssetType.directory}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.directory}>. */
export interface DirectoryAsset extends Asset<AssetType.directory> {} export type DirectoryAsset = Asset<AssetType.directory>
/** A convenience alias for {@link Asset}<{@link AssetType.project}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.project}>. */
export interface ProjectAsset extends Asset<AssetType.project> {} export type ProjectAsset = Asset<AssetType.project>
/** A convenience alias for {@link Asset}<{@link AssetType.file}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.file}>. */
export interface FileAsset extends Asset<AssetType.file> {} export type FileAsset = Asset<AssetType.file>
/** A convenience alias for {@link Asset}<{@link AssetType.datalink}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.datalink}>. */
export interface DatalinkAsset extends Asset<AssetType.datalink> {} export type DatalinkAsset = Asset<AssetType.datalink>
/** A convenience alias for {@link Asset}<{@link AssetType.secret}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.secret}>. */
export interface SecretAsset extends Asset<AssetType.secret> {} export type SecretAsset = Asset<AssetType.secret>
/** A convenience alias for {@link Asset}<{@link AssetType.specialLoading}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.specialLoading}>. */
export interface SpecialLoadingAsset extends Asset<AssetType.specialLoading> {} export type SpecialLoadingAsset = Asset<AssetType.specialLoading>
/** A convenience alias for {@link Asset}<{@link AssetType.specialEmpty}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.specialEmpty}>. */
export interface SpecialEmptyAsset extends Asset<AssetType.specialEmpty> {} export type SpecialEmptyAsset = Asset<AssetType.specialEmpty>
/** A convenience alias for {@link Asset}<{@link AssetType.specialError}>. */ /** A convenience alias for {@link Asset}<{@link AssetType.specialError}>. */
export interface SpecialErrorAsset extends Asset<AssetType.specialError> {} export type SpecialErrorAsset = Asset<AssetType.specialError>
/** Creates a {@link DirectoryAsset} representing the root directory for the organization, /**
* with all irrelevant fields initialized to default values. */ * Creates a {@link DirectoryAsset} representing the root directory for the organization,
* with all irrelevant fields initialized to default values.
*/
export function createRootDirectoryAsset(directoryId: DirectoryId): DirectoryAsset { export function createRootDirectoryAsset(directoryId: DirectoryId): DirectoryAsset {
return { return {
type: AssetType.directory, type: AssetType.directory,
@ -860,8 +888,10 @@ export function createPlaceholderProjectAsset(
} }
} }
/** Creates a {@link SpecialLoadingAsset}, with all irrelevant fields initialized to default /**
* values. */ * Creates a {@link SpecialLoadingAsset}, with all irrelevant fields initialized to default
* values.
*/
export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoadingAsset { export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoadingAsset {
return { return {
type: AssetType.specialLoading, type: AssetType.specialLoading,
@ -876,8 +906,10 @@ export function createSpecialLoadingAsset(directoryId: DirectoryId): SpecialLoad
} }
} }
/** Creates a {@link SpecialEmptyAsset}, with all irrelevant fields initialized to default /**
* values. */ * Creates a {@link SpecialEmptyAsset}, with all irrelevant fields initialized to default
* values.
*/
export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyAsset { export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyAsset {
return { return {
type: AssetType.specialEmpty, type: AssetType.specialEmpty,
@ -892,8 +924,10 @@ export function createSpecialEmptyAsset(directoryId: DirectoryId): SpecialEmptyA
} }
} }
/** Creates a {@link SpecialErrorAsset}, with all irrelevant fields initialized to default /**
* values. */ * Creates a {@link SpecialErrorAsset}, with all irrelevant fields initialized to default
* values.
*/
export function createSpecialErrorAsset(directoryId: DirectoryId): SpecialErrorAsset { export function createSpecialErrorAsset(directoryId: DirectoryId): SpecialErrorAsset {
return { return {
type: AssetType.specialError, type: AssetType.specialError,
@ -1011,8 +1045,10 @@ export interface AssetVersions {
// === compareAssetPermissions === // === compareAssetPermissions ===
// =============================== // ===============================
/** Return a positive number when `a > b`, a negative number when `a < b`, and `0` /**
* when `a === b`. */ * Return a positive number when `a > b`, a negative number when `a < b`, and `0`
* when `a === b`.
*/
export function compareAssetPermissions(a: AssetPermission, b: AssetPermission) { export function compareAssetPermissions(a: AssetPermission, b: AssetPermission) {
const relativePermissionPrecedence = const relativePermissionPrecedence =
permissions.PERMISSION_ACTION_PRECEDENCE[a.permission] - permissions.PERMISSION_ACTION_PRECEDENCE[a.permission] -
@ -1126,8 +1162,10 @@ export interface CreateProjectRequestBody {
readonly datalinkId?: DatalinkId readonly datalinkId?: DatalinkId
} }
/** HTTP request body for the "update project" endpoint. /**
* Only updates of the `projectName` or `ami` are allowed. */ * HTTP request body for the "update project" endpoint.
* Only updates of the `projectName` or `ami` are allowed.
*/
export interface UpdateProjectRequestBody { export interface UpdateProjectRequestBody {
readonly projectName: string | null readonly projectName: string | null
readonly ami: Ami | null readonly ami: Ami | null
@ -1256,8 +1294,10 @@ export function compareAssets(a: AnyAsset, b: AnyAsset) {
// === getAssetId === // === getAssetId ===
// ================== // ==================
/** A convenience function to get the `id` of an {@link Asset}. /**
* This is useful to avoid React re-renders as it is not re-created on each function call. */ * A convenience function to get the `id` of an {@link Asset}.
* This is useful to avoid React re-renders as it is not re-created on each function call.
*/
export function getAssetId<Type extends AssetType>(asset: Asset<Type>) { export function getAssetId<Type extends AssetType>(asset: Asset<Type>) {
return asset.id return asset.id
} }
@ -1313,16 +1353,16 @@ export function stripProjectExtension(name: string) {
return name.replace(/[.](?:tar[.]gz|zip|enso-project)$/, '') return name.replace(/[.](?:tar[.]gz|zip|enso-project)$/, '')
} }
/** Return both the name and extension of the project file name (if any). /**
* Otherwise, returns the entire name as the basename. */ * Return both the name and extension of the project file name (if any).
* Otherwise, returns the entire name as the basename.
*/
export function extractProjectExtension(name: string) { export function extractProjectExtension(name: string) {
const [, basename, extension] = name.match(/^(.*)[.](tar[.]gz|zip|enso-project)$/) ?? [] const [, basename, extension] = name.match(/^(.*)[.](tar[.]gz|zip|enso-project)$/) ?? []
return { basename: basename ?? name, extension: extension ?? '' } return { basename: basename ?? name, extension: extension ?? '' }
} }
/** /** Network error class. */
* Network error class.
*/
export class NetworkError extends Error { export class NetworkError extends Error {
/** /**
* Create a new instance of the {@link NetworkError} class. * Create a new instance of the {@link NetworkError} class.
@ -1336,9 +1376,7 @@ export class NetworkError extends Error {
super(message) super(message)
} }
} }
/** /** Error class for when the user is not authorized to access a resource. */
* Error class for when the user is not authorized to access a resource.
*/
export class NotAuthorizedError extends NetworkError {} export class NotAuthorizedError extends NetworkError {}
// =============== // ===============

View File

@ -15,15 +15,19 @@ export function shallowEqual<T>(a: readonly T[], b: readonly T[]) {
// === includes === // === includes ===
// ================ // ================
/** Returns a type predicate that returns true if and only if the value is in the array. /**
* The array MUST contain every element of `T`. */ * Returns a type predicate that returns true if and only if the value is in the array.
* The array MUST contain every element of `T`.
*/
export function includes<T>(array: T[], item: unknown): item is T { export function includes<T>(array: T[], item: unknown): item is T {
const arrayOfUnknown: unknown[] = array const arrayOfUnknown: unknown[] = array
return arrayOfUnknown.includes(item) return arrayOfUnknown.includes(item)
} }
/** Returns a type predicate that returns true if and only if the value is in the iterable. /**
* The iterable MUST contain every element of `T`. */ * Returns a type predicate that returns true if and only if the value is in the iterable.
* The iterable MUST contain every element of `T`.
*/
export function includesPredicate<T>(array: Iterable<T>) { export function includesPredicate<T>(array: Iterable<T>) {
const set: Set<unknown> = array instanceof Set ? array : new Set<T>(array) const set: Set<unknown> = array instanceof Set ? array : new Set<T>(array)
return (item: unknown): item is T => set.has(item) return (item: unknown): item is T => set.has(item)
@ -36,32 +40,40 @@ export function includesPredicate<T>(array: Iterable<T>) {
/** The value returned when {@link Array.findIndex} fails. */ /** The value returned when {@link Array.findIndex} fails. */
const NOT_FOUND = -1 const NOT_FOUND = -1
/** Insert items before the first index `i` for which `predicate(array[i])` is `true`. /**
* Insert the items at the end if the `predicate` never returns `true`. */ * Insert items before the first index `i` for which `predicate(array[i])` is `true`.
* Insert the items at the end if the `predicate` never returns `true`.
*/
export function spliceBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) { export function spliceBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
const index = array.findIndex(predicate) const index = array.findIndex(predicate)
array.splice(index === NOT_FOUND ? array.length : index, 0, ...items) array.splice(index === NOT_FOUND ? array.length : index, 0, ...items)
return array return array
} }
/** Return a copy of the array, with items inserted before the first index `i` for which /**
* Return a copy of the array, with items inserted before the first index `i` for which
* `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never * `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never
* returns `true`. */ * returns `true`.
*/
export function splicedBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) { export function splicedBefore<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
return spliceBefore(Array.from(array), items, predicate) return spliceBefore(Array.from(array), items, predicate)
} }
/** Insert items after the first index `i` for which `predicate(array[i])` is `true`. /**
* Insert the items at the end if the `predicate` never returns `true`. */ * Insert items after the first index `i` for which `predicate(array[i])` is `true`.
* Insert the items at the end if the `predicate` never returns `true`.
*/
export function spliceAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) { export function spliceAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
const index = array.findIndex(predicate) const index = array.findIndex(predicate)
array.splice(index === NOT_FOUND ? array.length : index + 1, 0, ...items) array.splice(index === NOT_FOUND ? array.length : index + 1, 0, ...items)
return array return array
} }
/** Return a copy of the array, with items inserted after the first index `i` for which /**
* Return a copy of the array, with items inserted after the first index `i` for which
* `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never * `predicate(array[i])` is `true`. The items are inserted at the end if the `predicate` never
* returns `true`. */ * returns `true`.
*/
export function splicedAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) { export function splicedAfter<T>(array: T[], items: T[], predicate: (value: T) => boolean) {
return spliceAfter(Array.from(array), items, predicate) return spliceAfter(Array.from(array), items, predicate)
} }

View File

@ -35,8 +35,10 @@ export type Rfc3339DateTime = newtype.Newtype<string, 'Rfc3339DateTime'>
// eslint-disable-next-line @typescript-eslint/no-redeclare // eslint-disable-next-line @typescript-eslint/no-redeclare
export const Rfc3339DateTime = newtype.newtypeConstructor<Rfc3339DateTime>() export const Rfc3339DateTime = newtype.newtypeConstructor<Rfc3339DateTime>()
/** Return a new {@link Date} with units below days (hours, minutes, seconds and milliseconds) /**
* set to `0`. */ * Return a new {@link Date} with units below days (hours, minutes, seconds and milliseconds)
* set to `0`.
*/
export function toDate(dateTime: Date) { export function toDate(dateTime: Date) {
return new Date(dateTime.getFullYear(), dateTime.getMonth(), dateTime.getDate()) return new Date(dateTime.getFullYear(), dateTime.getMonth(), dateTime.getDate())
} }

View File

@ -12,8 +12,10 @@ type NewtypeVariant<TypeName extends string> = {
readonly _$type: TypeName readonly _$type: TypeName
} }
/** An interface specifying the variant of a newtype, where the discriminator is mutable. /**
* This is safe, as the discriminator should be a string literal type anyway. */ * An interface specifying the variant of a newtype, where the discriminator is mutable.
* This is safe, as the discriminator should be a string literal type anyway.
*/
// This is required for compatibility with the dependency `enso-chat`. // This is required for compatibility with the dependency `enso-chat`.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
type MutableNewtypeVariant<TypeName extends string> = { type MutableNewtypeVariant<TypeName extends string> = {
@ -21,7 +23,8 @@ type MutableNewtypeVariant<TypeName extends string> = {
_$type: TypeName _$type: TypeName
} }
/** Used to create a "branded type", /**
* Used to create a "branded type",
* which contains a property that only exists at compile time. * which contains a property that only exists at compile time.
* *
* `Newtype<string, 'A'>` and `Newtype<string, 'B'>` are not compatible with each other, * `Newtype<string, 'A'>` and `Newtype<string, 'B'>` are not compatible with each other,
@ -33,11 +36,14 @@ type MutableNewtypeVariant<TypeName extends string> = {
* It is similar to a `newtype` in other languages. * It is similar to a `newtype` in other languages.
* Note however because TypeScript is structurally typed, * Note however because TypeScript is structurally typed,
* a branded type is assignable to its base type: * a branded type is assignable to its base type:
* `a: string = asNewtype<Newtype<string, 'Name'>>(b)` successfully typechecks. */ * `a: string = asNewtype<Newtype<string, 'Name'>>(b)` successfully typechecks.
*/
export type Newtype<T, TypeName extends string> = NewtypeVariant<TypeName> & T export type Newtype<T, TypeName extends string> = NewtypeVariant<TypeName> & T
/** Extracts the original type out of a {@link Newtype}. /**
* Its only use is in {@link newtypeConstructor}. */ * Extracts the original type out of a {@link Newtype}.
* Its only use is in {@link newtypeConstructor}.
*/
type UnNewtype<T extends Newtype<unknown, string>> = type UnNewtype<T extends Newtype<unknown, string>> =
T extends infer U & NewtypeVariant<T['_$type']> ? T extends infer U & NewtypeVariant<T['_$type']> ?
U extends infer V & MutableNewtypeVariant<T['_$type']> ? U extends infer V & MutableNewtypeVariant<T['_$type']> ?
@ -51,9 +57,11 @@ type NotNewtype = {
readonly _$type?: never readonly _$type?: never
} }
/** Converts a value that is not a newtype, to a value that is a newtype. /**
* Converts a value that is not a newtype, to a value that is a newtype.
* This function intentionally returns another function, to ensure that each function instance * This function intentionally returns another function, to ensure that each function instance
* is only used for one type, avoiding the de-optimization caused by polymorphic functions. */ * is only used for one type, avoiding the de-optimization caused by polymorphic functions.
*/
export function newtypeConstructor<T extends Newtype<unknown, string>>() { export function newtypeConstructor<T extends Newtype<unknown, string>>() {
// This cast is unsafe. // This cast is unsafe.
// `T` has an extra property `_$type` which is used purely for typechecking // `T` has an extra property `_$type` which is used purely for typechecking

View File

@ -16,8 +16,10 @@ export type Mutable<T> = {
/** Prevents generic parameter inference by hiding the type parameter behind a conditional type. */ /** Prevents generic parameter inference by hiding the type parameter behind a conditional type. */
type NoInfer<T> = [T][T extends T ? 0 : never] type NoInfer<T> = [T][T extends T ? 0 : never]
/** Immutably shallowly merge an object with a partial update. /**
* Does not preserve classes. Useful for preserving order of properties. */ * Immutably shallowly merge an object with a partial update.
* Does not preserve classes. Useful for preserving order of properties.
*/
export function merge<T extends object>(object: T, update: Partial<T>): T { export function merge<T extends object>(object: T, update: Partial<T>): T {
for (const [key, value] of Object.entries(update)) { for (const [key, value] of Object.entries(update)) {
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
@ -57,8 +59,10 @@ export function unsafeMutable<T extends object>(object: T): { -readonly [K in ke
// === unsafeEntries === // === unsafeEntries ===
// ===================== // =====================
/** Return the entries of an object. UNSAFE only when it is possible for an object to have /**
* extra keys. */ * Return the entries of an object. UNSAFE only when it is possible for an object to have
* extra keys.
*/
export function unsafeEntries<T extends object>( export function unsafeEntries<T extends object>(
object: T, object: T,
): readonly { [K in keyof T]: readonly [K, T[K]] }[keyof T][] { ): readonly { [K in keyof T]: readonly [K, T[K]] }[keyof T][] {
@ -83,8 +87,10 @@ export function unsafeRemoveUndefined<T extends object>(
// === mapEntries === // === mapEntries ===
// ================== // ==================
/** Return the entries of an object. UNSAFE only when it is possible for an object to have /**
* extra keys. */ * Return the entries of an object. UNSAFE only when it is possible for an object to have
* extra keys.
*/
export function mapEntries<K extends PropertyKey, V, W>( export function mapEntries<K extends PropertyKey, V, W>(
object: Record<K, V>, object: Record<K, V>,
map: (key: K, value: V) => W, map: (key: K, value: V) => W,

View File

@ -111,8 +111,10 @@ export const FROM_PERMISSION_ACTION: Readonly<Record<PermissionAction, Permissio
}, },
} }
/** The corresponding {@link PermissionAction} for each {@link Permission}. /**
* Assumes no docs sub-permission and no execute sub-permission. */ * The corresponding {@link PermissionAction} for each {@link Permission}.
* Assumes no docs sub-permission and no execute sub-permission.
*/
export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAction>> = { export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAction>> = {
[Permission.owner]: PermissionAction.own, [Permission.owner]: PermissionAction.own,
[Permission.admin]: PermissionAction.admin, [Permission.admin]: PermissionAction.admin,
@ -123,8 +125,10 @@ export const TYPE_TO_PERMISSION_ACTION: Readonly<Record<Permission, PermissionAc
[Permission.delete]: PermissionAction.view, [Permission.delete]: PermissionAction.view,
} }
/** The corresponding {@link text.TextId} for each {@link Permission}. /**
* Assumes no docs sub-permission and no execute sub-permission. */ * The corresponding {@link text.TextId} for each {@link Permission}.
* Assumes no docs sub-permission and no execute sub-permission.
*/
export const TYPE_TO_TEXT_ID: Readonly<Record<Permission, text.TextId>> = { export const TYPE_TO_TEXT_ID: Readonly<Record<Permission, text.TextId>> = {
[Permission.owner]: 'ownerPermissionType', [Permission.owner]: 'ownerPermissionType',
[Permission.admin]: 'adminPermissionType', [Permission.admin]: 'adminPermissionType',
@ -181,13 +185,13 @@ interface BasePermissions<T extends Permission> {
} }
/** Owner permissions for an asset. */ /** Owner permissions for an asset. */
interface OwnerPermissions extends BasePermissions<Permission.owner> {} type OwnerPermissions = BasePermissions<Permission.owner>
/** Admin permissions for an asset. */ /** Admin permissions for an asset. */
interface AdminPermissions extends BasePermissions<Permission.admin> {} type AdminPermissions = BasePermissions<Permission.admin>
/** Editor permissions for an asset. */ /** Editor permissions for an asset. */
interface EditPermissions extends BasePermissions<Permission.edit> {} type EditPermissions = BasePermissions<Permission.edit>
/** Reader permissions for an asset. */ /** Reader permissions for an asset. */
interface ReadPermissions extends BasePermissions<Permission.read> { interface ReadPermissions extends BasePermissions<Permission.read> {

View File

@ -42,7 +42,7 @@ const guiTabCases = [
v.test.each([ v.test.each([
{ group: 'Dashboard', cases: dashboardTabCases }, { group: 'Dashboard', cases: dashboardTabCases },
{ group: 'GUI', cases: guiTabCases }, { group: 'GUI', cases: guiTabCases },
])('Tab clip path: $group', ({ group, cases }) => { ])('Tab clip path: $group', ({ cases }) => {
cases.forEach(({ input, expected }) => { cases.forEach(({ input, expected }) => {
const result = tabBar.tabClipPath(input.bounds, input.radius, (input as TabClipPathInput)?.side) const result = tabBar.tabClipPath(input.bounds, input.radius, (input as TabClipPathInput)?.side)
v.expect(result).toBe(expected) v.expect(result).toBe(expected)

View File

@ -2,8 +2,8 @@
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"lib": ["DOM", "es2023"], "lib": ["DOM", "es2023"],
"allowJs": false, "allowJs": true,
"checkJs": false, "checkJs": true,
"skipLibCheck": false "skipLibCheck": false
}, },
"include": ["./src/", "./src/text/english.json", "../types/"] "include": ["./src/", "./src/text/english.json", "../types/"]

View File

@ -12,9 +12,7 @@ export default class EditorPageActions extends PageActions {
get goToPage(): Omit<goToPageActions.GoToPageActions, 'editor'> { get goToPage(): Omit<goToPageActions.GoToPageActions, 'editor'> {
return goToPageActions.goToPageActions(this.step.bind(this)) return goToPageActions.goToPageActions(this.step.bind(this))
} }
/** /** Waits for the editor to load. */
* Waits for the editor to load.
*/
waitForEditorToLoad(): EditorPageActions { waitForEditorToLoad(): EditorPageActions {
return this.step('wait for the editor to load', async () => { return this.step('wait for the editor to load', async () => {
await this.page.waitForSelector('[data-testid=editor]', { state: 'visible' }) await this.page.waitForSelector('[data-testid=editor]', { state: 'visible' })

View File

@ -35,9 +35,7 @@ test('Disconnect an edge from a port', async ({ page }) => {
await expect(await edgesToNodeWithBinding(page, 'sum')).toHaveCount(EDGE_PARTS) await expect(await edgesToNodeWithBinding(page, 'sum')).toHaveCount(EDGE_PARTS)
}) })
/** /** Scenario: We replace the `sum` parameter in the `prod` node` with the `ten` node. */
* Scenario: We replace the `sum` parameter in the `prod` node` with the `ten` node.
*/
test('Connect an node to a port', async ({ page }) => { test('Connect an node to a port', async ({ page }) => {
await initGraph(page) await initGraph(page)
@ -57,9 +55,7 @@ test('Connect an node to a port', async ({ page }) => {
await expect(graphNodeByBinding(page, 'prod')).toContainText('ten') await expect(graphNodeByBinding(page, 'prod')).toContainText('ten')
}) })
/** /** As above, but by dragging edge instead of clicking source and target separately. */
* As above, but by dragging edge instead of clicking source and target separately.
*/
test('Connect an node to a port via dragging the edge', async ({ page }) => { test('Connect an node to a port via dragging the edge', async ({ page }) => {
await initGraph(page) await initGraph(page)

View File

@ -34,9 +34,7 @@ test('Existence of edges between nodes', async ({ page }) => {
await expect(await edgesToNodeWithBinding(page, 'five')).toHaveCount(0) await expect(await edgesToNodeWithBinding(page, 'five')).toHaveCount(0)
}) })
/** /** Prepare the graph for the tests. We drag the `ten` node to the right for better access to its outgoing edge. */
* Prepare the graph for the tests. We drag the `ten` node to the right for better access to its outgoing edge.
*/
async function initGraph(page: Page) { async function initGraph(page: Page) {
await actions.goToGraph(page) await actions.goToGraph(page)
await actions.dragNodeByBinding(page, 'ten', 400, 0) await actions.dragNodeByBinding(page, 'ten', 400, 0)

View File

@ -5,9 +5,7 @@ import { mockExpressionUpdate } from './expressionUpdates'
import * as locate from './locate' import * as locate from './locate'
import { graphNodeByBinding } from './locate' import { graphNodeByBinding } from './locate'
/** /** Prepare the graph for the tests. We add the table type to the `aggregated` node. */
* Prepare the graph for the tests. We add the table type to the `aggregated` node.
*/
async function initGraph(page: Page) { async function initGraph(page: Page) {
await actions.goToGraph(page) await actions.goToGraph(page)
await mockExpressionUpdate(page, 'aggregated', { type: 'Standard.Table.Table.Table' }) await mockExpressionUpdate(page, 'aggregated', { type: 'Standard.Table.Table.Table' })

View File

@ -21,7 +21,7 @@
"build": "vite build", "build": "vite build",
"build-cloud": "cross-env CLOUD_BUILD=true corepack pnpm run build", "build-cloud": "cross-env CLOUD_BUILD=true corepack pnpm run build",
"preview": "vite preview", "preview": "vite preview",
"lint": "cross-env eslint . --max-warnings=0", "lint": "eslint . --max-warnings=0",
"format": "prettier --version && prettier --write src/ && eslint . --fix", "format": "prettier --version && prettier --write src/ && eslint . --fix",
"dev:vite": "vite", "dev:vite": "vite",
"test": "corepack pnpm run /^^^^test:.*/", "test": "corepack pnpm run /^^^^test:.*/",

View File

@ -7,9 +7,7 @@ export const DOCUMENTS = getDocumentsPath()
const CHILD_PROCESS_TIMEOUT = 3000 const CHILD_PROCESS_TIMEOUT = 3000
/** /** Detects path of the user documents directory depending on the operating system. */
* Detects path of the user documents directory depending on the operating system.
*/
function getDocumentsPath(): string | undefined { function getDocumentsPath(): string | undefined {
if (process.platform === 'linux') { if (process.platform === 'linux') {
return getLinuxDocumentsPath() return getLinuxDocumentsPath()
@ -22,18 +20,14 @@ function getDocumentsPath(): string | undefined {
} }
} }
/** /** Returns the user documents path on Linux. */
* Returns the user documents path on Linux.
*/
function getLinuxDocumentsPath(): string { function getLinuxDocumentsPath(): string {
const xdgDocumentsPath = getXdgDocumentsPath() const xdgDocumentsPath = getXdgDocumentsPath()
return xdgDocumentsPath ?? path.join(os.homedir(), 'enso') return xdgDocumentsPath ?? path.join(os.homedir(), 'enso')
} }
/** /** Gets the documents directory from the XDG directory management system. */
* Gets the documents directory from the XDG directory management system.
*/
function getXdgDocumentsPath(): string | undefined { function getXdgDocumentsPath(): string | undefined {
const out = childProcess.spawnSync('xdg-user-dir', ['DOCUMENTS'], { const out = childProcess.spawnSync('xdg-user-dir', ['DOCUMENTS'], {
timeout: CHILD_PROCESS_TIMEOUT, timeout: CHILD_PROCESS_TIMEOUT,
@ -54,9 +48,7 @@ function getMacOsDocumentsPath(): string {
return path.join(os.homedir(), 'Documents') return path.join(os.homedir(), 'Documents')
} }
/** /** Get the path to the `My Documents` Windows directory. */
* Get the path to the `My Documents` Windows directory.
*/
function getWindowsDocumentsPath(): string | undefined { function getWindowsDocumentsPath(): string | undefined {
const out = childProcess.spawnSync( const out = childProcess.spawnSync(
'reg', 'reg',

View File

@ -45,23 +45,17 @@ export const SUPPORT_EMAIL = 'cloud@enso.org'
/** Return the `mailto:` URL for contacting support. */ /** Return the `mailto:` URL for contacting support. */
export const SUPPORT_EMAIL_URL = `mailto:${SUPPORT_EMAIL}` export const SUPPORT_EMAIL_URL = `mailto:${SUPPORT_EMAIL}`
/** /** Build a Subscription URL for a given plan. */
* Build a Subscription URL for a given plan.
*/
export function getUpgradeURL(plan: string): string { export function getUpgradeURL(plan: string): string {
return SUBSCRIBE_PATH + '?plan=' + plan return SUBSCRIBE_PATH + '?plan=' + plan
} }
/** /** Return the mailto URL for contacting sales. */
* Return the mailto URL for contacting sales.
*/
export function getSalesEmail(): string { export function getSalesEmail(): string {
return 'mailto:contact@enso.org' return 'mailto:contact@enso.org'
} }
/** /** Build a Subscription URL for contacting sales. */
* Build a Subscription URL for contacting sales.
*/
export function getContactSalesURL(): string { export function getContactSalesURL(): string {
return 'mailto:contact@enso.org?subject=Upgrading%20to%20Organization%20Plan' return 'mailto:contact@enso.org?subject=Upgrading%20to%20Organization%20Plan'
} }

View File

@ -306,16 +306,12 @@ export class Cognito {
} }
} }
/** /** Refresh the current user's session. */
* Refresh the current user's session.
*/
async refreshUserSession() { async refreshUserSession() {
return Promise.resolve(results.Ok(null)) return Promise.resolve(results.Ok(null))
} }
/** /** Returns MFA preference for the current user. */
* Returns MFA preference for the current user.
*/
async getMFAPreference() { async getMFAPreference() {
return Promise.resolve(results.Ok('NOMFA')) return Promise.resolve(results.Ok('NOMFA'))
} }

View File

@ -74,9 +74,7 @@ interface UserAttributes {
} }
/* eslint-enable @typescript-eslint/naming-convention */ /* eslint-enable @typescript-eslint/naming-convention */
/** /** The type of multi-factor authentication (MFA) that the user has set up. */
* The type of multi-factor authentication (MFA) that the user has set up.
*/
export type MfaType = 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | 'TOTP' export type MfaType = 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | 'TOTP'
/** /**
@ -244,9 +242,7 @@ export class Cognito {
return userInfo.attributes['custom:organizationId'] ?? null return userInfo.attributes['custom:organizationId'] ?? null
} }
/** /** Gets user email from cognito */
* Gets user email from cognito
*/
async email() { async email() {
// This `any` comes from a third-party API and cannot be avoided. // This `any` comes from a third-party API and cannot be avoided.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@ -337,9 +333,7 @@ export class Cognito {
return result.mapErr(intoAmplifyErrorOrThrow).mapErr(intoSignInWithPasswordErrorOrThrow) return result.mapErr(intoAmplifyErrorOrThrow).mapErr(intoSignInWithPasswordErrorOrThrow)
} }
/** /** Refresh the current user session. */
* Refresh the current user session.
*/
async refreshUserSession() { async refreshUserSession() {
const result = await results.Result.wrapAsync(async () => { const result = await results.Result.wrapAsync(async () => {
const currentUser = await currentAuthenticatedUser() const currentUser = await currentAuthenticatedUser()
@ -432,9 +426,7 @@ export class Cognito {
} }
} }
/** /** Start the TOTP setup process. Returns the secret and the URL to scan the QR code. */
* Start the TOTP setup process. Returns the secret and the URL to scan the QR code.
*/
async setupTOTP() { async setupTOTP() {
const email = await this.email() const email = await this.email()
const cognitoUserResult = await currentAuthenticatedUser() const cognitoUserResult = await currentAuthenticatedUser()
@ -472,9 +464,7 @@ export class Cognito {
} }
} }
/** /** Set the user's preferred MFA method. */
* Set the user's preferred MFA method.
*/
async updateMFAPreference(mfaMethod: MfaType) { async updateMFAPreference(mfaMethod: MfaType) {
const cognitoUserResult = await currentAuthenticatedUser() const cognitoUserResult = await currentAuthenticatedUser()
if (cognitoUserResult.ok) { if (cognitoUserResult.ok) {
@ -488,9 +478,7 @@ export class Cognito {
} }
} }
/** /** Get the user's preferred MFA method. */
* Get the user's preferred MFA method.
*/
async getMFAPreference() { async getMFAPreference() {
const cognitoUserResult = await currentAuthenticatedUser() const cognitoUserResult = await currentAuthenticatedUser()
if (cognitoUserResult.ok) { if (cognitoUserResult.ok) {
@ -523,9 +511,7 @@ export class Cognito {
} }
} }
/** /** Confirm the sign in with the MFA token. */
* Confirm the sign in with the MFA token.
*/
async confirmSignIn( async confirmSignIn(
user: amplify.CognitoUser, user: amplify.CognitoUser,
confirmationCode: string, confirmationCode: string,

View File

@ -11,9 +11,7 @@ import { createContext, useContext, useId } from 'react'
import { twJoin } from '#/utilities/tailwindMerge' import { twJoin } from '#/utilities/tailwindMerge'
import invariant from 'tiny-invariant' import invariant from 'tiny-invariant'
/** /** Props for {@link AnimatedBackground}. */
* Props for {@link AnimatedBackground}.
*/
interface AnimatedBackgroundProps extends PropsWithChildren { interface AnimatedBackgroundProps extends PropsWithChildren {
readonly value: string readonly value: string
readonly transition?: Transition readonly transition?: Transition
@ -37,9 +35,7 @@ const DEFAULT_TRANSITION: Transition = {
velocity: 12, velocity: 12,
} }
/** /** `<AnimatedBackground />` component visually highlights selected items by sliding a background into view when hovered over or clicked. */
* `<AnimatedBackground />` component visually highlights selected items by sliding a background into view when hovered over or clicked.
*/
export function AnimatedBackground(props: AnimatedBackgroundProps) { export function AnimatedBackground(props: AnimatedBackgroundProps) {
const { value, transition = DEFAULT_TRANSITION, children } = props const { value, transition = DEFAULT_TRANSITION, children } = props
const layoutId = useId() const layoutId = useId()
@ -51,18 +47,14 @@ export function AnimatedBackground(props: AnimatedBackgroundProps) {
) )
} }
/** /** Props for {@link AnimatedBackground.Item}. */
* Props for {@link AnimatedBackground.Item}.
*/
interface AnimatedBackgroundItemProps extends PropsWithChildren { interface AnimatedBackgroundItemProps extends PropsWithChildren {
readonly value: string readonly value: string
readonly className?: string readonly className?: string
readonly animationClassName?: string readonly animationClassName?: string
} }
/** /** Item within an {@link AnimatedBackground}. */
* Item within an {@link AnimatedBackground}.
*/
AnimatedBackground.Item = function AnimatedBackgroundItem(props: AnimatedBackgroundItemProps) { AnimatedBackground.Item = function AnimatedBackgroundItem(props: AnimatedBackgroundItemProps) {
const context = useContext(AnimatedBackgroundContext) const context = useContext(AnimatedBackgroundContext)
invariant(context, 'useAnimatedBackground must be used within an AnimatedBackgroundProvider') invariant(context, 'useAnimatedBackground must be used within an AnimatedBackgroundProvider')

View File

@ -61,9 +61,7 @@ export interface AlertProps
extends PropsWithChildren, extends PropsWithChildren,
VariantProps<typeof ALERT_STYLES>, VariantProps<typeof ALERT_STYLES>,
HTMLAttributes<HTMLDivElement> { HTMLAttributes<HTMLDivElement> {
/** /** The icon to display in the Alert */
* The icon to display in the Alert
*/
readonly icon?: React.ReactElement | string | null | undefined readonly icon?: React.ReactElement | string | null | undefined
} }

View File

@ -22,39 +22,29 @@ export type ButtonProps =
| (BaseButtonProps<aria.ButtonRenderProps> & Omit<aria.ButtonProps, 'onPress'> & PropsWithoutHref) | (BaseButtonProps<aria.ButtonRenderProps> & Omit<aria.ButtonProps, 'onPress'> & PropsWithoutHref)
| (BaseButtonProps<aria.LinkRenderProps> & Omit<aria.LinkProps, 'onPress'> & PropsWithHref) | (BaseButtonProps<aria.LinkRenderProps> & Omit<aria.LinkProps, 'onPress'> & PropsWithHref)
/** /** Props for a button with an href. */
* Props for a button with an href.
*/
interface PropsWithHref { interface PropsWithHref {
readonly href: string readonly href: string
} }
/** /** Props for a button without an href. */
* Props for a button without an href.
*/
interface PropsWithoutHref { interface PropsWithoutHref {
readonly href?: never readonly href?: never
} }
/** /** Base props for a button. */
* Base props for a button.
*/
export interface BaseButtonProps<Render> export interface BaseButtonProps<Render>
extends Omit<VariantProps<typeof BUTTON_STYLES>, 'iconOnly'> { extends Omit<VariantProps<typeof BUTTON_STYLES>, 'iconOnly'> {
/** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */ /** Falls back to `aria-label`. Pass `false` to explicitly disable the tooltip. */
readonly tooltip?: React.ReactElement | string | false | null readonly tooltip?: React.ReactElement | string | false | null
readonly tooltipPlacement?: aria.Placement readonly tooltipPlacement?: aria.Placement
/** /** The icon to display in the button */
* The icon to display in the button
*/
readonly icon?: readonly icon?:
| React.ReactElement | React.ReactElement
| string | string
| ((render: Render) => React.ReactElement | string | null) | ((render: Render) => React.ReactElement | string | null)
| null | null
/** /** When `true`, icon will be shown only when hovered. */
* When `true`, icon will be shown only when hovered.
*/
readonly showIconOnHover?: boolean readonly showIconOnHover?: boolean
/** /**
* Handler that is called when the press is released over the target. * Handler that is called when the press is released over the target.

View File

@ -33,9 +33,7 @@ import type { TestIdProps } from '../types'
import { useCheckboxContext } from './CheckboxContext' import { useCheckboxContext } from './CheckboxContext'
import { CheckboxGroup } from './CheckboxGroup' import { CheckboxGroup } from './CheckboxGroup'
/** /** Props for the {@link Checkbox} component. */
* Props for the {@link Checkbox} component.
*/
export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> = Omit< export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> = Omit<
VariantProps<typeof CHECKBOX_STYLES>, VariantProps<typeof CHECKBOX_STYLES>,
'isDisabled' | 'isInvalid' 'isDisabled' | 'isInvalid'
@ -46,18 +44,14 @@ export type CheckboxProps<Schema extends TSchema, TFieldName extends FieldPath<S
readonly checkboxRef?: MutableRefObject<HTMLInputElement> readonly checkboxRef?: MutableRefObject<HTMLInputElement>
} & (CheckboxGroupCheckboxProps | StandaloneCheckboxProps<Schema, TFieldName>) } & (CheckboxGroupCheckboxProps | StandaloneCheckboxProps<Schema, TFieldName>)
/** /** Props for the {@link Checkbox} component when used inside a {@link CheckboxGroup}. */
* Props for the {@link Checkbox} component when used inside a {@link CheckboxGroup}.
*/
interface CheckboxGroupCheckboxProps extends AriaCheckboxProps { interface CheckboxGroupCheckboxProps extends AriaCheckboxProps {
readonly value: string readonly value: string
readonly form?: never readonly form?: never
readonly name?: never readonly name?: never
} }
/** /** Props for the {@link Checkbox} component when used outside of a {@link CheckboxGroup}. */
* Props for the {@link Checkbox} component when used outside of a {@link CheckboxGroup}.
*/
type StandaloneCheckboxProps< type StandaloneCheckboxProps<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,
@ -123,9 +117,7 @@ export const TICK_VARIANTS: Variants = {
}, },
} }
/** /** Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected. */
* Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const Checkbox = forwardRef(function Checkbox< export const Checkbox = forwardRef(function Checkbox<
Schema extends TSchema, Schema extends TSchema,

View File

@ -1,6 +1,4 @@
/** /** @file */
* @file
*/
import { useEventCallback } from '#/hooks/eventCallbackHooks' import { useEventCallback } from '#/hooks/eventCallbackHooks'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import { createContext, useContext, useMemo, useState } from 'react' import { createContext, useContext, useMemo, useState } from 'react'
@ -8,9 +6,7 @@ import type { StoreApi } from 'zustand'
import { createStore } from 'zustand' import { createStore } from 'zustand'
import type { TSchema, UseFormRegisterReturn } from '../Form' import type { TSchema, UseFormRegisterReturn } from '../Form'
/** /** Context for the checkbox. */
* Context for the checkbox.
*/
interface CheckboxContextType { interface CheckboxContextType {
readonly store: StoreApi<CheckGroupPropsState> readonly store: StoreApi<CheckGroupPropsState>
readonly addSelected: (selected: string) => void readonly addSelected: (selected: string) => void
@ -25,9 +21,7 @@ const CheckboxContext = createContext<CheckboxContextType>({
toggleSelected: () => {}, toggleSelected: () => {},
}) })
/** /** Gets the context for the checkbox. */
* Gets the context for the checkbox.
*/
export function useCheckboxContext() { export function useCheckboxContext() {
return useContext(CheckboxContext) return useContext(CheckboxContext)
} }
@ -50,9 +44,7 @@ export function useCheckboxGroupState() {
*/ */
type CheckGroupPropsState = CheckBoxGroupPropsStateInsideGroup | CheckBoxGroupPropsStateOutsideGroup type CheckGroupPropsState = CheckBoxGroupPropsStateInsideGroup | CheckBoxGroupPropsStateOutsideGroup
/** /** Checkbox group state when the checkbox is inside a group. */
* Checkbox group state when the checkbox is inside a group.
*/
interface CheckBoxGroupPropsStateInsideGroup { interface CheckBoxGroupPropsStateInsideGroup {
readonly insideGroup: true readonly insideGroup: true
readonly selected: Set<string> readonly selected: Set<string>
@ -60,16 +52,12 @@ interface CheckBoxGroupPropsStateInsideGroup {
readonly field: UseFormRegisterReturn<TSchema> readonly field: UseFormRegisterReturn<TSchema>
} }
/** /** Checkbox group state when the checkbox is not inside a group. */
* Checkbox group state when the checkbox is not inside a group.
*/
interface CheckBoxGroupPropsStateOutsideGroup { interface CheckBoxGroupPropsStateOutsideGroup {
readonly insideGroup: false readonly insideGroup: false
} }
/** /** Props for {@link CheckboxGroupProvider}. */
* Props for {@link CheckboxGroupProvider}.
*/
export interface CheckboxGroupProviderProps extends PropsWithChildren { export interface CheckboxGroupProviderProps extends PropsWithChildren {
readonly name: string readonly name: string
readonly onChange: (selected: string[]) => void readonly onChange: (selected: string[]) => void
@ -77,9 +65,7 @@ export interface CheckboxGroupProviderProps extends PropsWithChildren {
readonly defaultValue?: string[] | undefined readonly defaultValue?: string[] | undefined
} }
/** /** Checkbox group provider used to manage the state of a group of checkboxes. */
* Checkbox group provider used to manage the state of a group of checkboxes.
*/
export function CheckboxGroupProvider(props: CheckboxGroupProviderProps) { export function CheckboxGroupProvider(props: CheckboxGroupProviderProps) {
const { children, onChange, name, field, defaultValue = [] } = props const { children, onChange, name, field, defaultValue = [] } = props

View File

@ -16,9 +16,7 @@ import { Form, type FieldPath, type FieldProps, type FieldStateProps, type TSche
import type { TestIdProps } from '../types' import type { TestIdProps } from '../types'
import { CheckboxGroupProvider } from './CheckboxContext' import { CheckboxGroupProvider } from './CheckboxContext'
/** /** Props for the {@link CheckboxGroupProps} component. */
* Props for the {@link CheckboxGroupProps} component.
*/
export interface CheckboxGroupProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> export interface CheckboxGroupProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
extends FieldStateProps<AriaCheckboxGroupProps, Schema, TFieldName>, extends FieldStateProps<AriaCheckboxGroupProps, Schema, TFieldName>,
FieldProps, FieldProps,
@ -36,9 +34,7 @@ const CHECKBOX_GROUP_STYLES = tv({
variants: { fullWidth: { true: 'w-full' } }, variants: { fullWidth: { true: 'w-full' } },
}) })
/** /** A CheckboxGroup allows users to select one or more items from a list of choices. */
* A CheckboxGroup allows users to select one or more items from a list of choices.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const CheckboxGroup = forwardRef( export const CheckboxGroup = forwardRef(
<Schema extends TSchema, TFieldName extends FieldPath<Schema>>( <Schema extends TSchema, TFieldName extends FieldPath<Schema>>(

View File

@ -12,14 +12,10 @@ import * as eventCallback from '#/hooks/eventCallbackHooks'
import * as button from '../Button' import * as button from '../Button'
import * as dialogProvider from './DialogProvider' import * as dialogProvider from './DialogProvider'
/** /** Props for {@link Close} component. */
* Props for {@link Close} component.
*/
export type CloseProps = button.ButtonProps export type CloseProps = button.ButtonProps
/** /** Close button for a dialog. */
* Close button for a dialog.
*/
export function Close(props: CloseProps) { export function Close(props: CloseProps) {
const dialogContext = dialogProvider.useDialogContext() const dialogContext = dialogProvider.useDialogContext()

View File

@ -23,9 +23,7 @@ import { DIALOG_BACKGROUND } from './variants'
// ================= // =================
// === Constants === // === Constants ===
// ================= // =================
/** /** Props for the {@link Dialog} component. */
* Props for the {@link Dialog} component.
*/
export interface DialogProps export interface DialogProps
extends types.DialogProps, extends types.DialogProps,
Omit<VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {} Omit<VariantProps<typeof DIALOG_STYLES>, 'scrolledToTop'> {}
@ -177,9 +175,7 @@ export function Dialog(props: DialogProps) {
const [isScrolledToTop, setIsScrolledToTop] = React.useState(true) const [isScrolledToTop, setIsScrolledToTop] = React.useState(true)
/** /** Handles the scroll event on the dialog content. */
* Handles the scroll event on the dialog content.
*/
const handleScroll = (scrollTop: number) => { const handleScroll = (scrollTop: number) => {
React.startTransition(() => { React.startTransition(() => {
if (scrollTop > 0) { if (scrollTop > 0) {

View File

@ -5,28 +5,20 @@
*/ */
import * as React from 'react' import * as React from 'react'
/** /** The context value for a dialog. */
* The context value for a dialog.
*/
export interface DialogContextValue { export interface DialogContextValue {
readonly close: () => void readonly close: () => void
readonly dialogId: string readonly dialogId: string
} }
/** /** The context for a dialog. */
* The context for a dialog.
*/
const DialogContext = React.createContext<DialogContextValue | null>(null) const DialogContext = React.createContext<DialogContextValue | null>(null)
/** /** The provider for a dialog. */
* The provider for a dialog.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const DialogProvider = DialogContext.Provider export const DialogProvider = DialogContext.Provider
/** /** Custom hook to get the dialog context. */
* Custom hook to get the dialog context.
*/
export function useDialogContext() { export function useDialogContext() {
return React.useContext(DialogContext) return React.useContext(DialogContext)
} }

View File

@ -1,6 +1,4 @@
/** /** @file This file provides the DialogStackProvider component and related functionality. */
* @file This file provides the DialogStackProvider component and related functionality.
*/
import * as React from 'react' import * as React from 'react'
@ -8,17 +6,13 @@ import invariant from 'tiny-invariant'
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks' import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
/** /** DialogStackItem represents an item in the dialog stack. */
* DialogStackItem represents an item in the dialog stack.
*/
export interface DialogStackItem { export interface DialogStackItem {
readonly id: string readonly id: string
readonly type: 'dialog-fullscreen' | 'dialog' | 'popover' readonly type: 'dialog-fullscreen' | 'dialog' | 'popover'
} }
/** /** DialogStackContextType represents the context for the dialog stack. */
* DialogStackContextType represents the context for the dialog stack.
*/
export interface DialogStackContextType { export interface DialogStackContextType {
readonly stack: DialogStackItem[] readonly stack: DialogStackItem[]
readonly dialogsStack: DialogStackItem[] readonly dialogsStack: DialogStackItem[]
@ -28,9 +22,7 @@ export interface DialogStackContextType {
const DialogStackContext = React.createContext<DialogStackContextType | null>(null) const DialogStackContext = React.createContext<DialogStackContextType | null>(null)
/** /** DialogStackProvider is a React component that provides the dialog stack context to its children. */
* DialogStackProvider is a React component that provides the dialog stack context to its children.
*/
export function DialogStackProvider(props: React.PropsWithChildren) { export function DialogStackProvider(props: React.PropsWithChildren) {
const { children } = props const { children } = props
@ -72,9 +64,7 @@ updated properly.`)
return <DialogStackContext.Provider value={value}>{children}</DialogStackContext.Provider> return <DialogStackContext.Provider value={value}>{children}</DialogStackContext.Provider>
} }
/** /** DialogStackRegistrar is a React component that registers a dialog in the dialog stack. */
* DialogStackRegistrar is a React component that registers a dialog in the dialog stack.
*/
export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackItem>) { export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackItem>) {
const { children, id: idRaw, type: typeRaw } = props const { children, id: idRaw, type: typeRaw } = props
const idRef = React.useRef(idRaw) const idRef = React.useRef(idRaw)
@ -100,16 +90,12 @@ export function DialogStackRegistrar(props: React.PropsWithChildren<DialogStackI
return children return children
} }
/** /** Props for {@link useDialogStackState} */
* Props for {@link useDialogStackState}
*/
export interface UseDialogStackStateProps { export interface UseDialogStackStateProps {
readonly id: string readonly id: string
} }
/** /** useDialogStackState is a custom hook that provides the state of the dialog stack. */
* useDialogStackState is a custom hook that provides the state of the dialog stack.
*/
export function useDialogStackState(props: UseDialogStackStateProps) { export function useDialogStackState(props: UseDialogStackStateProps) {
const ctx = React.useContext(DialogStackContext) const ctx = React.useContext(DialogStackContext)

View File

@ -10,21 +10,15 @@ import { useOverlayTriggerState } from 'react-stately'
const PLACEHOLDER = <div /> const PLACEHOLDER = <div />
/** /** Props passed to the render function of a {@link DialogTrigger}. */
* Props passed to the render function of a {@link DialogTrigger}.
*/
export interface DialogTriggerRenderProps { export interface DialogTriggerRenderProps {
readonly isOpen: boolean readonly isOpen: boolean
readonly close: () => void readonly close: () => void
readonly open: () => void readonly open: () => void
} }
/** /** Props for a {@link DialogTrigger}. */
* Props for a {@link DialogTrigger}.
*/
export interface DialogTriggerProps extends Omit<aria.DialogTriggerProps, 'children'> { export interface DialogTriggerProps extends Omit<aria.DialogTriggerProps, 'children'> {
/** /** The trigger element. */
* The trigger element.
*/
readonly children: [ readonly children: [
React.ReactElement, React.ReactElement,
React.ReactElement | ((props: DialogTriggerRenderProps) => React.ReactElement), React.ReactElement | ((props: DialogTriggerRenderProps) => React.ReactElement),

View File

@ -23,16 +23,12 @@ const IGNORE_INTERACT_OUTSIDE_ELEMENTS = [
const IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR = `:is(${IGNORE_INTERACT_OUTSIDE_ELEMENTS.join(', ')})` const IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR = `:is(${IGNORE_INTERACT_OUTSIDE_ELEMENTS.join(', ')})`
/** /** Check if the element is a part of a component that should ignore the interact outside event */
* Check if the element is a part of a component that should ignore the interact outside event
*/
export function shouldIgnoreInteractOutside(element: HTMLElement) { export function shouldIgnoreInteractOutside(element: HTMLElement) {
return element.closest(IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR) return element.closest(IGNORE_INTERACT_OUTSIDE_ELEMENTS_SELECTOR)
} }
/** /** Props for {@link useInteractOutside} */
* Props for {@link useInteractOutside}
*/
export interface UseInteractOutsideProps { export interface UseInteractOutsideProps {
readonly ref: React.RefObject<HTMLElement> readonly ref: React.RefObject<HTMLElement>
readonly id: string readonly id: string
@ -40,9 +36,7 @@ export interface UseInteractOutsideProps {
readonly isDisabled?: boolean readonly isDisabled?: boolean
} }
/** /** Hook that handles the interact outside event for the dialog */
* Hook that handles the interact outside event for the dialog
*/
export function useInteractOutside(props: UseInteractOutsideProps) { export function useInteractOutside(props: UseInteractOutsideProps) {
const { ref, id, onInteractOutside, isDisabled = false } = props const { ref, id, onInteractOutside, isDisabled = false } = props
const shouldCloseOnInteractOutsideRef = React.useRef(false) const shouldCloseOnInteractOutsideRef = React.useRef(false)

View File

@ -14,9 +14,7 @@ import * as text from '../../Text'
import { Form } from '../Form' import { Form } from '../Form'
import type * as types from './types' import type * as types from './types'
/** /** Props for Field component */
* Props for Field component
*/
export interface FieldComponentProps<Schema extends types.TSchema> export interface FieldComponentProps<Schema extends types.TSchema>
extends VariantProps<typeof FIELD_STYLES>, extends VariantProps<typeof FIELD_STYLES>,
types.FieldProps { types.FieldProps {
@ -29,16 +27,12 @@ export interface FieldComponentProps<Schema extends types.TSchema>
readonly style?: React.CSSProperties | undefined readonly style?: React.CSSProperties | undefined
} }
/** /** Props for Field variants */
* Props for Field variants
*/
export interface FieldVariantProps { export interface FieldVariantProps {
readonly fieldVariants?: VariantProps<typeof FIELD_STYLES>['variants'] | undefined readonly fieldVariants?: VariantProps<typeof FIELD_STYLES>['variants'] | undefined
} }
/** /** Props for Field children */
* Props for Field children
*/
export interface FieldChildrenRenderProps { export interface FieldChildrenRenderProps {
readonly isInvalid: boolean readonly isInvalid: boolean
readonly isDirty: boolean readonly isDirty: boolean
@ -65,9 +59,7 @@ export const FIELD_STYLES = tv({
defaultVariants: { fullWidth: true }, defaultVariants: { fullWidth: true },
}) })
/** /** Field component */
* Field component
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const Field = forwardRef(function Field<Schema extends types.TSchema>( export const Field = forwardRef(function Field<Schema extends types.TSchema>(
props: FieldComponentProps<Schema>, props: FieldComponentProps<Schema>,

View File

@ -13,18 +13,14 @@ import * as reactAriaComponents from '#/components/AriaComponents'
import * as formContext from './FormProvider' import * as formContext from './FormProvider'
import type * as types from './types' import type * as types from './types'
/** /** Props for the FormError component. */
* Props for the FormError component.
*/
export interface FormErrorProps extends Omit<reactAriaComponents.AlertProps, 'children'> { export interface FormErrorProps extends Omit<reactAriaComponents.AlertProps, 'children'> {
// We do not need to know the form fields. // We do not need to know the form fields.
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly form?: types.FormInstance<any> readonly form?: types.FormInstance<any>
} }
/** /** Form error component. */
* Form error component.
*/
export function FormError(props: FormErrorProps) { export function FormError(props: FormErrorProps) {
const { size = 'large', variant = 'error', rounded = 'large', ...alertProps } = props const { size = 'large', variant = 'error', rounded = 'large', ...alertProps } = props
@ -33,9 +29,7 @@ export function FormError(props: FormErrorProps) {
const { errors } = formState const { errors } = formState
const { getText } = textProvider.useText() const { getText } = textProvider.useText()
/** /** Get the error message. */
* Get the error message.
*/
const getSubmitError = (): string | null => { const getSubmitError = (): string | null => {
const formErrors = errors.root const formErrors = errors.root

View File

@ -9,9 +9,7 @@ import invariant from 'tiny-invariant'
import type * as types from './types' import type * as types from './types'
import type { FormInstance, FormInstanceValidated } from './types' import type { FormInstance, FormInstanceValidated } from './types'
/** /** Context type for the form provider. */
* Context type for the form provider.
*/
interface FormContextType<Schema extends types.TSchema> { interface FormContextType<Schema extends types.TSchema> {
readonly form: types.UseFormReturn<Schema> readonly form: types.UseFormReturn<Schema>
} }
@ -20,9 +18,7 @@ interface FormContextType<Schema extends types.TSchema> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const FormContext = createContext<FormContextType<any> | null>(null) const FormContext = createContext<FormContextType<any> | null>(null)
/** /** Provides the form instance to the component tree. */
* Provides the form instance to the component tree.
*/
export function FormProvider<Schema extends types.TSchema>( export function FormProvider<Schema extends types.TSchema>(
props: FormContextType<Schema> & PropsWithChildren, props: FormContextType<Schema> & PropsWithChildren,
) { ) {
@ -36,9 +32,7 @@ export function FormProvider<Schema extends types.TSchema>(
) )
} }
/** /** Returns the form instance from the context. */
* Returns the form instance from the context.
*/
export function useFormContext<Schema extends types.TSchema>( export function useFormContext<Schema extends types.TSchema>(
form?: FormInstanceValidated<Schema>, form?: FormInstanceValidated<Schema>,
): FormInstance<Schema> { ): FormInstance<Schema> {
@ -56,9 +50,7 @@ export function useFormContext<Schema extends types.TSchema>(
} }
} }
/** /** Returns the form instance from the context, or null if the context is not available. */
* Returns the form instance from the context, or null if the context is not available.
*/
export function useOptionalFormContext< export function useOptionalFormContext<
Form extends FormInstanceValidated<Schema> | undefined, Form extends FormInstanceValidated<Schema> | undefined,
Schema extends types.TSchema, Schema extends types.TSchema,

View File

@ -11,9 +11,7 @@ import { useText } from '#/providers/TextProvider'
import * as formContext from './FormProvider' import * as formContext from './FormProvider'
import type * as types from './types' import type * as types from './types'
/** /** Props for the Reset component. */
* Props for the Reset component.
*/
export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'> { export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'> {
/** /**
* Connects the submit button to a form. * Connects the submit button to a form.
@ -28,9 +26,7 @@ export interface ResetProps extends Omit<ariaComponents.ButtonProps, 'loading'>
readonly action?: 'cancel' | 'reset' readonly action?: 'cancel' | 'reset'
} }
/** /** Reset button for forms. */
* Reset button for forms.
*/
export function Reset(props: ResetProps): React.JSX.Element { export function Reset(props: ResetProps): React.JSX.Element {
const { getText } = useText() const { getText } = useText()
const { const {

View File

@ -11,9 +11,7 @@ import { useText } from '#/providers/TextProvider'
import { useFormContext } from './FormProvider' import { useFormContext } from './FormProvider'
import type { FormInstance } from './types' import type { FormInstance } from './types'
/** /** Additional props for the Submit component. */
* Additional props for the Submit component.
*/
interface SubmitButtonBaseProps { interface SubmitButtonBaseProps {
readonly variant?: ButtonProps['variant'] readonly variant?: ButtonProps['variant']
/** /**
@ -29,9 +27,7 @@ interface SubmitButtonBaseProps {
readonly action?: 'cancel' | 'submit' | 'update' readonly action?: 'cancel' | 'submit' | 'update'
} }
/** /** Props for the Submit component. */
* Props for the Submit component.
*/
export type SubmitProps = Omit<ButtonProps, 'formnovalidate' | 'href' | 'variant'> & export type SubmitProps = Omit<ButtonProps, 'formnovalidate' | 'href' | 'variant'> &
SubmitButtonBaseProps SubmitButtonBaseProps

View File

@ -24,17 +24,13 @@ export type TransformedValues<Schema extends TSchema | undefined> =
*/ */
export type FieldPath<Schema extends TSchema> = reactHookForm.FieldPath<FieldValues<Schema>> export type FieldPath<Schema extends TSchema> = reactHookForm.FieldPath<FieldValues<Schema>>
/** /** Schema type */
* Schema type
*/
export type TSchema = export type TSchema =
| z.AnyZodObject | z.AnyZodObject
| z.ZodEffects<z.AnyZodObject> | z.ZodEffects<z.AnyZodObject>
| z.ZodEffects<z.ZodEffects<z.AnyZodObject>> | z.ZodEffects<z.ZodEffects<z.AnyZodObject>>
/** /** OnSubmitCallbacks type. */
* OnSubmitCallbacks type.
*/
export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void> { export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void> {
readonly onSubmit?: readonly onSubmit?:
| (( | ((
@ -67,9 +63,7 @@ export interface OnSubmitCallbacks<Schema extends TSchema, SubmitResult = void>
| undefined | undefined
} }
/** /** Props for the useForm hook. */
* Props for the useForm hook.
*/
export interface UseFormProps<Schema extends TSchema, SubmitResult = void> export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
extends Omit< extends Omit<
reactHookForm.UseFormProps<FieldValues<Schema>>, reactHookForm.UseFormProps<FieldValues<Schema>>,
@ -83,16 +77,12 @@ export interface UseFormProps<Schema extends TSchema, SubmitResult = void>
*/ */
readonly canSubmitOffline?: boolean readonly canSubmitOffline?: boolean
/** /** Debug name for the form. Use it to identify the form in the tanstack query devtools. */
* Debug name for the form. Use it to identify the form in the tanstack query devtools.
*/
readonly debugName?: string readonly debugName?: string
readonly method?: 'dialog' | (string & {}) | undefined readonly method?: 'dialog' | (string & {}) | undefined
} }
/** /** Register function for a form field. */
* Register function for a form field.
*/
export type UseFormRegister<Schema extends TSchema> = < export type UseFormRegister<Schema extends TSchema> = <
TFieldName extends FieldPath<Schema> = FieldPath<Schema>, TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
>( >(
@ -100,9 +90,7 @@ export type UseFormRegister<Schema extends TSchema> = <
options?: reactHookForm.RegisterOptions<FieldValues<Schema>, TFieldName>, options?: reactHookForm.RegisterOptions<FieldValues<Schema>, TFieldName>,
) => UseFormRegisterReturn<Schema, TFieldName> ) => UseFormRegisterReturn<Schema, TFieldName>
/** /** UseFormRegister return type. */
* UseFormRegister return type.
*/
export interface UseFormRegisterReturn< export interface UseFormRegisterReturn<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema> = FieldPath<Schema>, TFieldName extends FieldPath<Schema> = FieldPath<Schema>,
@ -147,9 +135,7 @@ export type FormState<Schema extends TSchema> = reactHookForm.FormState<FieldVal
*/ */
export type FormInstance<Schema extends TSchema> = UseFormReturn<Schema> export type FormInstance<Schema extends TSchema> = UseFormReturn<Schema>
/** /** Form type interface that check if FieldValues type is compatible with the value type from component */
* Form type interface that check if FieldValues type is compatible with the value type from component
*/
export interface FormWithValueValidation< export interface FormWithValueValidation<
BaseValueType, BaseValueType,
Schema extends TSchema, Schema extends TSchema,
@ -180,9 +166,7 @@ export type FormInstanceValidated<
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
> = FormInstance<Schema> | (any[] & NonNullable<unknown>) > = FormInstance<Schema> | (any[] & NonNullable<unknown>)
/** /** Props for the Field component. */
* Props for the Field component.
*/
// Readonly omitted here to avoid type mismatch with native HTML attributes // Readonly omitted here to avoid type mismatch with native HTML attributes
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export interface FieldProps { export interface FieldProps {
@ -191,27 +175,19 @@ export interface FieldProps {
readonly description?: React.ReactNode | undefined readonly description?: React.ReactNode | undefined
readonly error?: React.ReactNode | undefined readonly error?: React.ReactNode | undefined
/** /** Defines a string value that labels the current element. */
* Defines a string value that labels the current element.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
'aria-label'?: string | undefined 'aria-label'?: string | undefined
/** /** Identifies the element (or elements) that labels the current element. */
* Identifies the element (or elements) that labels the current element.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
'aria-labelledby'?: string | undefined 'aria-labelledby'?: string | undefined
/** /** Identifies the element (or elements) that describes the object. */
* Identifies the element (or elements) that describes the object.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
'aria-describedby'?: string | undefined 'aria-describedby'?: string | undefined
/** /** Identifies the element (or elements) that provide a detailed, extended description for the object. */
* Identifies the element (or elements) that provide a detailed, extended description for the object.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
'aria-details'?: string | undefined 'aria-details'?: string | undefined
} }
@ -233,9 +209,7 @@ export interface FormFieldProps<
readonly isInvalid?: boolean | undefined readonly isInvalid?: boolean | undefined
} }
/** /** Field State Props */
* Field State Props
*/
export type FieldStateProps< export type FieldStateProps<
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
BaseProps extends { value?: unknown }, BaseProps extends { value?: unknown },

View File

@ -8,9 +8,7 @@ import * as reactHookForm from 'react-hook-form'
import * as formContext from './FormProvider' import * as formContext from './FormProvider'
import type * as types from './types' import type * as types from './types'
/** /** Options for {@link useField} hook. */
* Options for {@link useField} hook.
*/
export interface UseFieldOptions< export interface UseFieldOptions<
BaseValueType, BaseValueType,
Schema extends types.TSchema, Schema extends types.TSchema,
@ -21,9 +19,7 @@ export interface UseFieldOptions<
readonly defaultValue?: types.FieldValues<Schema>[TFieldName] | undefined readonly defaultValue?: types.FieldValues<Schema>[TFieldName] | undefined
} }
/** /** A hook that connects a field to a form state. */
* A hook that connects a field to a form state.
*/
export function useField< export function useField<
BaseValueType, BaseValueType,
Schema extends types.TSchema, Schema extends types.TSchema,

View File

@ -13,9 +13,7 @@ import type {
TSchema, TSchema,
} from './types' } from './types'
/** /** Options for the useFieldRegister hook. */
* Options for the useFieldRegister hook.
*/
export type UseFieldRegisterOptions< export type UseFieldRegisterOptions<
BaseValueType extends { value?: unknown }, BaseValueType extends { value?: unknown },
Schema extends TSchema, Schema extends TSchema,
@ -31,9 +29,7 @@ export type UseFieldRegisterOptions<
setValueAs?: ((value: unknown) => unknown) | undefined setValueAs?: ((value: unknown) => unknown) | undefined
} }
/** /** Registers a field in the form. */
* Registers a field in the form.
*/
export function useFieldRegister< export function useFieldRegister<
BaseValueType extends { value?: unknown }, BaseValueType extends { value?: unknown },
Schema extends TSchema, Schema extends TSchema,
@ -62,9 +58,7 @@ export function useFieldRegister<
return { fieldProps, formInstance } as const return { fieldProps, formInstance } as const
} }
/** /** Tried to extract validation details from the schema. */
* Tried to extract validation details from the schema.
*/
// This name is intentional to highlight that this function is unsafe and should be used with caution. // This name is intentional to highlight that this function is unsafe and should be used with caution.
// eslint-disable-next-line camelcase, @typescript-eslint/naming-convention // eslint-disable-next-line camelcase, @typescript-eslint/naming-convention
function unsafe__extractValidationDetailsFromSchema< function unsafe__extractValidationDetailsFromSchema<

View File

@ -7,9 +7,7 @@ import { useFormState } from 'react-hook-form'
import { useFormContext } from './FormProvider' import { useFormContext } from './FormProvider'
import type { FieldPath, FormInstanceValidated, TSchema } from './types' import type { FieldPath, FormInstanceValidated, TSchema } from './types'
/** /** Options for the `useFieldState` hook. */
* Options for the `useFieldState` hook.
*/
export interface UseFieldStateOptions< export interface UseFieldStateOptions<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,
@ -18,9 +16,7 @@ export interface UseFieldStateOptions<
readonly form?: FormInstanceValidated<Schema> | undefined readonly form?: FormInstanceValidated<Schema> | undefined
} }
/** /** Hook to get the state of a field. */
* Hook to get the state of a field.
*/
export function useFieldState<Schema extends TSchema, TFieldName extends FieldPath<Schema>>( export function useFieldState<Schema extends TSchema, TFieldName extends FieldPath<Schema>>(
options: UseFieldStateOptions<Schema, TFieldName>, options: UseFieldStateOptions<Schema, TFieldName>,
) { ) {

View File

@ -18,9 +18,7 @@ import { useMutation } from '@tanstack/react-query'
import * as schemaModule from './schema' import * as schemaModule from './schema'
import type * as types from './types' import type * as types from './types'
/** /** Maps the value to the event object. */
* Maps the value to the event object.
*/
function mapValueOnEvent(value: unknown) { function mapValueOnEvent(value: unknown) {
if (typeof value === 'object' && value != null && 'target' in value && 'type' in value) { if (typeof value === 'object' && value != null && 'target' in value && 'type' in value) {
return value return value
@ -241,9 +239,7 @@ export function useForm<Schema extends types.TSchema, SubmitResult = void>(
} }
} }
/** /** Get the type of arguments passed to the useForm hook */
* Get the type of arguments passed to the useForm hook
*/
function getArgsType<Schema extends types.TSchema, SubmitResult = void>( function getArgsType<Schema extends types.TSchema, SubmitResult = void>(
args: types.UseFormProps<Schema, SubmitResult>, args: types.UseFormProps<Schema, SubmitResult>,
) { ) {

View File

@ -5,9 +5,7 @@
*/ */
import * as twv from '#/utilities/tailwindVariants' import * as twv from '#/utilities/tailwindVariants'
/** /** Props for form components. */
* Props for form components.
*/
export type FormStyleProps = twv.VariantProps<typeof FORM_STYLES> export type FormStyleProps = twv.VariantProps<typeof FORM_STYLES>
export const FORM_STYLES = twv.tv({ export const FORM_STYLES = twv.tv({
base: 'flex flex-col items-start', base: 'flex flex-col items-start',

View File

@ -14,18 +14,14 @@ import type * as styles from './styles'
export type * from './components' export type * from './components'
/** /** Props for the Form component */
* Props for the Form component
*/
export type FormProps< export type FormProps<
Schema extends components.TSchema, Schema extends components.TSchema,
SubmitResult = void, SubmitResult = void,
> = BaseFormProps<Schema> & > = BaseFormProps<Schema> &
(FormPropsWithOptions<Schema, SubmitResult> | FormPropsWithParentForm<Schema>) (FormPropsWithOptions<Schema, SubmitResult> | FormPropsWithParentForm<Schema>)
/** /** Base props for the Form component. */
* Base props for the Form component.
*/
interface BaseFormProps<Schema extends components.TSchema> interface BaseFormProps<Schema extends components.TSchema>
extends Omit< extends Omit<
React.HTMLProps<HTMLFormElement>, React.HTMLProps<HTMLFormElement>,
@ -48,9 +44,7 @@ interface BaseFormProps<Schema extends components.TSchema>
readonly className?: string | ((props: components.UseFormReturn<Schema>) => string) readonly className?: string | ((props: components.UseFormReturn<Schema>) => string)
/** /** When set to `dialog`, form submission will close the parent dialog on successful submission. */
* When set to `dialog`, form submission will close the parent dialog on successful submission.
*/
readonly method?: 'dialog' | (NonNullable<unknown> & string) readonly method?: 'dialog' | (NonNullable<unknown> & string)
readonly canSubmitOffline?: boolean readonly canSubmitOffline?: boolean
@ -93,9 +87,7 @@ interface FormPropsWithOptions<Schema extends components.TSchema, SubmitResult =
readonly form?: never readonly form?: never
} }
/** /** Register function for a form field. */
* Register function for a form field.
*/
export type UseFormRegister<Schema extends components.TSchema> = < export type UseFormRegister<Schema extends components.TSchema> = <
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>, TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
>( >(
@ -103,9 +95,7 @@ export type UseFormRegister<Schema extends components.TSchema> = <
options?: reactHookForm.RegisterOptions<components.FieldValues<Schema>, TFieldName>, options?: reactHookForm.RegisterOptions<components.FieldValues<Schema>, TFieldName>,
) => UseFormRegisterReturn<Schema, TFieldName> ) => UseFormRegisterReturn<Schema, TFieldName>
/** /** UseFormRegister return type. */
* UseFormRegister return type.
*/
export interface UseFormRegisterReturn< export interface UseFormRegisterReturn<
Schema extends components.TSchema, Schema extends components.TSchema,
TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>, TFieldName extends components.FieldPath<Schema> = components.FieldPath<Schema>,
@ -119,9 +109,7 @@ export interface UseFormRegisterReturn<
readonly isInvalid?: boolean readonly isInvalid?: boolean
} }
/** /** Form Render Props. */
* Form Render Props.
*/
export type FormStateRenderProps<Schema extends components.TSchema> = Pick< export type FormStateRenderProps<Schema extends components.TSchema> = Pick<
components.FormInstance<Schema>, components.FormInstance<Schema>,
| 'clearErrors' | 'clearErrors'

View File

@ -32,9 +32,7 @@ import type { ExtractFunction, VariantProps } from '#/utilities/tailwindVariants
import { omit } from 'enso-common/src/utilities/data/object' import { omit } from 'enso-common/src/utilities/data/object'
import { INPUT_STYLES } from '../variants' import { INPUT_STYLES } from '../variants'
/** /** Props for the Input component. */
* Props for the Input component.
*/
export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
extends FieldStateProps<Omit<aria.InputProps, 'children' | 'size'>, Schema, TFieldName>, extends FieldStateProps<Omit<aria.InputProps, 'children' | 'size'>, Schema, TFieldName>,
FieldProps, FieldProps,
@ -53,9 +51,7 @@ export interface InputProps<Schema extends TSchema, TFieldName extends FieldPath
readonly fieldVariants?: FieldComponentProps<Schema>['variants'] readonly fieldVariants?: FieldComponentProps<Schema>['variants']
} }
/** /** Basic input component. Input component is a component that is used to get user input in a text field. */
* Basic input component. Input component is a component that is used to get user input in a text field.
*/
export const Input = forwardRef(function Input< export const Input = forwardRef(function Input<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,

View File

@ -79,9 +79,7 @@ export const MULTI_SELECTOR_STYLES = tv({
}, },
}) })
/** /** A horizontal multi-selector. */
* A horizontal multi-selector.
*/
export const MultiSelector = forwardRef(function MultiSelector< export const MultiSelector = forwardRef(function MultiSelector<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,

View File

@ -1,6 +1,4 @@
/** /** @file */
* @file
*/
import { mergeProps } from '#/components/aria' import { mergeProps } from '#/components/aria'
import { mergeRefs } from '#/utilities/mergeRefs' import { mergeRefs } from '#/utilities/mergeRefs'
import type { VariantProps } from '#/utilities/tailwindVariants' import type { VariantProps } from '#/utilities/tailwindVariants'
@ -23,9 +21,7 @@ import { Separator } from '../../Separator'
import { TEXT_STYLE } from '../../Text' import { TEXT_STYLE } from '../../Text'
import type { TestIdProps } from '../../types' import type { TestIdProps } from '../../types'
/** /** Props for an {@link OTPInput}. */
* Props for an {@link OTPInput}.
*/
export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
extends FieldStateProps<Omit<OTPInputProps, 'children' | 'render'>, Schema, TFieldName>, extends FieldStateProps<Omit<OTPInputProps, 'children' | 'render'>, Schema, TFieldName>,
FieldProps, FieldProps,
@ -40,9 +36,7 @@ export interface OtpInputProps<Schema extends TSchema, TFieldName extends FieldP
* @default true * @default true
*/ */
readonly submitOnComplete?: boolean readonly submitOnComplete?: boolean
/** /** Callback when the OTP is filled. */
* Callback when the OTP is filled.
*/
readonly onComplete?: () => void readonly onComplete?: () => void
} }
@ -82,9 +76,7 @@ const SLOT_STYLES = tv({
], ],
}) })
/** /** Accessible one-time password component with copy paste functionality. */
* Accessible one-time password component with copy paste functionality.
*/
export const OTPInput = forwardRef(function OTPInput< export const OTPInput = forwardRef(function OTPInput<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,
@ -196,9 +188,7 @@ export const OTPInput = forwardRef(function OTPInput<
) )
}) })
/** /** Props for a single {@link Slot}. */
* Props for a single {@link Slot}.
*/
interface SlotProps extends Omit<OTPInputSlotProps, 'isActive'>, VariantProps<typeof SLOT_STYLES> {} interface SlotProps extends Omit<OTPInputSlotProps, 'isActive'>, VariantProps<typeof SLOT_STYLES> {}
/** /**

View File

@ -1,6 +1,4 @@
/** /** @file A resizable input that uses a content-editable div. */
* @file A resizable input that uses a content-editable div.
*/
import { import {
useEffect, useEffect,
useRef, useRef,
@ -31,9 +29,7 @@ const CONTENT_EDITABLE_STYLES = tv({
slots: { placeholder: 'opacity-50 absolute inset-0 pointer-events-none' }, slots: { placeholder: 'opacity-50 absolute inset-0 pointer-events-none' },
}) })
/** /** Props for a {@link ResizableContentEditableInput}. */
* Props for a {@link ResizableContentEditableInput}.
*/
export interface ResizableContentEditableInputProps< export interface ResizableContentEditableInputProps<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,

View File

@ -1,6 +1,4 @@
/** /** @file A resizable input field. */
* @file A resizable input field.
*/
import * as React from 'react' import * as React from 'react'
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks' import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
@ -12,17 +10,13 @@ import * as mergeRefs from '#/utilities/mergeRefs'
import { forwardRef } from '#/utilities/react' import { forwardRef } from '#/utilities/react'
import * as variants from '../variants' import * as variants from '../variants'
/** /** Props for a {@link ResizableInput}. */
* Props for a {@link ResizableInput}.
*/
export interface ResizableInputProps extends aria.TextFieldProps { export interface ResizableInputProps extends aria.TextFieldProps {
readonly placeholder?: string readonly placeholder?: string
readonly description?: React.ReactNode readonly description?: React.ReactNode
} }
/** /** A resizable input field. */
* A resizable input field.
*/
export const ResizableInput = forwardRef(function ResizableInput( export const ResizableInput = forwardRef(function ResizableInput(
props: ResizableInputProps, props: ResizableInputProps,
ref: React.ForwardedRef<HTMLTextAreaElement>, ref: React.ForwardedRef<HTMLTextAreaElement>,

View File

@ -76,9 +76,7 @@ export const SELECTOR_STYLES = tv({
}, },
}) })
/** /** A horizontal selector. */
* A horizontal selector.
*/
export const Selector = forwardRef(function Selector< export const Selector = forwardRef(function Selector<
Schema extends TSchema, Schema extends TSchema,
TFieldName extends FieldPath<Schema>, TFieldName extends FieldPath<Schema>,

View File

@ -44,16 +44,12 @@ const RADIO_STYLES = twv.tv({
], ],
}) })
/** /** Props for the {@link Radio} component. */
* Props for the {@link Radio} component.
*/
export interface RadioProps extends aria.RadioProps { export interface RadioProps extends aria.RadioProps {
readonly label?: string readonly label?: string
} }
/** /** A radio button. */
* A radio button.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const Radio = forwardRef(function Radio( export const Radio = forwardRef(function Radio(
props: RadioProps, props: RadioProps,

View File

@ -16,9 +16,7 @@ import type { FieldVariantProps } from '../Form'
import * as formComponent from '../Form' import * as formComponent from '../Form'
import * as radioGroupContext from './RadioGroupContext' import * as radioGroupContext from './RadioGroupContext'
/** /** Props for {@link RadioGroup}. */
* Props for {@link RadioGroup}.
*/
export interface RadioGroupProps< export interface RadioGroupProps<
Schema extends formComponent.TSchema, Schema extends formComponent.TSchema,
TFieldName extends formComponent.FieldPath<Schema>, TFieldName extends formComponent.FieldPath<Schema>,
@ -39,9 +37,7 @@ export const RADIO_GROUP_STYLES = twv.tv({
variants: { fullWidth: { true: 'w-full' } }, variants: { fullWidth: { true: 'w-full' } },
}) })
/** /** A radio group component. */
* A radio group component.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const RadioGroup = forwardRef(function RadioGroup< export const RadioGroup = forwardRef(function RadioGroup<
Schema extends formComponent.TSchema, Schema extends formComponent.TSchema,

View File

@ -18,9 +18,7 @@ import invariant from 'tiny-invariant'
import * as eventCallback from '#/hooks/eventCallbackHooks' import * as eventCallback from '#/hooks/eventCallbackHooks'
/** /** Props for {@link RadioGroupContextProps} */
* Props for {@link RadioGroupContextProps}
*/
export interface RadioGroupContextProps { export interface RadioGroupContextProps {
/** /**
* Tells if a Radio element is being pressed * Tells if a Radio element is being pressed
@ -28,13 +26,9 @@ export interface RadioGroupContextProps {
* It's not the same as selected value, instead it stores the value user is clicking on at the moment * It's not the same as selected value, instead it stores the value user is clicking on at the moment
*/ */
readonly pressedRadio: string | null readonly pressedRadio: string | null
/** /** Sets the pressed Radio element */
* Sets the pressed Radio element
*/
readonly setPressedRadio: (value: string) => void readonly setPressedRadio: (value: string) => void
/** /** Clears the pressed Radio element */
* Clears the pressed Radio element
*/
readonly clearPressedRadio: () => void readonly clearPressedRadio: () => void
} }
@ -68,16 +62,12 @@ export function RadioGroupProvider(props: React.PropsWithChildren) {
return <RadioGroupContext.Provider value={value}>{children}</RadioGroupContext.Provider> return <RadioGroupContext.Provider value={value}>{children}</RadioGroupContext.Provider>
} }
/** /** Props for {@link useRadioGroupContext} */
* Props for {@link useRadioGroupContext}
*/
export interface UseRadioGroupContextProps { export interface UseRadioGroupContextProps {
readonly value: string readonly value: string
} }
/** /** Provides useful information about sibling Radio elements within a RadioGroup */
* Provides useful information about sibling Radio elements within a RadioGroup
*/
export function useRadioGroupContext(props: UseRadioGroupContextProps) { export function useRadioGroupContext(props: UseRadioGroupContextProps) {
const { value } = props const { value } = props
const context = React.useContext(RadioGroupContext) const context = React.useContext(RadioGroupContext)

View File

@ -9,18 +9,14 @@ import * as aria from '#/components/aria'
import * as twv from '#/utilities/tailwindVariants' import * as twv from '#/utilities/tailwindVariants'
/** /** The props for {@link Separator} component. */
* The props for {@link Separator} component.
*/
export interface SeparatorProps export interface SeparatorProps
extends aria.SeparatorProps, extends aria.SeparatorProps,
twv.VariantProps<typeof SEPARATOR_STYLES> { twv.VariantProps<typeof SEPARATOR_STYLES> {
readonly className?: string readonly className?: string
} }
/** /** The styles for the {@link Separator} component. */
* The styles for the {@link Separator} component.
*/
export const SEPARATOR_STYLES = twv.tv({ export const SEPARATOR_STYLES = twv.tv({
base: 'rounded-full border-none', base: 'rounded-full border-none',
variants: { variants: {
@ -79,9 +75,7 @@ export const SEPARATOR_STYLES = twv.tv({
], ],
}) })
/** /** A separator component. */
* A separator component.
*/
export function Separator(props: SeparatorProps) { export function Separator(props: SeparatorProps) {
const { orientation = 'horizontal', variant, className, size, ...rest } = props const { orientation = 'horizontal', variant, className, size, ...rest } = props

View File

@ -16,9 +16,7 @@ import { tv, type VariantProps } from '#/utilities/tailwindVariants'
import { Form, type FieldPath, type FieldProps, type FieldStateProps, type TSchema } from '../Form' import { Form, type FieldPath, type FieldProps, type FieldStateProps, type TSchema } from '../Form'
import { TEXT_STYLE } from '../Text' import { TEXT_STYLE } from '../Text'
/** /** Props for the {@Switch} component. */
* Props for the {@Switch} component.
*/
export interface SwitchProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>> export interface SwitchProps<Schema extends TSchema, TFieldName extends FieldPath<Schema>>
extends FieldStateProps< extends FieldStateProps<
Omit<AriaSwitchProps, 'children' | 'size' | 'value'> & { value: boolean }, Omit<AriaSwitchProps, 'children' | 'size' | 'value'> & { value: boolean },
@ -59,9 +57,7 @@ export const SWITCH_STYLES = tv({
}, },
}) })
/** /** A switch allows a user to turn a setting on or off. */
* A switch allows a user to turn a setting on or off.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const Switch = forwardRef(function Switch< export const Switch = forwardRef(function Switch<
Schema extends TSchema, Schema extends TSchema,

View File

@ -1,6 +1,4 @@
/** /** @file Text component */
* @file Text component
*/
import * as React from 'react' import * as React from 'react'
import * as aria from '#/components/aria' import * as aria from '#/components/aria'
@ -12,9 +10,7 @@ import { forwardRef } from '#/utilities/react'
import * as textProvider from './TextProvider' import * as textProvider from './TextProvider'
import * as visualTooltip from './useVisualTooltip' import * as visualTooltip from './useVisualTooltip'
/** /** Props for the Text component */
* Props for the Text component
*/
export interface TextProps export interface TextProps
extends Omit<aria.TextProps, 'color'>, extends Omit<aria.TextProps, 'color'>,
twv.VariantProps<typeof TEXT_STYLE> { twv.VariantProps<typeof TEXT_STYLE> {
@ -119,9 +115,7 @@ export const TEXT_STYLE = twv.tv({
}, },
}) })
/** /** Text component that supports truncation and show a tooltip on hover when text is truncated */
* Text component that supports truncation and show a tooltip on hover when text is truncated
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HTMLSpanElement>) { export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HTMLSpanElement>) {
const { const {
@ -216,17 +210,13 @@ export const Text = forwardRef(function Text(props: TextProps, ref: React.Ref<HT
Group: React.FC<React.PropsWithChildren> Group: React.FC<React.PropsWithChildren>
} }
/** /** Heading props */
* Heading props
*/
export interface HeadingProps extends Omit<TextProps, 'elementType'> { export interface HeadingProps extends Omit<TextProps, 'elementType'> {
// eslint-disable-next-line @typescript-eslint/no-magic-numbers // eslint-disable-next-line @typescript-eslint/no-magic-numbers
readonly level?: '1' | '2' | '3' | '4' | '5' | '6' | 1 | 2 | 3 | 4 | 5 | 6 readonly level?: '1' | '2' | '3' | '4' | '5' | '6' | 1 | 2 | 3 | 4 | 5 | 6
} }
/** /** Heading component */
* Heading component
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
const Heading = forwardRef(function Heading( const Heading = forwardRef(function Heading(
props: HeadingProps, props: HeadingProps,
@ -237,9 +227,7 @@ const Heading = forwardRef(function Heading(
}) })
Text.Heading = Heading Text.Heading = Heading
/** /** Text group component. It's used to visually group text elements together */
* Text group component. It's used to visually group text elements together
*/
Text.Group = function TextGroup(props: React.PropsWithChildren) { Text.Group = function TextGroup(props: React.PropsWithChildren) {
return ( return (
<textProvider.TextProvider value={{ isInsideTextComponent: true }}> <textProvider.TextProvider value={{ isInsideTextComponent: true }}>

View File

@ -5,13 +5,9 @@
*/ */
import * as React from 'react' import * as React from 'react'
/** /** Context for the Text component. */
* Context for the Text component.
*/
export interface TextContextType { export interface TextContextType {
/** /** Flag indicating whether the component is inside a Text component. */
* Flag indicating whether the component is inside a Text component.
*/
readonly isInsideTextComponent: boolean readonly isInsideTextComponent: boolean
} }
@ -19,9 +15,7 @@ const TextContext = React.createContext<TextContextType>({
isInsideTextComponent: false, isInsideTextComponent: false,
}) })
/** /** Hook to get the Text context. */
* Hook to get the Text context.
*/
export function useTextContext(): TextContextType { export function useTextContext(): TextContextType {
return React.useContext(TextContext) return React.useContext(TextContext)
} }

View File

@ -12,9 +12,7 @@ import * as aria from '#/components/aria'
import * as ariaComponents from '#/components/AriaComponents' import * as ariaComponents from '#/components/AriaComponents'
import Portal from '#/components/Portal' import Portal from '#/components/Portal'
/** /** Props for {@link useVisualTooltip}. */
* Props for {@link useVisualTooltip}.
*/
export interface VisualTooltipProps export interface VisualTooltipProps
extends Pick<ariaComponents.TooltipProps, 'maxWidth' | 'rounded' | 'size' | 'variant'> { extends Pick<ariaComponents.TooltipProps, 'maxWidth' | 'rounded' | 'size' | 'variant'> {
readonly children: React.ReactNode readonly children: React.ReactNode
@ -41,9 +39,7 @@ export interface VisualTooltipReturn {
readonly tooltip: JSX.Element | null readonly tooltip: JSX.Element | null
} }
/** /** The display strategy for the tooltip. */
* The display strategy for the tooltip.
*/
type DisplayStrategy = 'always' | 'whenOverflowing' type DisplayStrategy = 'always' | 'whenOverflowing'
const DEFAULT_OFFSET = 6 const DEFAULT_OFFSET = 6

View File

@ -8,16 +8,12 @@ import * as React from 'react'
import { forwardRef } from '#/utilities/react' import { forwardRef } from '#/utilities/react'
import * as twv from '#/utilities/tailwindVariants' import * as twv from '#/utilities/tailwindVariants'
/** /** Props for the {@link VisuallyHidden} component. */
* Props for the {@link VisuallyHidden} component.
*/
export type VisuallyHiddenProps = React.HTMLProps<HTMLElement> export type VisuallyHiddenProps = React.HTMLProps<HTMLElement>
export const VISUALLY_HIDDEN_STYLES = twv.tv({ base: 'sr-only' }) export const VISUALLY_HIDDEN_STYLES = twv.tv({ base: 'sr-only' })
/** /** A component visually hides its children from the screen, but keeps them accessible to screen readers. */
* A component visually hides its children from the screen, but keeps them accessible to screen readers.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const VisuallyHidden = forwardRef(function VisuallyHidden( export const VisuallyHidden = forwardRef(function VisuallyHidden(
props: VisuallyHiddenProps, props: VisuallyHiddenProps,

View File

@ -4,13 +4,9 @@
* Common types for ARIA components. * Common types for ARIA components.
*/ */
/** /** Props for adding a test id to a component */
* Props for adding a test id to a component
*/
export interface TestIdProps { export interface TestIdProps {
/** /** @deprecated Use `testid` instead */
* @deprecated Use `testid` instead
*/
readonly 'data-testid'?: string | undefined readonly 'data-testid'?: string | undefined
readonly testId?: string | undefined readonly testId?: string | undefined
} }

View File

@ -8,9 +8,7 @@ import { tv } from '#/utilities/tailwindVariants'
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import { TEXT_STYLE } from '../AriaComponents' import { TEXT_STYLE } from '../AriaComponents'
/** /** Props for the {@link Badge} component. */
* Props for the {@link Badge} component.
*/
export interface BadgeProps extends VariantProps<typeof BADGE_STYLES> { export interface BadgeProps extends VariantProps<typeof BADGE_STYLES> {
readonly children?: ReactNode readonly children?: ReactNode
readonly className?: string readonly className?: string
@ -61,9 +59,7 @@ export const BADGE_STYLES = tv({
}, },
}) })
/** /** Badges are used to highlight an item's status for quick recognition. */
* Badges are used to highlight an item's status for quick recognition.
*/
export function Badge(props: BadgeProps) { export function Badge(props: BadgeProps) {
const { children, color, rounded, className, variant } = props const { children, color, rounded, className, variant } = props

View File

@ -48,9 +48,7 @@ import * as backend from '#/services/Backend'
import LocalStorage, { type LocalStorageData } from '#/utilities/LocalStorage' import LocalStorage, { type LocalStorageData } from '#/utilities/LocalStorage'
import { unsafeEntries } from 'enso-common/src/utilities/data/object' import { unsafeEntries } from 'enso-common/src/utilities/data/object'
/** /** A component that provides a UI for toggling paywall features. */
* A component that provides a UI for toggling paywall features.
*/
export function EnsoDevtools() { export function EnsoDevtools() {
const { getText } = textProvider.useText() const { getText } = textProvider.useText()
const { authQueryKey, session } = authProvider.useAuth() const { authQueryKey, session } = authProvider.useAuth()

View File

@ -5,9 +5,7 @@
import type { PaywallFeatureName } from '#/hooks/billing' import type { PaywallFeatureName } from '#/hooks/billing'
import * as zustand from 'zustand' import * as zustand from 'zustand'
/** /** Configuration for a paywall feature. */
* Configuration for a paywall feature.
*/
export interface PaywallDevtoolsFeatureConfiguration { export interface PaywallDevtoolsFeatureConfiguration {
readonly isForceEnabled: boolean | null readonly isForceEnabled: boolean | null
} }
@ -62,9 +60,7 @@ export function useSetEnableVersionChecker() {
return zustand.useStore(ensoDevtoolsStore, (state) => state.setEnableVersionChecker) return zustand.useStore(ensoDevtoolsStore, (state) => state.setEnableVersionChecker)
} }
/** /** A hook that provides access to the paywall devtools. */
* A hook that provides access to the paywall devtools.
*/
export function usePaywallDevtools() { export function usePaywallDevtools() {
return zustand.useStore(ensoDevtoolsStore, (state) => ({ return zustand.useStore(ensoDevtoolsStore, (state) => ({
features: state.paywallFeatures, features: state.paywallFeatures,

View File

@ -1,6 +1,4 @@
/** /** @file Show the React Query Devtools. */
* @file Show the React Query Devtools.
*/
import * as React from 'react' import * as React from 'react'
import * as reactQuery from '@tanstack/react-query' import * as reactQuery from '@tanstack/react-query'

View File

@ -14,14 +14,10 @@ import * as offlineHooks from '#/hooks/offlineHooks'
import * as textProvider from '#/providers/TextProvider' import * as textProvider from '#/providers/TextProvider'
/** /** Props for {@link OfflineNotificationManager} */
* Props for {@link OfflineNotificationManager}
*/
export type OfflineNotificationManagerProps = Readonly<React.PropsWithChildren> export type OfflineNotificationManagerProps = Readonly<React.PropsWithChildren>
/** /** Context props for {@link OfflineNotificationManager} */
* Context props for {@link OfflineNotificationManager}
*/
interface OfflineNotificationManagerContextProps { interface OfflineNotificationManagerContextProps {
readonly isNested: boolean readonly isNested: boolean
readonly toastId?: string readonly toastId?: string
@ -30,9 +26,7 @@ interface OfflineNotificationManagerContextProps {
const OfflineNotificationManagerContext = const OfflineNotificationManagerContext =
React.createContext<OfflineNotificationManagerContextProps>({ isNested: false }) React.createContext<OfflineNotificationManagerContextProps>({ isNested: false })
/** /** Offline Notification Manager component. */
* Offline Notification Manager component.
*/
export function OfflineNotificationManager(props: OfflineNotificationManagerProps) { export function OfflineNotificationManager(props: OfflineNotificationManagerProps) {
const { children } = props const { children } = props
const toastId = 'offline' const toastId = 'offline'

View File

@ -17,17 +17,13 @@ import ContextMenuEntryBase from '#/components/ContextMenuEntry'
import * as paywallDialog from './PaywallDialog' import * as paywallDialog from './PaywallDialog'
/** /** Props for {@link ContextMenuEntry}. */
* Props for {@link ContextMenuEntry}.
*/
export interface ContextMenuEntryProps export interface ContextMenuEntryProps
extends Omit<contextMenuEntry.ContextMenuEntryProps, 'doAction' | 'isDisabled'> { extends Omit<contextMenuEntry.ContextMenuEntryProps, 'doAction' | 'isDisabled'> {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
} }
/** /** A context menu entry that opens a paywall dialog. */
* A context menu entry that opens a paywall dialog.
*/
export function ContextMenuEntry(props: ContextMenuEntryProps) { export function ContextMenuEntry(props: ContextMenuEntryProps) {
const { feature, ...rest } = props const { feature, ...rest } = props
const { setModal } = modalProvider.useSetModal() const { setModal } = modalProvider.useSetModal()

View File

@ -16,9 +16,7 @@ import * as ariaComponents from '#/components/AriaComponents'
import * as paywall from '#/components/Paywall' import * as paywall from '#/components/Paywall'
import SvgMask from '#/components/SvgMask' import SvgMask from '#/components/SvgMask'
/** /** Props for {@link PaywallAlert}. */
* Props for {@link PaywallAlert}.
*/
export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'children'> { export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'children'> {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
readonly label: string readonly label: string
@ -26,9 +24,7 @@ export interface PaywallAlertProps extends Omit<ariaComponents.AlertProps, 'chil
readonly upgradeButtonProps?: Omit<paywall.UpgradeButtonProps, 'feature'> readonly upgradeButtonProps?: Omit<paywall.UpgradeButtonProps, 'feature'>
} }
/** /** A paywall alert. */
* A paywall alert.
*/
export function PaywallAlert(props: PaywallAlertProps) { export function PaywallAlert(props: PaywallAlertProps) {
const { const {
label, label,

View File

@ -15,16 +15,12 @@ import * as ariaComponents from '#/components/AriaComponents'
import * as components from './components' import * as components from './components'
import * as upgradeButton from './UpgradeButton' import * as upgradeButton from './UpgradeButton'
/** /** Props for a {@link PaywallDialog}. */
* Props for a {@link PaywallDialog}.
*/
export interface PaywallDialogProps extends ariaComponents.DialogProps { export interface PaywallDialogProps extends ariaComponents.DialogProps {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
} }
/** /** A dialog that prompts the user to upgrade to a paid plan. */
* A dialog that prompts the user to upgrade to a paid plan.
*/
export function PaywallDialog(props: PaywallDialogProps) { export function PaywallDialog(props: PaywallDialogProps) {
const { feature, type = 'modal', title, ...dialogProps } = props const { feature, type = 'modal', title, ...dialogProps } = props

View File

@ -11,18 +11,14 @@ import * as ariaComponents from '#/components/AriaComponents'
import * as components from './components' import * as components from './components'
import * as paywallDialog from './PaywallDialog' import * as paywallDialog from './PaywallDialog'
/** /** Props for a {@link PaywallDialogButton}. */
* Props for a {@link PaywallDialogButton}.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export type PaywallDialogButtonProps = components.PaywallButtonProps & { export type PaywallDialogButtonProps = components.PaywallButtonProps & {
readonly dialogProps?: paywallDialog.PaywallDialogProps readonly dialogProps?: paywallDialog.PaywallDialogProps
readonly dialogTriggerProps?: ariaComponents.DialogTriggerProps readonly dialogTriggerProps?: ariaComponents.DialogTriggerProps
} }
/** /** A button that opens a paywall dialog when clicked */
* A button that opens a paywall dialog when clicked
*/
export function PaywallDialogButton(props: PaywallDialogButtonProps) { export function PaywallDialogButton(props: PaywallDialogButtonProps) {
const { feature, dialogProps, dialogTriggerProps, ...buttonProps } = props const { feature, dialogProps, dialogTriggerProps, ...buttonProps } = props

View File

@ -17,17 +17,13 @@ import * as ariaComponents from '#/components/AriaComponents'
import * as components from './components' import * as components from './components'
import * as upgradeButton from './UpgradeButton' import * as upgradeButton from './UpgradeButton'
/** /** Props for a {@link PaywallScreen}. */
* Props for a {@link PaywallScreen}.
*/
export interface PaywallScreenProps { export interface PaywallScreenProps {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
readonly className?: string readonly className?: string
} }
/** /** A screen that shows a paywall. */
* A screen that shows a paywall.
*/
export function PaywallScreen(props: PaywallScreenProps) { export function PaywallScreen(props: PaywallScreenProps) {
const { feature, className } = props const { feature, className } = props
const { getText } = textProvider.useText() const { getText } = textProvider.useText()

View File

@ -13,18 +13,14 @@ import * as textProvider from '#/providers/TextProvider'
import * as ariaComponents from '#/components/AriaComponents' import * as ariaComponents from '#/components/AriaComponents'
/** /** Props for an {@link UpgradeButton}. */
* Props for an {@link UpgradeButton}.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export type UpgradeButtonProps = Omit<ariaComponents.ButtonProps, 'variant'> & { export type UpgradeButtonProps = Omit<ariaComponents.ButtonProps, 'variant'> & {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
readonly variant?: ariaComponents.ButtonProps['variant'] readonly variant?: ariaComponents.ButtonProps['variant']
} }
/** /** A button that links to the upgrade page. */
* A button that links to the upgrade page.
*/
export function UpgradeButton(props: UpgradeButtonProps) { export function UpgradeButton(props: UpgradeButtonProps) {
const { const {
feature, feature,

View File

@ -16,17 +16,13 @@ import * as textProvider from '#/providers/TextProvider'
import * as ariaComponents from '#/components/AriaComponents' import * as ariaComponents from '#/components/AriaComponents'
import SvgMask from '#/components/SvgMask' import SvgMask from '#/components/SvgMask'
/** /** Props for a {@link PaywallBulletPoints}. */
* Props for a {@link PaywallBulletPoints}.
*/
export interface PaywallBulletPointsProps { export interface PaywallBulletPointsProps {
readonly bulletPointsTextId: text.TextId readonly bulletPointsTextId: text.TextId
readonly className?: string readonly className?: string
} }
/** /** A component that renders a list of bullet points for a paywall. */
* A component that renders a list of bullet points for a paywall.
*/
export function PaywallBulletPoints(props: PaywallBulletPointsProps) { export function PaywallBulletPoints(props: PaywallBulletPointsProps) {
const { bulletPointsTextId, className } = props const { bulletPointsTextId, className } = props

View File

@ -13,9 +13,7 @@ import * as textProvider from '#/providers/TextProvider'
import * as ariaComponents from '#/components/AriaComponents' import * as ariaComponents from '#/components/AriaComponents'
/** /** Props for {@link PaywallButton}. */
* Props for {@link PaywallButton}.
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export type PaywallButtonProps = ariaComponents.ButtonProps & { export type PaywallButtonProps = ariaComponents.ButtonProps & {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
@ -23,9 +21,7 @@ export type PaywallButtonProps = ariaComponents.ButtonProps & {
readonly showIcon?: boolean readonly showIcon?: boolean
} }
/** /** A styled button that shows that a feature is behind a paywall */
* A styled button that shows that a feature is behind a paywall
*/
export function PaywallButton(props: PaywallButtonProps) { export function PaywallButton(props: PaywallButtonProps) {
const { feature, iconOnly = false, showIcon = true, children, ...buttonProps } = props const { feature, iconOnly = false, showIcon = true, children, ...buttonProps } = props

View File

@ -1,6 +1,4 @@
/** /** @file A lock icon with a label indicating the paywall level required to access a feature. */
* @file A lock icon with a label indicating the paywall level required to access a feature.
*/
import * as tw from 'tailwind-merge' import * as tw from 'tailwind-merge'
import LockIcon from '#/assets/lock.svg' import LockIcon from '#/assets/lock.svg'
@ -12,17 +10,13 @@ import * as textProvider from '#/providers/TextProvider'
import * as ariaComponents from '#/components/AriaComponents' import * as ariaComponents from '#/components/AriaComponents'
import SvgMask from '#/components/SvgMask' import SvgMask from '#/components/SvgMask'
/** /** Props for a {@link PaywallLock}. */
* Props for a {@link PaywallLock}.
*/
export interface PaywallLockProps { export interface PaywallLockProps {
readonly feature: billingHooks.PaywallFeatureName readonly feature: billingHooks.PaywallFeatureName
readonly className?: string readonly className?: string
} }
/** /** A lock icon with a label indicating the paywall level required to access a feature. */
* A lock icon with a label indicating the paywall level required to access a feature.
*/
export function PaywallLock(props: PaywallLockProps) { export function PaywallLock(props: PaywallLockProps) {
const { feature, className } = props const { feature, className } = props
const { getText } = textProvider.useText() const { getText } = textProvider.useText()

View File

@ -8,9 +8,7 @@ import invariant from 'tiny-invariant'
const PortalContext = React.createContext<Element | null>(null) const PortalContext = React.createContext<Element | null>(null)
/** /** Allows to access the root element for the Portal component */
* Allows to access the root element for the Portal component
*/
export function usePortalContext() { export function usePortalContext() {
const root = React.useContext(PortalContext) const root = React.useContext(PortalContext)
@ -29,8 +27,6 @@ export function useStrictPortalContext() {
return root return root
} }
/** /** Specifies the root element for the Portal component */
* Specifies the root element for the Portal component
*/
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export const PortalProvider = PortalContext.Provider export const PortalProvider = PortalContext.Provider

View File

@ -4,9 +4,7 @@
*/ */
import type * as React from 'react' import type * as React from 'react'
/** /** The props for the Portal component */
* The props for the Portal component
*/
export interface PortalProps { export interface PortalProps {
/** /**
* Ref, where `<Portal />` should render its children * Ref, where `<Portal />` should render its children
@ -19,9 +17,7 @@ export interface PortalProps {
* @default false * @default false
*/ */
readonly isDisabled?: boolean readonly isDisabled?: boolean
/** /** Callback, will be called after portal's children mounted */
* Callback, will be called after portal's children mounted
*/
readonly onMount?: () => void readonly onMount?: () => void
readonly children?: React.ReactNode readonly children?: React.ReactNode
} }

View File

@ -19,9 +19,7 @@ import type * as stepperState from './useStepperState'
/** A prop with the given type, or a function to produce a value of the given type. */ /** A prop with the given type, or a function to produce a value of the given type. */
type StepProp<T> = T | ((props: RenderStepProps) => T) type StepProp<T> = T | ((props: RenderStepProps) => T)
/** /** Props for {@link Step} component. */
* Props for {@link Step} component.
*/
export interface StepProps extends RenderStepProps { export interface StepProps extends RenderStepProps {
readonly className?: StepProp<string | null | undefined> readonly className?: StepProp<string | null | undefined>
readonly icon?: StepProp<React.ReactElement | string | null | undefined> readonly icon?: StepProp<React.ReactElement | string | null | undefined>
@ -52,9 +50,7 @@ const STEP_STYLES = tv({
}, },
}) })
/** /** A step component is used to represent a single step in a stepper component. */
* A step component is used to represent a single step in a stepper component.
*/
export function Step(props: StepProps) { export function Step(props: StepProps) {
const { const {
index, index,

View File

@ -6,18 +6,14 @@ import type { ReactElement, ReactNode } from 'react'
import { useStepperContext } from './StepperProvider' import { useStepperContext } from './StepperProvider'
import type { RenderChildrenProps } from './types' import type { RenderChildrenProps } from './types'
/** /** Props for {@link StepContent} component. */
* Props for {@link StepContent} component.
*/
export interface StepContentProps { export interface StepContentProps {
readonly index: number readonly index: number
readonly children: ReactNode | ((props: RenderChildrenProps) => ReactNode) readonly children: ReactNode | ((props: RenderChildrenProps) => ReactNode)
readonly forceRender?: boolean readonly forceRender?: boolean
} }
/** /** Step content component. Renders the step content if the step is current or if `forceRender` is true. */
* Step content component. Renders the step content if the step is current or if `forceRender` is true.
*/
export function StepContent(props: StepContentProps): ReactElement | null { export function StepContent(props: StepContentProps): ReactElement | null {
const { index, children, forceRender = false } = props const { index, children, forceRender = false } = props
const { currentStep, goToStep, nextStep, previousStep, totalSteps } = useStepperContext() const { currentStep, goToStep, nextStep, previousStep, totalSteps } = useStepperContext()

View File

@ -19,9 +19,7 @@ import * as stepperProvider from './StepperProvider'
import type { BaseRenderProps, RenderChildrenProps, RenderStepProps } from './types' import type { BaseRenderProps, RenderChildrenProps, RenderStepProps } from './types'
import * as stepperState from './useStepperState' import * as stepperState from './useStepperState'
/** /** Props for {@link Stepper} component. */
* Props for {@link Stepper} component.
*/
export interface StepperProps { export interface StepperProps {
readonly state: stepperState.StepperState readonly state: stepperState.StepperState
readonly children: React.ReactNode | ((props: RenderChildrenProps) => React.ReactNode) readonly children: React.ReactNode | ((props: RenderChildrenProps) => React.ReactNode)
@ -48,9 +46,7 @@ const STEPPER_STYLES = tv({
const ANIMATION_OFFSET = 15 const ANIMATION_OFFSET = 15
/** /** A stepper component is used to indicate progress through a multi-step process. */
* A stepper component is used to indicate progress through a multi-step process.
*/
export function Stepper(props: StepperProps) { export function Stepper(props: StepperProps) {
const { renderStep, children, state } = props const { renderStep, children, state } = props
@ -77,9 +73,7 @@ export function Stepper(props: StepperProps) {
const style = typeof props.style === 'function' ? props.style(baseRenderProps) : props.style const style = typeof props.style === 'function' ? props.style(baseRenderProps) : props.style
/** /** Render children of the stepper component. */
* Render children of the stepper component.
*/
const renderChildren = () => { const renderChildren = () => {
const renderProps = { const renderProps = {
currentStep, currentStep,

View File

@ -9,9 +9,7 @@ import invariant from 'tiny-invariant'
import type { StepperState } from './useStepperState' import type { StepperState } from './useStepperState'
/** /** StepperProvider props */
* StepperProvider props
*/
export interface StepperContextType { export interface StepperContextType {
readonly currentStep: number readonly currentStep: number
readonly goToStep: (step: number) => void readonly goToStep: (step: number) => void

View File

@ -4,9 +4,7 @@
* Types for the stepper component. * Types for the stepper component.
*/ */
/** /** Render props for the stepper component. */
* Render props for the stepper component.
*/
export interface BaseRenderProps { export interface BaseRenderProps {
readonly goToStep: (step: number) => void readonly goToStep: (step: number) => void
readonly nextStep: () => void readonly nextStep: () => void
@ -15,21 +13,15 @@ export interface BaseRenderProps {
readonly totalSteps: number readonly totalSteps: number
} }
/** /** Render props for rendering children of the stepper component. */
* Render props for rendering children of the stepper component.
*/
export interface RenderChildrenProps extends BaseRenderProps { export interface RenderChildrenProps extends BaseRenderProps {
readonly isFirst: boolean readonly isFirst: boolean
readonly isLast: boolean readonly isLast: boolean
} }
/** /** Render props for lazy rendering of steps. */
* Render props for lazy rendering of steps.
*/
export interface RenderStepProps extends BaseRenderProps { export interface RenderStepProps extends BaseRenderProps {
/** /** The index of the step, starting from 0. */
* The index of the step, starting from 0.
*/
readonly index: number readonly index: number
readonly isCurrent: boolean readonly isCurrent: boolean
readonly isCompleted: boolean readonly isCompleted: boolean

View File

@ -9,30 +9,20 @@ import invariant from 'tiny-invariant'
import * as eventCallbackHooks from '#/hooks/eventCallbackHooks' import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
/** /** Direction of the stepper */
* Direction of the stepper
*/
type Direction = 'back-none' | 'back' | 'forward-none' | 'forward' | 'initial' type Direction = 'back-none' | 'back' | 'forward-none' | 'forward' | 'initial'
/** /** Props for {@link useStepperState} */
* Props for {@link useStepperState}
*/
export interface StepperStateProps { export interface StepperStateProps {
/** /** The default step to start on (0-indexed) */
* The default step to start on (0-indexed)
*/
readonly defaultStep?: number readonly defaultStep?: number
/** /** The number of steps in the stepper (amount of steps is 1-indexed) */
* The number of steps in the stepper (amount of steps is 1-indexed)
*/
readonly steps: number readonly steps: number
readonly onStepChange?: (step: number, direction: 'back' | 'forward') => void readonly onStepChange?: (step: number, direction: 'back' | 'forward') => void
readonly onCompleted?: () => void readonly onCompleted?: () => void
} }
/** /** State for a stepper component */
* State for a stepper component
*/
export interface StepperState { export interface StepperState {
readonly currentStep: number readonly currentStep: number
readonly onStepChange: (step: number) => void readonly onStepChange: (step: number) => void
@ -43,9 +33,7 @@ export interface StepperState {
readonly percentComplete: number readonly percentComplete: number
} }
/** /** Result of {@link useStepperState} */
* Result of {@link useStepperState}
*/
export interface UseStepperStateResult { export interface UseStepperStateResult {
readonly stepperState: StepperState readonly stepperState: StepperState
readonly direction: Direction readonly direction: Direction

View File

@ -18,9 +18,7 @@ import * as result from '#/components/Result'
import * as loader from './Loader' import * as loader from './Loader'
/** /** Props for {@link Suspense} component. */
* Props for {@link Suspense} component.
*/
export interface SuspenseProps extends React.SuspenseProps { export interface SuspenseProps extends React.SuspenseProps {
readonly loaderProps?: loader.LoaderProps readonly loaderProps?: loader.LoaderProps
readonly offlineFallback?: React.ReactNode readonly offlineFallback?: React.ReactNode

View File

@ -7,9 +7,7 @@ import { useInteractOutside } from '#/components/aria'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { useEventCallback } from './eventCallbackHooks' import { useEventCallback } from './eventCallbackHooks'
/** /** Props for the {@link useAutoFocus} hook. */
* Props for the {@link useAutoFocus} hook.
*/
export interface UseAutoFocusProps { export interface UseAutoFocusProps {
readonly ref: React.RefObject<HTMLElement> readonly ref: React.RefObject<HTMLElement>
readonly disabled?: boolean | undefined readonly disabled?: boolean | undefined

View File

@ -8,9 +8,7 @@ import type * as text from 'enso-common/src/text'
import * as backend from '#/services/Backend' import * as backend from '#/services/Backend'
/** /** Registered paywall features. */
* Registered paywall features.
*/
export const PAYWALL_FEATURES = { export const PAYWALL_FEATURES = {
userGroups: 'userGroups', userGroups: 'userGroups',
userGroupsFull: 'userGroupsFull', userGroupsFull: 'userGroupsFull',
@ -20,14 +18,10 @@ export const PAYWALL_FEATURES = {
shareFull: 'shareFull', shareFull: 'shareFull',
} as const } as const
/** /** Paywall features. */
* Paywall features.
*/
export type PaywallFeatureName = keyof typeof PAYWALL_FEATURES export type PaywallFeatureName = keyof typeof PAYWALL_FEATURES
/** /** Paywall level names */
* Paywall level names
*/
export type PaywallLevelName = backend.Plan | 'free' export type PaywallLevelName = backend.Plan | 'free'
/** /**
@ -42,9 +36,7 @@ export type PaywallLevelValue =
| (2 & { readonly name: PaywallLevelName; readonly label: text.TextId }) | (2 & { readonly name: PaywallLevelName; readonly label: text.TextId })
| (3 & { readonly name: PaywallLevelName; readonly label: text.TextId }) | (3 & { readonly name: PaywallLevelName; readonly label: text.TextId })
/** /** Paywall levels configuration. */
* Paywall levels configuration.
*/
export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = { export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = {
free: Object.assign(0, { name: 'free', label: 'freePlanName' } as const), free: Object.assign(0, { name: 'free', label: 'freePlanName' } as const),
[backend.Plan.solo]: Object.assign(1, { [backend.Plan.solo]: Object.assign(1, {
@ -61,14 +53,10 @@ export const PAYWALL_LEVELS: Record<PaywallLevelName, PaywallLevelValue> = {
} as const), } as const),
} }
/** /** Possible paywall unlock states for a user account. */
*
*/
export type PaywallLevel = (typeof PAYWALL_LEVELS)[keyof typeof PAYWALL_LEVELS] export type PaywallLevel = (typeof PAYWALL_LEVELS)[keyof typeof PAYWALL_LEVELS]
/** /** Paywall feature labels. */
* Paywall feature labels.
*/
const PAYWALL_FEATURES_LABELS: Record<PaywallFeatureName, text.TextId> = { const PAYWALL_FEATURES_LABELS: Record<PaywallFeatureName, text.TextId> = {
userGroups: 'userGroupsFeatureLabel', userGroups: 'userGroupsFeatureLabel',
userGroupsFull: 'userGroupsFullFeatureLabel', userGroupsFull: 'userGroupsFullFeatureLabel',
@ -88,18 +76,14 @@ const PAYWALL_FEATURE_META = {
shareFull: undefined, shareFull: undefined,
} satisfies { [K in PaywallFeatureName]: unknown } } satisfies { [K in PaywallFeatureName]: unknown }
/** /** Basic feature configuration. */
* Basic feature configuration.
*/
interface BasicFeatureConfiguration { interface BasicFeatureConfiguration {
readonly level: PaywallLevel readonly level: PaywallLevel
readonly bulletPointsTextId: `${PaywallFeatureName}FeatureBulletPoints` readonly bulletPointsTextId: `${PaywallFeatureName}FeatureBulletPoints`
readonly descriptionTextId: `${PaywallFeatureName}FeatureDescription` readonly descriptionTextId: `${PaywallFeatureName}FeatureDescription`
} }
/** /** Feature configuration. */
* Feature configuration.
*/
export type FeatureConfiguration<Key extends PaywallFeatureName = PaywallFeatureName> = export type FeatureConfiguration<Key extends PaywallFeatureName = PaywallFeatureName> =
BasicFeatureConfiguration & { BasicFeatureConfiguration & {
readonly name: Key readonly name: Key
@ -140,23 +124,17 @@ const PAYWALL_CONFIGURATION: Record<PaywallFeatureName, BasicFeatureConfiguratio
}, },
} }
/** /** Map a plan to a paywall level. */
* Map a plan to a paywall level.
*/
export function mapPlanOnPaywall(plan: backend.Plan | undefined): PaywallLevel { export function mapPlanOnPaywall(plan: backend.Plan | undefined): PaywallLevel {
return plan != null ? PAYWALL_LEVELS[plan] : PAYWALL_LEVELS.free return plan != null ? PAYWALL_LEVELS[plan] : PAYWALL_LEVELS.free
} }
/** /** Check if a given string is a valid feature name. */
* Check if a given string is a valid feature name.
*/
export function isFeatureName(name: string): name is PaywallFeatureName { export function isFeatureName(name: string): name is PaywallFeatureName {
return name in PAYWALL_FEATURES return name in PAYWALL_FEATURES
} }
/** /** Get the configuration for a given feature. */
* Get the configuration for a given feature.
*/
export function getFeatureConfiguration<Key extends PaywallFeatureName>( export function getFeatureConfiguration<Key extends PaywallFeatureName>(
feature: Key, feature: Key,
): FeatureConfiguration<Key> { ): FeatureConfiguration<Key> {

View File

@ -8,9 +8,7 @@ import * as eventCallbackHooks from '#/hooks/eventCallbackHooks'
import * as paywallFeaturesConfiguration from './FeaturesConfiguration' import * as paywallFeaturesConfiguration from './FeaturesConfiguration'
/** /** A hook that provides access to the paywall features configuration. */
* A hook that provides access to the paywall features configuration.
*/
export function usePaywallFeatures() { export function usePaywallFeatures() {
const getFeature = eventCallbackHooks.useEventCallback( const getFeature = eventCallbackHooks.useEventCallback(
<Key extends paywallFeaturesConfiguration.PaywallFeatureName>(feature: Key) => { <Key extends paywallFeaturesConfiguration.PaywallFeatureName>(feature: Key) => {

View File

@ -12,17 +12,13 @@ import type * as backend from '#/services/Backend'
import * as paywallConfiguration from './FeaturesConfiguration' import * as paywallConfiguration from './FeaturesConfiguration'
import * as paywallFeatures from './paywallFeaturesHooks' import * as paywallFeatures from './paywallFeaturesHooks'
/** /** Props for the {@link usePaywall} hook. */
* Props for the {@link usePaywall} hook.
*/
export interface UsePaywallProps { export interface UsePaywallProps {
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
readonly plan?: backend.Plan | undefined readonly plan?: backend.Plan | undefined
} }
/** /** A hook that provides paywall-related functionality. */
* A hook that provides paywall-related functionality.
*/
export function usePaywall(props: UsePaywallProps) { export function usePaywall(props: UsePaywallProps) {
const { plan } = props const { plan } = props

View File

@ -13,9 +13,7 @@ import * as toastAndLogHooks from '#/hooks/toastAndLogHooks'
import * as textProvider from '#/providers/TextProvider' import * as textProvider from '#/providers/TextProvider'
/** /** Props for the useCopy hook. */
* Props for the useCopy hook.
*/
export interface UseCopyProps { export interface UseCopyProps {
readonly copyText: string readonly copyText: string
readonly onCopy?: () => void readonly onCopy?: () => void
@ -24,9 +22,7 @@ export interface UseCopyProps {
const DEFAULT_TIMEOUT = 2000 const DEFAULT_TIMEOUT = 2000
/** /** A hook for copying text to the clipboard. */
* A hook for copying text to the clipboard.
*/
export function useCopy(props: UseCopyProps) { export function useCopy(props: UseCopyProps) {
const { copyText, onCopy, successToastMessage = true } = props const { copyText, onCopy, successToastMessage = true } = props

View File

@ -8,9 +8,7 @@ import * as React from 'react'
import * as callbackHooks from './eventCallbackHooks' import * as callbackHooks from './eventCallbackHooks'
import * as unmountEffect from './unmountHooks' import * as unmountEffect from './unmountHooks'
/** /** Wrap a callback into debounce function */
* Wrap a callback into debounce function
*/
export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>( export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>(
callback: Fn, callback: Fn,
deps: React.DependencyList, deps: React.DependencyList,
@ -83,9 +81,8 @@ export function useDebouncedCallback<Fn extends (...args: never[]) => unknown>(
}, [stableCallback, delay, maxWait, ...deps]) }, [stableCallback, delay, maxWait, ...deps])
} }
/** /** The type of a wrapped function that has been debounced. */
* export type DebouncedFunction<Fn extends (...args: never[]) => unknown> = (
*/ this: ThisParameterType<Fn>,
export interface DebouncedFunction<Fn extends (...args: never[]) => unknown> { ...args: Parameters<Fn>
(this: ThisParameterType<Fn>, ...args: Parameters<Fn>): void ) => void
}

View File

@ -9,9 +9,7 @@ import * as React from 'react'
import * as debouncedCallback from './debounceCallbackHooks' import * as debouncedCallback from './debounceCallbackHooks'
import * as eventCallbackHooks from './eventCallbackHooks' import * as eventCallbackHooks from './eventCallbackHooks'
/** /** A hook that returns a stateful value, and a function to update it that will debounce updates. */
* A hook that returns a stateful value, and a function to update it that will debounce updates.
*/
export function useDebounceState<S>( export function useDebounceState<S>(
initialState: S | (() => S), initialState: S | (() => S),
delay: number, delay: number,

View File

@ -5,9 +5,7 @@
*/ */
import * as debounceState from './debounceStateHooks' import * as debounceState from './debounceStateHooks'
/** /** Debounce a value. */
* Debounce a value.
*/
export function useDebounceValue<T>(value: T, delay: number, maxWait?: number) { export function useDebounceValue<T>(value: T, delay: number, maxWait?: number) {
const [debouncedValue, setDebouncedValue] = debounceState.useDebounceState(value, delay, maxWait) const [debouncedValue, setDebouncedValue] = debounceState.useDebounceState(value, delay, maxWait)

View File

@ -34,9 +34,7 @@ export function useMounted(callback: () => void) {
}, [stableCallback]) }, [stableCallback])
} }
/** /** Returns a function that returns `true` if the component renders for the first time. */
* Returns a function that returns `true` if the component renders for the first time.
*/
export function useIsFirstRender() { export function useIsFirstRender() {
const isFirstMount = useRef(true) const isFirstMount = useRef(true)
const stableCallbackTrue = useEventCallback(() => true) const stableCallbackTrue = useEventCallback(() => true)

View File

@ -9,9 +9,7 @@ import * as reactQuery from '@tanstack/react-query'
import * as eventCallback from '#/hooks/eventCallbackHooks' import * as eventCallback from '#/hooks/eventCallbackHooks'
/** /** Hook to get the offline status */
* Hook to get the offline status
*/
export function useOffline() { export function useOffline() {
const isOnline = React.useSyncExternalStore( const isOnline = React.useSyncExternalStore(
reactQuery.onlineManager.subscribe.bind(reactQuery.onlineManager), reactQuery.onlineManager.subscribe.bind(reactQuery.onlineManager),
@ -22,17 +20,13 @@ export function useOffline() {
return { isOffline: !isOnline } return { isOffline: !isOnline }
} }
/** /** Props for the {@link useOfflineChange} hook */
* Props for the {@link useOfflineChange} hook
*/
export interface UseOfflineChangeProps { export interface UseOfflineChangeProps {
readonly triggerImmediate?: boolean | 'if-offline' | 'if-online' readonly triggerImmediate?: boolean | 'if-offline' | 'if-online'
readonly isDisabled?: boolean readonly isDisabled?: boolean
} }
/** /** Hook to subscribe to online/offline changes */
* Hook to subscribe to online/offline changes
*/
export function useOfflineChange( export function useOfflineChange(
callback: (isOffline: boolean) => void, callback: (isOffline: boolean) => void,
props: UseOfflineChangeProps = {}, props: UseOfflineChangeProps = {},

View File

@ -18,9 +18,7 @@ import * as safeJsonParse from '#/utilities/safeJsonParse'
// === SearchParamsStateReturnType === // === SearchParamsStateReturnType ===
// =================================== // ===================================
/** /** The return type of the `useSearchParamsState` hook. */
* The return type of the `useSearchParamsState` hook.
*/
type SearchParamsStateReturnType<T> = Readonly< type SearchParamsStateReturnType<T> = Readonly<
[ [
value: T, value: T,
@ -29,9 +27,7 @@ type SearchParamsStateReturnType<T> = Readonly<
] ]
> >
/** /** Set options for the `set` function. */
* Set options for the `set` function.
*/
export interface SearchParamsSetOptions { export interface SearchParamsSetOptions {
readonly replace?: boolean readonly replace?: boolean
} }

Some files were not shown because too many files have changed in this diff Show More