ng-primitives 0.96.0 → 0.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import { coerceElement } from '@angular/cdk/coercion';
2
- import { InjectionToken, signal, inject, computed, linkedSignal, isSignal, Injector, ElementRef, runInInjectionContext, afterRenderEffect, NgZone, DestroyRef, forwardRef, ChangeDetectorRef } from '@angular/core';
2
+ import { isPlatformBrowser } from '@angular/common';
3
+ import { InjectionToken, signal, inject, computed, linkedSignal, isSignal, Injector, ElementRef, runInInjectionContext, NgZone, DestroyRef, afterRenderEffect, forwardRef, ChangeDetectorRef, PLATFORM_ID, effect } from '@angular/core';
3
4
  import { Subject } from 'rxjs';
4
5
 
5
6
  /* eslint-disable @angular-eslint/no-uncalled-signals */
@@ -192,11 +193,9 @@ function setAttribute(element, attr, value) {
192
193
  }
193
194
  }
194
195
  function attrBinding(element, attr, value) {
195
- afterRenderEffect({
196
- write: () => {
197
- const valueResult = typeof value === 'function' ? value() : value;
198
- setAttribute(element, attr, valueResult?.toString() ?? null);
199
- },
196
+ isomorphicEffect(() => {
197
+ const valueResult = typeof value === 'function' ? value() : value;
198
+ setAttribute(element, attr, valueResult?.toString() ?? null);
200
199
  });
201
200
  }
202
201
  function getStyleUnit(style) {
@@ -227,39 +226,35 @@ function getStyleUnit(style) {
227
226
  return '';
228
227
  }
229
228
  function styleBinding(element, style, value) {
230
- afterRenderEffect({
231
- write: () => {
232
- const styleValue = typeof value === 'function' ? value() : value;
233
- // we should look for units in the style name, just like Angular does e.g. width.px
234
- const styleUnit = getStyleUnit(style);
235
- const styleName = styleUnit ? style.replace(`.${styleUnit}`, '') : style;
236
- if (styleValue !== null) {
237
- element.nativeElement.style.setProperty(styleName, styleValue + styleUnit);
238
- }
239
- else {
240
- element.nativeElement.style.removeProperty(styleName);
241
- }
242
- },
229
+ isomorphicEffect(() => {
230
+ const styleValue = typeof value === 'function' ? value() : value;
231
+ // we should look for units in the style name, just like Angular does e.g. width.px
232
+ const styleUnit = getStyleUnit(style);
233
+ const styleName = styleUnit ? style.replace(`.${styleUnit}`, '') : style;
234
+ if (styleValue !== null) {
235
+ element.nativeElement.style.setProperty(styleName, styleValue + styleUnit);
236
+ }
237
+ else {
238
+ element.nativeElement.style.removeProperty(styleName);
239
+ }
243
240
  });
244
241
  }
245
242
  function dataBinding(element, attr, value) {
246
243
  if (!attr.startsWith('data-')) {
247
244
  throw new Error(`dataBinding: attribute "${attr}" must start with "data-"`);
248
245
  }
249
- afterRenderEffect({
250
- write: () => {
251
- let valueResult = typeof value === 'function' ? value() : value;
252
- if (valueResult === false) {
253
- valueResult = null;
254
- }
255
- else if (valueResult === true) {
256
- valueResult = '';
257
- }
258
- else if (valueResult !== null && typeof valueResult !== 'string') {
259
- valueResult = String(valueResult);
260
- }
261
- setAttribute(element, attr, valueResult);
262
- },
246
+ isomorphicEffect(() => {
247
+ let valueResult = typeof value === 'function' ? value() : value;
248
+ if (valueResult === false) {
249
+ valueResult = null;
250
+ }
251
+ else if (valueResult === true) {
252
+ valueResult = '';
253
+ }
254
+ else if (valueResult !== null && typeof valueResult !== 'string') {
255
+ valueResult = String(valueResult);
256
+ }
257
+ setAttribute(element, attr, valueResult);
263
258
  });
264
259
  }
265
260
  function listener(element, event, handler, options) {
@@ -329,6 +324,17 @@ function emitter({ injector = inject(Injector), } = {}) {
329
324
  };
330
325
  });
331
326
  }
327
+ function isomorphicEffect(callback) {
328
+ const injector = inject(Injector);
329
+ const platformId = injector.get(PLATFORM_ID);
330
+ if (isPlatformBrowser(platformId)) {
331
+ afterRenderEffect(() => callback());
332
+ }
333
+ else {
334
+ // On the server, we just run the effect immediately
335
+ effect(() => callback());
336
+ }
337
+ }
332
338
 
333
339
  /**
334
340
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ng-primitives-state.mjs","sources":["../../../../packages/ng-primitives/state/src/index.ts","../../../../packages/ng-primitives/state/src/ng-primitives-state.ts"],"sourcesContent":["/* eslint-disable @angular-eslint/no-uncalled-signals */\nimport { coerceElement } from '@angular/cdk/coercion';\nimport {\n afterRenderEffect,\n ChangeDetectorRef,\n computed,\n DestroyRef,\n ElementRef,\n FactoryProvider,\n forwardRef,\n inject,\n InjectionToken,\n InjectOptions,\n Injector,\n InputSignal,\n InputSignalWithTransform,\n isSignal,\n linkedSignal,\n NgZone,\n ProviderToken,\n runInInjectionContext,\n signal,\n Signal,\n WritableSignal,\n} from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\n\n/**\n * This converts the state object to a writable state object.\n * This means that inputs become signals which are writable.\n */\nexport type State<T> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends InputSignalWithTransform<infer U, any>\n ? WritableSignal<U>\n : T[K] extends InputSignal<infer R>\n ? WritableSignal<R>\n : T[K];\n};\n\n/**\n * This is similar to the state object, but we don't expose properties that are not\n * inputs.\n */\nexport type CreatedState<T> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends InputSignalWithTransform<infer U, any>\n ? WritableSignal<U>\n : T[K] extends InputSignal<infer R>\n ? WritableSignal<R>\n : never;\n};\n\nexport type InjectedState<T> = Signal<State<T>>;\n\n/**\n * Create a new injection token for the state.\n * @param description The description of the token\n */\nexport function createStateToken<T>(description: string): InjectionToken<T> {\n return new InjectionToken<Signal<State<T>>>(`Ngp${description}StateToken`);\n}\n\nexport interface CreateStateProviderOptions {\n /**\n * Whether we should check for the state in the parent injector.\n */\n inherit?: boolean;\n}\n\n/**\n * Create a new provider for the state. It first tries to inject the state from the parent injector,\n * as this allows for the state to be hoisted to a higher level in the component tree. This can\n * be useful to avoid issues where the injector can't be shared in some cases when ng-content is used.\n * @param token The token for the state\n */\nexport function createStateProvider<T>(\n token: ProviderToken<T>,\n): (options?: CreateStateProviderOptions) => FactoryProvider {\n return ({ inherit }: CreateStateProviderOptions = {}) => ({\n provide: token,\n useFactory: () => {\n if (inherit === false) {\n // if we are not checking the parent, we want to create a new state\n return signal({});\n }\n // if we are checking the parent, we want to check if the state is already defined\n return inject(token, { optional: true, skipSelf: true }) ?? signal({});\n },\n });\n}\n\ntype CreateStateInjectorOptions = {\n /**\n * Whether the state may not be immediately available. This can happen when the child is instantiated before the parent.\n */\n deferred?: boolean;\n};\n\n/**\n * Create a new state injector for the state.\n * @param token The token for the state\n */\nexport function createStateInjector<T>(\n token: ProviderToken<State<T>>,\n options: { deferred: true },\n): <U = T>(injectOptions?: InjectOptions) => Signal<State<U> | undefined>;\nexport function createStateInjector<T>(\n token: ProviderToken<State<T>>,\n options?: CreateStateInjectorOptions,\n): <U = T>(injectOptions?: InjectOptions) => Signal<State<U>>;\nexport function createStateInjector<T>(\n token: ProviderToken<State<T>>,\n options: CreateStateInjectorOptions = {},\n): <U = T>(injectOptions?: InjectOptions) => Signal<State<U> | undefined> {\n return <U = T>(injectOptions: InjectOptions = {}) => {\n const value = inject(token, injectOptions) as Signal<State<U> | undefined> | null;\n\n if (options.deferred) {\n return computed(() =>\n value && Object.keys(value() ?? {}).length === 0 ? undefined : value?.(),\n ) as Signal<State<U> | undefined>;\n }\n\n return (value as Signal<State<U>>) ?? signal(undefined);\n };\n}\n\n/**\n * Convert the original state object into a writable state object.\n * @param token The token for the state\n */\nexport function createState(token: ProviderToken<WritableSignal<State<unknown>>>) {\n return <U>(state: U): CreatedState<U> => {\n const internalState = inject(token);\n\n internalState.update(obj => {\n // Iterating over properties\n for (const key in state) {\n const value = state[key as keyof U];\n\n // We want to make this a controlled input if it is an InputSignal or InputSignalWithTransform\n if (isSignalInput(value)) {\n // @ts-ignore\n obj[key] = createControlledInput(value);\n } else {\n // @ts-ignore\n obj[key] = value;\n }\n }\n\n // Iterating over prototype methods\n const prototype = Object.getPrototypeOf(state);\n\n for (const key of Object.getOwnPropertyNames(prototype)) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n\n // if this is a getter or setter, we need to define it on the object\n if (descriptor?.get || descriptor?.set) {\n Object.defineProperty(obj, key, descriptor);\n } else if (typeof prototype[key as keyof U] === 'function') {\n (obj as Record<string, unknown>)[key] = prototype[key as keyof U].bind(state);\n } else {\n // @ts-ignore\n obj[key] = prototype[key as keyof U];\n }\n }\n\n return { ...obj };\n });\n\n return internalState() as unknown as CreatedState<U>;\n };\n}\n\n// this is a bit hacky, but we need to do it to track whether this is controlled\nfunction createControlledInput(\n property: InputSignal<unknown> | InputSignalWithTransform<unknown, unknown>,\n): WritableSignal<unknown> {\n const value = signal(property());\n let isControlled = false;\n\n const symbol = Object.getOwnPropertySymbols(property).find(s => s.description === 'SIGNAL');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputDefinition = symbol ? (property as any)[symbol] : undefined;\n\n if (\n !symbol ||\n !inputDefinition ||\n typeof inputDefinition.applyValueToInputSignal !== 'function'\n ) {\n console.warn(\n 'Angular has changed its internal Input implementation, report this issue to ng-primitives.',\n );\n // fallback to a linked signal which is partially controlled\n return linkedSignal(() => property());\n }\n\n const originalApply = inputDefinition.applyValueToInputSignal.bind(inputDefinition);\n const originalSet = value.set.bind(value);\n const originalUpdate = value.update.bind(value);\n\n inputDefinition.applyValueToInputSignal = (inputSignalNode: unknown, newValue: unknown) => {\n isControlled = true;\n originalSet(newValue);\n originalApply(inputSignalNode, newValue);\n };\n\n value.set = (newValue: unknown) => {\n if (!isControlled) {\n originalSet(newValue);\n }\n };\n\n value.update = (updateFn: (value: unknown) => unknown) => {\n if (!isControlled) {\n originalUpdate(updateFn);\n }\n };\n\n return value;\n}\n\nfunction isSignalInput(\n property: unknown,\n): property is InputSignal<unknown> | InputSignalWithTransform<unknown, unknown> {\n if (!isSignal(property)) {\n return false;\n }\n\n const symbol = Object.getOwnPropertySymbols(property).find(s => s.description === 'SIGNAL');\n\n if (!symbol) {\n return false;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputDefinition = symbol ? (property as any)[symbol] : undefined;\n\n if (!inputDefinition) {\n return false;\n }\n\n return 'transformFn' in inputDefinition || 'applyValueToInputSignal' in inputDefinition;\n}\n\nexport interface CreatePrimitiveOptions {\n injector?: Injector;\n elementRef?: ElementRef<HTMLElement>;\n}\n\ntype PrimitiveState<TFactory extends (...args: any[]) => unknown> = TFactory extends (\n props: unknown,\n) => infer R\n ? R\n : TFactory extends (...args: any[]) => infer R\n ? R\n : never;\n\ntype BasePrimitiveInjectionFn<TState> = {\n (): Signal<TState>;\n (options: { hoisted: true; optional?: boolean; skipSelf?: boolean }): Signal<TState | null>;\n (options?: {\n hoisted?: boolean;\n optional?: boolean;\n skipSelf?: boolean;\n }): Signal<TState | null> | Signal<TState>;\n};\n\ntype PrimitiveInjectionFn<TFactory extends (...args: any[]) => unknown> = TFactory extends (\n props: unknown,\n) => infer R\n ? {\n (): Signal<R>;\n (options: { hoisted: true; optional?: boolean; skipSelf?: boolean }): Signal<R | null>;\n (options?: {\n hoisted?: boolean;\n optional?: boolean;\n skipSelf?: boolean;\n }): Signal<R | null> | Signal<R>;\n }\n : BasePrimitiveInjectionFn<PrimitiveState<TFactory>>;\n\nexport function createPrimitive<TFactory extends (...args: any[]) => unknown>(\n name: string,\n fn: TFactory,\n options?: CreatePrimitiveOptions,\n): [\n InjectionToken<WritableSignal<PrimitiveState<TFactory>>>,\n TFactory,\n PrimitiveInjectionFn<TFactory>,\n (opts?: { inherit?: boolean }) => FactoryProvider,\n];\nexport function createPrimitive<TFactory extends (...args: any[]) => unknown>(\n name: string,\n fn: TFactory,\n options: CreatePrimitiveOptions = {},\n): [\n InjectionToken<WritableSignal<PrimitiveState<TFactory>>>,\n TFactory,\n PrimitiveInjectionFn<TFactory>,\n (opts?: { inherit?: boolean }) => FactoryProvider,\n] {\n // Create a unique injection token for the primitive's state signal\n const token = new InjectionToken<WritableSignal<PrimitiveState<TFactory>>>(`Primitive: ${name}`);\n\n // Create the state signal within the appropriate injection context\n const factory = ((props: Parameters<TFactory>[0]) => {\n // determine the injector to use\n let injector = options.injector ?? inject(Injector);\n\n // If an ElementRef is provided in options, create a child injector\n if (options.elementRef) {\n injector = Injector.create({\n providers: [{ provide: ElementRef, useValue: options.elementRef }],\n parent: injector,\n });\n }\n\n return runInInjectionContext(injector, () => {\n const state = inject(token, { optional: true });\n const instance = fn(props);\n state?.set(instance as PrimitiveState<TFactory>);\n return instance;\n });\n }) as TFactory;\n\n // create an injection function that provides the state signal\n function injectFn<T = PrimitiveState<TFactory>>(): Signal<T>;\n function injectFn<T = PrimitiveState<TFactory>>(\n options: { hoisted: true } & InjectOptions,\n ): Signal<T | null>;\n function injectFn<T = PrimitiveState<TFactory>>(options?: {\n hoisted?: boolean;\n optional?: boolean;\n skipSelf?: boolean;\n }): Signal<T | null> | Signal<T> {\n const hoisted = options?.hoisted ?? false;\n const optional = options?.optional ?? false;\n const skipSelf = options?.skipSelf ?? false;\n\n if (hoisted || optional) {\n return (inject(token, { optional: true, skipSelf }) ??\n signal(null)) as unknown as Signal<T | null>;\n }\n\n return inject(token, { optional, skipSelf }) as unknown as Signal<T>;\n }\n\n // create a function to provide the state\n const provideFn = (opts?: { inherit?: boolean }): FactoryProvider => {\n const inherit = opts?.inherit ?? true;\n return {\n provide: token,\n useFactory: () => {\n if (inherit === false) {\n return signal(null);\n }\n\n return inject(token, { optional: true, skipSelf: true }) ?? signal(null);\n },\n };\n };\n\n return [token, factory as TFactory, injectFn as PrimitiveInjectionFn<TFactory>, provideFn];\n}\n\nexport function controlled<T>(value: Signal<T>): WritableSignal<T> {\n return linkedSignal(() => value());\n}\n\nfunction setAttribute(\n element: ElementRef<HTMLElement>,\n attr: string,\n value: string | null | undefined,\n): void {\n // if the attribute is \"disabled\" and the value is 'false', we need to remove the attribute\n if (attr === 'disabled' && value === 'false') {\n element.nativeElement.removeAttribute(attr);\n return;\n }\n\n if (value !== null && value !== undefined) {\n element.nativeElement.setAttribute(attr, value);\n } else {\n element.nativeElement.removeAttribute(attr);\n }\n}\n\nexport function attrBinding(\n element: ElementRef<HTMLElement>,\n attr: string,\n value:\n | (() => string | number | boolean | null | undefined)\n | string\n | number\n | boolean\n | null\n | undefined,\n): void {\n afterRenderEffect({\n write: () => {\n const valueResult = typeof value === 'function' ? value() : value;\n\n setAttribute(element, attr, valueResult?.toString() ?? null);\n },\n });\n}\n\nfunction getStyleUnit(style: string): string {\n const parts = style.split('.');\n\n if (parts.length > 1) {\n const unit = parts[parts.length - 1];\n\n switch (unit) {\n case 'px':\n case 'em':\n case 'rem':\n case '%':\n case 'vh':\n case 'vw':\n case 'vmin':\n case 'vmax':\n case 'cm':\n case 'mm':\n case 'in':\n case 'pt':\n case 'pc':\n case 'ex':\n case 'ch':\n return unit;\n default:\n return '';\n }\n }\n\n return '';\n}\n\nexport function styleBinding(\n element: ElementRef<HTMLElement>,\n style: string,\n value: (() => string | number | null) | string | number | null,\n): void {\n afterRenderEffect({\n write: () => {\n const styleValue = typeof value === 'function' ? value() : value;\n // we should look for units in the style name, just like Angular does e.g. width.px\n const styleUnit = getStyleUnit(style);\n const styleName = styleUnit ? style.replace(`.${styleUnit}`, '') : style;\n\n if (styleValue !== null) {\n element.nativeElement.style.setProperty(styleName, styleValue + styleUnit);\n } else {\n element.nativeElement.style.removeProperty(styleName);\n }\n },\n });\n}\n\nexport function dataBinding(\n element: ElementRef<HTMLElement>,\n attr: string,\n value: (() => string | boolean | null) | string | boolean | null,\n): void {\n if (!attr.startsWith('data-')) {\n throw new Error(`dataBinding: attribute \"${attr}\" must start with \"data-\"`);\n }\n\n afterRenderEffect({\n write: () => {\n let valueResult = typeof value === 'function' ? value() : value;\n\n if (valueResult === false) {\n valueResult = null;\n } else if (valueResult === true) {\n valueResult = '';\n } else if (valueResult !== null && typeof valueResult !== 'string') {\n valueResult = String(valueResult);\n }\n\n setAttribute(element, attr, valueResult);\n },\n });\n}\nexport function listener<K extends keyof HTMLElementEventMap>(\n element: HTMLElement | ElementRef<HTMLElement> | Document,\n event: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n options?: { injector?: Injector; config?: AddEventListenerOptions | boolean },\n): () => void;\nexport function listener(\n element: HTMLElement | ElementRef<HTMLElement> | Document,\n event: string,\n handler: (event: Event) => void,\n options?: { injector?: Injector; config?: AddEventListenerOptions | boolean },\n): () => void;\nexport function listener<K extends keyof HTMLElementEventMap>(\n element: HTMLElement | ElementRef<HTMLElement> | Document,\n event: K | string,\n handler: (event: HTMLElementEventMap[K] | Event) => void,\n options?: { injector?: Injector; config?: AddEventListenerOptions | boolean },\n): () => void {\n return runInInjectionContext(options?.injector ?? inject(Injector), () => {\n const ngZone = inject(NgZone);\n const destroyRef = inject(DestroyRef);\n const nativeElement = coerceElement(element);\n\n const removeListener = () =>\n nativeElement.removeEventListener(event, handler as EventListener, options?.config);\n destroyRef.onDestroy(removeListener);\n ngZone.runOutsideAngular(() =>\n nativeElement.addEventListener(event, handler as EventListener, options?.config),\n );\n\n return removeListener;\n });\n}\n\nexport function onMount(callback: () => void): void {\n const injector = inject(Injector);\n afterRenderEffect(() => runInInjectionContext(injector, callback), { injector });\n}\n\nexport function onDestroy(callback: () => void): void {\n const destroyRef = inject(DestroyRef);\n destroyRef.onDestroy(callback);\n}\n\n/**\n * Previously, with our state approach, we allowed signals to be written directly using their setters.\n * However, with our new approach, we want people to use the appropriate set method instead. This function takes in a writable\n * signal and returns a proxy that warns the user when set is called directly.\n */\nexport function deprecatedSetter<T>(\n signal: WritableSignal<T>,\n methodName: string,\n): WritableSignal<T> {\n return new Proxy(signal, {\n get(target, prop) {\n if (prop === 'set') {\n return (value: T) => {\n console.warn(\n `Deprecation warning: Use ${methodName}() instead of setting the value directly.`,\n );\n target.set(value);\n };\n }\n return target[prop as keyof WritableSignal<T>];\n },\n });\n}\n\n/**\n * A utility function to inject an inherited state from a parent injector. This is useful for cases\n * where a primitive needs to inherit state from a parent primitive, such as in roving focus groups.\n * We could use inject with a forwardRef, but forwardRef returns an any - no thanks...\n */\nexport function injectInheritedState<T>(\n token: () => InjectionToken<T>,\n injectOptions: InjectOptions = {},\n): T | null {\n return (\n inject<T>(\n forwardRef(() => token()),\n { optional: true, skipSelf: true, ...injectOptions },\n ) ?? null\n );\n}\n\nexport interface Emitter<T> {\n emit(value: T): void;\n asObservable(): Observable<T>;\n}\n\n/**\n * An emitter is a simple wrapper around a Subject that ensures\n * that change detection is run when an event is emitted and that\n * the subject is automatically completed when the component is destroyed.\n */\nexport function emitter<T>({\n injector = inject(Injector),\n}: { injector?: Injector } = {}): Emitter<T> {\n return runInInjectionContext(injector, () => {\n const eventEmitter = new Subject<T>();\n const changeDetector = inject(ChangeDetectorRef);\n\n // Complete the subject on destroy\n onDestroy(() => eventEmitter.complete());\n\n return {\n emit(value: T): void {\n eventEmitter.next(value);\n changeDetector.markForCheck();\n },\n asObservable(): Observable<T> {\n return eventEmitter.asObservable();\n },\n };\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-primitives-state.mjs","sources":["../../../../packages/ng-primitives/state/src/index.ts","../../../../packages/ng-primitives/state/src/ng-primitives-state.ts"],"sourcesContent":["/* eslint-disable @angular-eslint/no-uncalled-signals */\nimport { coerceElement } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n afterRenderEffect,\n ChangeDetectorRef,\n computed,\n DestroyRef,\n effect,\n ElementRef,\n FactoryProvider,\n forwardRef,\n inject,\n InjectionToken,\n InjectOptions,\n Injector,\n InputSignal,\n InputSignalWithTransform,\n isSignal,\n linkedSignal,\n NgZone,\n PLATFORM_ID,\n ProviderToken,\n runInInjectionContext,\n signal,\n Signal,\n WritableSignal,\n} from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\n\n/**\n * This converts the state object to a writable state object.\n * This means that inputs become signals which are writable.\n */\nexport type State<T> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends InputSignalWithTransform<infer U, any>\n ? WritableSignal<U>\n : T[K] extends InputSignal<infer R>\n ? WritableSignal<R>\n : T[K];\n};\n\n/**\n * This is similar to the state object, but we don't expose properties that are not\n * inputs.\n */\nexport type CreatedState<T> = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends InputSignalWithTransform<infer U, any>\n ? WritableSignal<U>\n : T[K] extends InputSignal<infer R>\n ? WritableSignal<R>\n : never;\n};\n\nexport type InjectedState<T> = Signal<State<T>>;\n\n/**\n * Create a new injection token for the state.\n * @param description The description of the token\n */\nexport function createStateToken<T>(description: string): InjectionToken<T> {\n return new InjectionToken<Signal<State<T>>>(`Ngp${description}StateToken`);\n}\n\nexport interface CreateStateProviderOptions {\n /**\n * Whether we should check for the state in the parent injector.\n */\n inherit?: boolean;\n}\n\n/**\n * Create a new provider for the state. It first tries to inject the state from the parent injector,\n * as this allows for the state to be hoisted to a higher level in the component tree. This can\n * be useful to avoid issues where the injector can't be shared in some cases when ng-content is used.\n * @param token The token for the state\n */\nexport function createStateProvider<T>(\n token: ProviderToken<T>,\n): (options?: CreateStateProviderOptions) => FactoryProvider {\n return ({ inherit }: CreateStateProviderOptions = {}) => ({\n provide: token,\n useFactory: () => {\n if (inherit === false) {\n // if we are not checking the parent, we want to create a new state\n return signal({});\n }\n // if we are checking the parent, we want to check if the state is already defined\n return inject(token, { optional: true, skipSelf: true }) ?? signal({});\n },\n });\n}\n\ntype CreateStateInjectorOptions = {\n /**\n * Whether the state may not be immediately available. This can happen when the child is instantiated before the parent.\n */\n deferred?: boolean;\n};\n\n/**\n * Create a new state injector for the state.\n * @param token The token for the state\n */\nexport function createStateInjector<T>(\n token: ProviderToken<State<T>>,\n options: { deferred: true },\n): <U = T>(injectOptions?: InjectOptions) => Signal<State<U> | undefined>;\nexport function createStateInjector<T>(\n token: ProviderToken<State<T>>,\n options?: CreateStateInjectorOptions,\n): <U = T>(injectOptions?: InjectOptions) => Signal<State<U>>;\nexport function createStateInjector<T>(\n token: ProviderToken<State<T>>,\n options: CreateStateInjectorOptions = {},\n): <U = T>(injectOptions?: InjectOptions) => Signal<State<U> | undefined> {\n return <U = T>(injectOptions: InjectOptions = {}) => {\n const value = inject(token, injectOptions) as Signal<State<U> | undefined> | null;\n\n if (options.deferred) {\n return computed(() =>\n value && Object.keys(value() ?? {}).length === 0 ? undefined : value?.(),\n ) as Signal<State<U> | undefined>;\n }\n\n return (value as Signal<State<U>>) ?? signal(undefined);\n };\n}\n\n/**\n * Convert the original state object into a writable state object.\n * @param token The token for the state\n */\nexport function createState(token: ProviderToken<WritableSignal<State<unknown>>>) {\n return <U>(state: U): CreatedState<U> => {\n const internalState = inject(token);\n\n internalState.update(obj => {\n // Iterating over properties\n for (const key in state) {\n const value = state[key as keyof U];\n\n // We want to make this a controlled input if it is an InputSignal or InputSignalWithTransform\n if (isSignalInput(value)) {\n // @ts-ignore\n obj[key] = createControlledInput(value);\n } else {\n // @ts-ignore\n obj[key] = value;\n }\n }\n\n // Iterating over prototype methods\n const prototype = Object.getPrototypeOf(state);\n\n for (const key of Object.getOwnPropertyNames(prototype)) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n\n // if this is a getter or setter, we need to define it on the object\n if (descriptor?.get || descriptor?.set) {\n Object.defineProperty(obj, key, descriptor);\n } else if (typeof prototype[key as keyof U] === 'function') {\n (obj as Record<string, unknown>)[key] = prototype[key as keyof U].bind(state);\n } else {\n // @ts-ignore\n obj[key] = prototype[key as keyof U];\n }\n }\n\n return { ...obj };\n });\n\n return internalState() as unknown as CreatedState<U>;\n };\n}\n\n// this is a bit hacky, but we need to do it to track whether this is controlled\nfunction createControlledInput(\n property: InputSignal<unknown> | InputSignalWithTransform<unknown, unknown>,\n): WritableSignal<unknown> {\n const value = signal(property());\n let isControlled = false;\n\n const symbol = Object.getOwnPropertySymbols(property).find(s => s.description === 'SIGNAL');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputDefinition = symbol ? (property as any)[symbol] : undefined;\n\n if (\n !symbol ||\n !inputDefinition ||\n typeof inputDefinition.applyValueToInputSignal !== 'function'\n ) {\n console.warn(\n 'Angular has changed its internal Input implementation, report this issue to ng-primitives.',\n );\n // fallback to a linked signal which is partially controlled\n return linkedSignal(() => property());\n }\n\n const originalApply = inputDefinition.applyValueToInputSignal.bind(inputDefinition);\n const originalSet = value.set.bind(value);\n const originalUpdate = value.update.bind(value);\n\n inputDefinition.applyValueToInputSignal = (inputSignalNode: unknown, newValue: unknown) => {\n isControlled = true;\n originalSet(newValue);\n originalApply(inputSignalNode, newValue);\n };\n\n value.set = (newValue: unknown) => {\n if (!isControlled) {\n originalSet(newValue);\n }\n };\n\n value.update = (updateFn: (value: unknown) => unknown) => {\n if (!isControlled) {\n originalUpdate(updateFn);\n }\n };\n\n return value;\n}\n\nfunction isSignalInput(\n property: unknown,\n): property is InputSignal<unknown> | InputSignalWithTransform<unknown, unknown> {\n if (!isSignal(property)) {\n return false;\n }\n\n const symbol = Object.getOwnPropertySymbols(property).find(s => s.description === 'SIGNAL');\n\n if (!symbol) {\n return false;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const inputDefinition = symbol ? (property as any)[symbol] : undefined;\n\n if (!inputDefinition) {\n return false;\n }\n\n return 'transformFn' in inputDefinition || 'applyValueToInputSignal' in inputDefinition;\n}\n\nexport interface CreatePrimitiveOptions {\n injector?: Injector;\n elementRef?: ElementRef<HTMLElement>;\n}\n\ntype PrimitiveState<TFactory extends (...args: any[]) => unknown> = TFactory extends (\n props: unknown,\n) => infer R\n ? R\n : TFactory extends (...args: any[]) => infer R\n ? R\n : never;\n\ntype BasePrimitiveInjectionFn<TState> = {\n (): Signal<TState>;\n (options: { hoisted: true; optional?: boolean; skipSelf?: boolean }): Signal<TState | null>;\n (options?: {\n hoisted?: boolean;\n optional?: boolean;\n skipSelf?: boolean;\n }): Signal<TState | null> | Signal<TState>;\n};\n\ntype PrimitiveInjectionFn<TFactory extends (...args: any[]) => unknown> = TFactory extends (\n props: unknown,\n) => infer R\n ? {\n (): Signal<R>;\n (options: { hoisted: true; optional?: boolean; skipSelf?: boolean }): Signal<R | null>;\n (options?: {\n hoisted?: boolean;\n optional?: boolean;\n skipSelf?: boolean;\n }): Signal<R | null> | Signal<R>;\n }\n : BasePrimitiveInjectionFn<PrimitiveState<TFactory>>;\n\nexport function createPrimitive<TFactory extends (...args: any[]) => unknown>(\n name: string,\n fn: TFactory,\n options?: CreatePrimitiveOptions,\n): [\n InjectionToken<WritableSignal<PrimitiveState<TFactory>>>,\n TFactory,\n PrimitiveInjectionFn<TFactory>,\n (opts?: { inherit?: boolean }) => FactoryProvider,\n];\nexport function createPrimitive<TFactory extends (...args: any[]) => unknown>(\n name: string,\n fn: TFactory,\n options: CreatePrimitiveOptions = {},\n): [\n InjectionToken<WritableSignal<PrimitiveState<TFactory>>>,\n TFactory,\n PrimitiveInjectionFn<TFactory>,\n (opts?: { inherit?: boolean }) => FactoryProvider,\n] {\n // Create a unique injection token for the primitive's state signal\n const token = new InjectionToken<WritableSignal<PrimitiveState<TFactory>>>(`Primitive: ${name}`);\n\n // Create the state signal within the appropriate injection context\n const factory = ((props: Parameters<TFactory>[0]) => {\n // determine the injector to use\n let injector = options.injector ?? inject(Injector);\n\n // If an ElementRef is provided in options, create a child injector\n if (options.elementRef) {\n injector = Injector.create({\n providers: [{ provide: ElementRef, useValue: options.elementRef }],\n parent: injector,\n });\n }\n\n return runInInjectionContext(injector, () => {\n const state = inject(token, { optional: true });\n const instance = fn(props);\n state?.set(instance as PrimitiveState<TFactory>);\n return instance;\n });\n }) as TFactory;\n\n // create an injection function that provides the state signal\n function injectFn<T = PrimitiveState<TFactory>>(): Signal<T>;\n function injectFn<T = PrimitiveState<TFactory>>(\n options: { hoisted: true } & InjectOptions,\n ): Signal<T | null>;\n function injectFn<T = PrimitiveState<TFactory>>(options?: {\n hoisted?: boolean;\n optional?: boolean;\n skipSelf?: boolean;\n }): Signal<T | null> | Signal<T> {\n const hoisted = options?.hoisted ?? false;\n const optional = options?.optional ?? false;\n const skipSelf = options?.skipSelf ?? false;\n\n if (hoisted || optional) {\n return (inject(token, { optional: true, skipSelf }) ??\n signal(null)) as unknown as Signal<T | null>;\n }\n\n return inject(token, { optional, skipSelf }) as unknown as Signal<T>;\n }\n\n // create a function to provide the state\n const provideFn = (opts?: { inherit?: boolean }): FactoryProvider => {\n const inherit = opts?.inherit ?? true;\n return {\n provide: token,\n useFactory: () => {\n if (inherit === false) {\n return signal(null);\n }\n\n return inject(token, { optional: true, skipSelf: true }) ?? signal(null);\n },\n };\n };\n\n return [token, factory as TFactory, injectFn as PrimitiveInjectionFn<TFactory>, provideFn];\n}\n\nexport function controlled<T>(value: Signal<T>): WritableSignal<T> {\n return linkedSignal(() => value());\n}\n\nfunction setAttribute(\n element: ElementRef<HTMLElement>,\n attr: string,\n value: string | null | undefined,\n): void {\n // if the attribute is \"disabled\" and the value is 'false', we need to remove the attribute\n if (attr === 'disabled' && value === 'false') {\n element.nativeElement.removeAttribute(attr);\n return;\n }\n\n if (value !== null && value !== undefined) {\n element.nativeElement.setAttribute(attr, value);\n } else {\n element.nativeElement.removeAttribute(attr);\n }\n}\n\nexport function attrBinding(\n element: ElementRef<HTMLElement>,\n attr: string,\n value:\n | (() => string | number | boolean | null | undefined)\n | string\n | number\n | boolean\n | null\n | undefined,\n): void {\n isomorphicEffect(() => {\n const valueResult = typeof value === 'function' ? value() : value;\n setAttribute(element, attr, valueResult?.toString() ?? null);\n });\n}\n\nfunction getStyleUnit(style: string): string {\n const parts = style.split('.');\n\n if (parts.length > 1) {\n const unit = parts[parts.length - 1];\n\n switch (unit) {\n case 'px':\n case 'em':\n case 'rem':\n case '%':\n case 'vh':\n case 'vw':\n case 'vmin':\n case 'vmax':\n case 'cm':\n case 'mm':\n case 'in':\n case 'pt':\n case 'pc':\n case 'ex':\n case 'ch':\n return unit;\n default:\n return '';\n }\n }\n\n return '';\n}\n\nexport function styleBinding(\n element: ElementRef<HTMLElement>,\n style: string,\n value: (() => string | number | null) | string | number | null,\n): void {\n isomorphicEffect(() => {\n const styleValue = typeof value === 'function' ? value() : value;\n // we should look for units in the style name, just like Angular does e.g. width.px\n const styleUnit = getStyleUnit(style);\n const styleName = styleUnit ? style.replace(`.${styleUnit}`, '') : style;\n\n if (styleValue !== null) {\n element.nativeElement.style.setProperty(styleName, styleValue + styleUnit);\n } else {\n element.nativeElement.style.removeProperty(styleName);\n }\n });\n}\n\nexport function dataBinding(\n element: ElementRef<HTMLElement>,\n attr: string,\n value: (() => string | boolean | null) | string | boolean | null,\n): void {\n if (!attr.startsWith('data-')) {\n throw new Error(`dataBinding: attribute \"${attr}\" must start with \"data-\"`);\n }\n\n isomorphicEffect(() => {\n let valueResult = typeof value === 'function' ? value() : value;\n\n if (valueResult === false) {\n valueResult = null;\n } else if (valueResult === true) {\n valueResult = '';\n } else if (valueResult !== null && typeof valueResult !== 'string') {\n valueResult = String(valueResult);\n }\n\n setAttribute(element, attr, valueResult);\n });\n}\n\nexport function listener<K extends keyof HTMLElementEventMap>(\n element: HTMLElement | ElementRef<HTMLElement> | Document,\n event: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n options?: { injector?: Injector; config?: AddEventListenerOptions | boolean },\n): () => void;\nexport function listener(\n element: HTMLElement | ElementRef<HTMLElement> | Document,\n event: string,\n handler: (event: Event) => void,\n options?: { injector?: Injector; config?: AddEventListenerOptions | boolean },\n): () => void;\nexport function listener<K extends keyof HTMLElementEventMap>(\n element: HTMLElement | ElementRef<HTMLElement> | Document,\n event: K | string,\n handler: (event: HTMLElementEventMap[K] | Event) => void,\n options?: { injector?: Injector; config?: AddEventListenerOptions | boolean },\n): () => void {\n return runInInjectionContext(options?.injector ?? inject(Injector), () => {\n const ngZone = inject(NgZone);\n const destroyRef = inject(DestroyRef);\n const nativeElement = coerceElement(element);\n\n const removeListener = () =>\n nativeElement.removeEventListener(event, handler as EventListener, options?.config);\n destroyRef.onDestroy(removeListener);\n ngZone.runOutsideAngular(() =>\n nativeElement.addEventListener(event, handler as EventListener, options?.config),\n );\n\n return removeListener;\n });\n}\n\nexport function onMount(callback: () => void): void {\n const injector = inject(Injector);\n afterRenderEffect(() => runInInjectionContext(injector, callback), { injector });\n}\n\nexport function onDestroy(callback: () => void): void {\n const destroyRef = inject(DestroyRef);\n destroyRef.onDestroy(callback);\n}\n\n/**\n * Previously, with our state approach, we allowed signals to be written directly using their setters.\n * However, with our new approach, we want people to use the appropriate set method instead. This function takes in a writable\n * signal and returns a proxy that warns the user when set is called directly.\n */\nexport function deprecatedSetter<T>(\n signal: WritableSignal<T>,\n methodName: string,\n): WritableSignal<T> {\n return new Proxy(signal, {\n get(target, prop) {\n if (prop === 'set') {\n return (value: T) => {\n console.warn(\n `Deprecation warning: Use ${methodName}() instead of setting the value directly.`,\n );\n target.set(value);\n };\n }\n return target[prop as keyof WritableSignal<T>];\n },\n });\n}\n\n/**\n * A utility function to inject an inherited state from a parent injector. This is useful for cases\n * where a primitive needs to inherit state from a parent primitive, such as in roving focus groups.\n * We could use inject with a forwardRef, but forwardRef returns an any - no thanks...\n */\nexport function injectInheritedState<T>(\n token: () => InjectionToken<T>,\n injectOptions: InjectOptions = {},\n): T | null {\n return (\n inject<T>(\n forwardRef(() => token()),\n { optional: true, skipSelf: true, ...injectOptions },\n ) ?? null\n );\n}\n\nexport interface Emitter<T> {\n emit(value: T): void;\n asObservable(): Observable<T>;\n}\n\n/**\n * An emitter is a simple wrapper around a Subject that ensures\n * that change detection is run when an event is emitted and that\n * the subject is automatically completed when the component is destroyed.\n */\nexport function emitter<T>({\n injector = inject(Injector),\n}: { injector?: Injector } = {}): Emitter<T> {\n return runInInjectionContext(injector, () => {\n const eventEmitter = new Subject<T>();\n const changeDetector = inject(ChangeDetectorRef);\n\n // Complete the subject on destroy\n onDestroy(() => eventEmitter.complete());\n\n return {\n emit(value: T): void {\n eventEmitter.next(value);\n changeDetector.markForCheck();\n },\n asObservable(): Observable<T> {\n return eventEmitter.asObservable();\n },\n };\n });\n}\n\nfunction isomorphicEffect(callback: () => void): void {\n const injector = inject(Injector);\n const platformId = injector.get(PLATFORM_ID);\n\n if (isPlatformBrowser(platformId)) {\n afterRenderEffect(() => callback());\n } else {\n // On the server, we just run the effect immediately\n effect(() => callback());\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -64,10 +64,24 @@ function updateStatus(control, status) {
64
64
  * It supports both classic reactive forms controls and signal-forms interop controls.
65
65
  * @internal
66
66
  */
67
+ /**
68
+ * Sets up event subscription for a given NgControl.
69
+ * Only sets up the subscription - does not call updateStatus.
70
+ */
71
+ function setupEventSubscription(ngControl, status, destroyRef) {
72
+ // For classic controls, also subscribe to the events observable.
73
+ const underlyingControl = ngControl.control;
74
+ if (underlyingControl?.events) {
75
+ underlyingControl.events
76
+ .pipe(safeTakeUntilDestroyed(destroyRef))
77
+ .subscribe(() => updateStatus(ngControl, status));
78
+ }
79
+ }
67
80
  function controlStatus() {
68
81
  const injector = inject(Injector);
69
82
  const destroyRef = inject(DestroyRef);
70
- const control = signal(null, ...(ngDevMode ? [{ debugName: "control" }] : []));
83
+ // Try to inject NgControl immediately for initial state
84
+ const control = signal(inject(NgControl, { optional: true }), ...(ngDevMode ? [{ debugName: "control" }] : []));
71
85
  const status = signal({
72
86
  valid: null,
73
87
  invalid: null,
@@ -77,19 +91,24 @@ function controlStatus() {
77
91
  pending: null,
78
92
  disabled: null,
79
93
  }, ...(ngDevMode ? [{ debugName: "status" }] : []));
94
+ // If we have a control immediately, update initial status
95
+ if (control()) {
96
+ updateStatus(control(), status);
97
+ }
80
98
  onMount(() => {
81
- control.set(inject(NgControl, { optional: true }));
82
- if (!control()) {
99
+ // Get the control (either from initial injection or from mount)
100
+ const mountControl = control() || inject(NgControl, { optional: true });
101
+ if (!mountControl) {
83
102
  return;
84
103
  }
85
- updateStatus(control(), status);
86
- // For classic controls, also subscribe to the events observable.
87
- const underlyingControl = control().control;
88
- if (underlyingControl?.events) {
89
- underlyingControl.events
90
- .pipe(safeTakeUntilDestroyed(destroyRef))
91
- .subscribe(() => updateStatus(control(), status));
104
+ // Update control signal if it wasn't set before
105
+ if (!control()) {
106
+ control.set(mountControl);
92
107
  }
108
+ // Update status to ensure latest values
109
+ updateStatus(mountControl, status);
110
+ // Set up event subscription for reactive updates
111
+ setupEventSubscription(mountControl, status, destroyRef);
93
112
  });
94
113
  // Use an effect to reactively track status changes.
95
114
  // For signal-forms interop controls, the status properties are signals.
@@ -1 +1 @@
1
- {"version":3,"file":"ng-primitives-utils.mjs","sources":["../../../../packages/ng-primitives/utils/src/forms/providers.ts","../../../../packages/ng-primitives/utils/src/observables/take-until-destroyed.ts","../../../../packages/ng-primitives/utils/src/forms/status.ts","../../../../packages/ng-primitives/utils/src/helpers/attributes.ts","../../../../packages/ng-primitives/utils/src/helpers/disposables.ts","../../../../packages/ng-primitives/utils/src/helpers/unique-id.ts","../../../../packages/ng-primitives/utils/src/helpers/validators.ts","../../../../packages/ng-primitives/utils/src/signals/index.ts","../../../../packages/ng-primitives/utils/src/ng-primitives-utils.ts"],"sourcesContent":["import { ExistingProvider, Type } from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/**\n * A simple helper function to provide a value accessor for a given type.\n * @param type The type to provide the value accessor for\n */\nexport function provideValueAccessor<T>(type: Type<T>): ExistingProvider {\n return { provide: NG_VALUE_ACCESSOR, useExisting: type, multi: true };\n}\n","/* eslint-disable @nx/workspace-take-until-destroyed */\nimport { DestroyRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { EMPTY, MonoTypeOperatorFunction, NEVER, pipe } from 'rxjs';\nimport { catchError, defaultIfEmpty, takeUntil } from 'rxjs/operators';\n\n/**\n * The built-in `takeUntilDestroyed` operator does not handle the case when the component is destroyed before the source observable emits.\n * This operator ensures that the source observable completes gracefully without throwing an error.\n * https://github.com/angular/angular/issues/54527#issuecomment-2098254508\n *\n * @internal\n */\nexport function safeTakeUntilDestroyed<T>(destroyRef?: DestroyRef): MonoTypeOperatorFunction<T> {\n return pipe(\n takeUntil(\n NEVER.pipe(\n takeUntilDestroyed(destroyRef),\n catchError(() => EMPTY),\n defaultIfEmpty(null),\n ),\n ),\n );\n}\n","import {\n DestroyRef,\n Injector,\n Signal,\n WritableSignal,\n effect,\n inject,\n signal,\n untracked,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { onMount } from 'ng-primitives/state';\nimport { safeTakeUntilDestroyed } from '../observables/take-until-destroyed';\n\nexport interface NgpControlStatus {\n valid: boolean | null;\n invalid: boolean | null;\n pristine: boolean | null;\n dirty: boolean | null;\n touched: boolean | null;\n pending: boolean | null;\n disabled: boolean | null;\n}\n\n/**\n * Detects an Angular signal-forms interop control without importing any of the new\n * signal-form types. Interop controls expose a `field()` method which returns the\n * underlying FieldState.\n */\nfunction isInteropControl(control: NgControl | null | undefined): boolean {\n return !!control && typeof (control as any).field === 'function';\n}\n\n/**\n * Reads status from a control and updates the status signal.\n * Wrapped in try-catch to handle signal-forms interop controls where\n * the `field` input may not be available yet (throws NG0950).\n */\nfunction updateStatus(control: NgControl, status: WritableSignal<NgpControlStatus>): void {\n try {\n // For interop controls, read directly from the control (which has signal getters).\n // For classic controls, read from the underlying AbstractControl.\n const source = isInteropControl(control) ? control : ((control as any).control ?? control);\n\n const newStatus: NgpControlStatus = {\n valid: source.valid ?? null,\n invalid: source.invalid ?? null,\n pristine: source.pristine ?? null,\n dirty: source.dirty ?? null,\n touched: source.touched ?? null,\n pending: source.pending ?? null,\n disabled: source.disabled ?? null,\n };\n\n untracked(() => status.set(newStatus));\n } catch {\n // NG0950: Required input not available yet. The effect will re-run\n // when the signal input becomes available.\n }\n}\n\n/**\n * A utility function to get the status of an Angular form control as a reactive signal.\n * This function injects the NgControl and returns a signal that reflects the control's status.\n * It supports both classic reactive forms controls and signal-forms interop controls.\n * @internal\n */\nexport function controlStatus(): Signal<NgpControlStatus> {\n const injector = inject(Injector);\n const destroyRef = inject(DestroyRef);\n const control = signal<NgControl | null>(null);\n\n const status = signal<NgpControlStatus>({\n valid: null,\n invalid: null,\n pristine: null,\n dirty: null,\n touched: null,\n pending: null,\n disabled: null,\n });\n\n onMount(() => {\n control.set(inject(NgControl, { optional: true }));\n\n if (!control()) {\n return;\n }\n\n updateStatus(control()!, status);\n\n // For classic controls, also subscribe to the events observable.\n const underlyingControl = (control() as any).control;\n if (underlyingControl?.events) {\n underlyingControl.events\n .pipe(safeTakeUntilDestroyed(destroyRef))\n .subscribe(() => updateStatus(control()!, status));\n }\n });\n\n // Use an effect to reactively track status changes.\n // For signal-forms interop controls, the status properties are signals.\n // For classic controls, this will read the current values and establish\n // no signal dependencies, but we also subscribe to events below.\n effect(\n () => {\n const c = control();\n if (c) {\n updateStatus(c, status);\n }\n },\n { injector },\n );\n\n return status;\n}\n","import { afterRenderEffect, Signal } from '@angular/core';\n\nexport function booleanAttributeBinding(\n element: HTMLElement,\n attribute: string,\n value: Signal<boolean> | undefined,\n): void {\n if (!value) {\n return;\n }\n\n afterRenderEffect({\n write: () =>\n value() ? element.setAttribute(attribute, '') : element.removeAttribute(attribute),\n });\n}\n","import { DestroyRef, inject } from '@angular/core';\n\n/**\n * Disposable functions are a way to manage timers, intervals, and event listeners\n * that should be cleared when a component is destroyed.\n *\n * This is heavily inspired by Headless UI disposables:\n * https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-react/src/utils/disposables.ts\n */\nexport function injectDisposables() {\n const destroyRef = inject(DestroyRef);\n let isDestroyed = false;\n\n destroyRef.onDestroy(() => (isDestroyed = true));\n\n return {\n /**\n * Set a timeout that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @param delay The delay before the callback is executed\n * @returns A function to clear the timeout\n */\n setTimeout: (callback: () => void, delay: number) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const id = setTimeout(callback, delay);\n const cleanup = () => clearTimeout(id);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n /**\n * Set an interval that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @param delay The delay before the callback is executed\n * @param target\n * @param type\n * @param listener\n * @param options\n * @returns A function to clear the interval\n */\n addEventListener: <K extends keyof HTMLElementEventMap>(\n target: EventTarget,\n type: K,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n target.addEventListener(type, listener as EventListenerOrEventListenerObject, options);\n const cleanup = () =>\n target.removeEventListener(type, listener as EventListenerOrEventListenerObject, options);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n /**\n * Set an interval that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @param delay The delay before the callback is executed\n * @returns A function to clear the interval\n */\n setInterval: (callback: () => void, delay: number) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const id = setInterval(callback, delay);\n const cleanup = () => clearInterval(id);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n /**\n * Set a requestAnimationFrame that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @returns A function to clear the requestAnimationFrame\n */\n requestAnimationFrame: (callback: FrameRequestCallback) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const id = requestAnimationFrame(callback);\n const cleanup = () => cancelAnimationFrame(id);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n };\n}\n","/**\n * Store a map of unique ids for elements so that there are no collisions.\n */\nconst uniqueIdMap = new Map<string, number>();\n\n/**\n * Generate a unique id for an element\n * @param prefix - The prefix to use for the id\n * @returns The generated id\n */\nexport function uniqueId(prefix: string): string {\n const id = uniqueIdMap.get(prefix) ?? 0;\n uniqueIdMap.set(prefix, id + 1);\n return `${prefix}-${id}`;\n}\n","/**\n * Type validation utilities\n */\n\n/**\n * Checks if a value is a string\n * @param value - The value to check\n * @returns true if the value is a string, false otherwise\n */\nexport function isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n/**\n * Checks if a value is a number\n * @param value - The value to check\n * @returns true if the value is a number, false otherwise\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === 'number';\n}\n\n/**\n * Checks if a value is a boolean\n * @param value - The value to check\n * @returns true if the value is a boolean, false otherwise\n */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === 'boolean';\n}\n\n/**\n * Checks if a value is a function\n * @param value - The value to check\n * @returns true if the value is a function, false otherwise\n */\nexport function isFunction(value: unknown): value is CallableFunction {\n return typeof value === 'function';\n}\n\n/**\n * Checks if a value is a plain object (but not null or array)\n * @param value - The value to check\n * @returns true if the value is a plain object, false otherwise\n */\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Checks if a value is undefined\n * @param value - The value to check\n * @returns true if the value is undefined, false otherwise\n */\nexport function isUndefined(value: unknown): value is undefined {\n return typeof value === 'undefined';\n}\n\n/**\n * Checks if a value is null or undefined\n * @param value - The value to check\n * @returns true if the value is null or undefined, false otherwise\n */\nexport function isNil(value: unknown): value is null | undefined {\n return isUndefined(value) || value === null;\n}\n\n/**\n * Checks if a value is not null and not undefined\n * @param value - The value to check\n * @returns true if the value is not null and not undefined, false otherwise\n */\nexport function notNil<T>(value: T | null | undefined): value is T {\n return !isNil(value);\n}\n","import { effect, Injector, Signal, signal, untracked } from '@angular/core';\n\n/**\n * Listen for changes to a signal and call a function when the signal changes.\n * @param source\n * @param fn\n * @param options\n * @param options.injector\n * @internal\n */\nexport function onChange<T>(\n source: Signal<T>,\n fn: (value: T, previousValue: T | null | undefined) => void,\n options?: { injector: Injector },\n): void {\n const previousValue = signal(source());\n\n effect(\n () => {\n const value = source();\n if (value !== previousValue()) {\n untracked(() => fn(value, previousValue()));\n previousValue.set(value);\n }\n },\n { injector: options?.injector },\n );\n\n // call the fn with the initial value\n fn(source(), null);\n}\n\n/**\n * Listen for changes to a boolean signal and call one of two functions when the signal changes.\n * @param source\n * @param onTrue\n * @param onFalse\n * @param options\n */\nexport function onBooleanChange(\n source: Signal<boolean>,\n onTrue?: () => void,\n onFalse?: () => void,\n options?: { injector: Injector },\n): void {\n onChange(source, value => (value ? onTrue?.() : onFalse?.()), options);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-primitives-utils.mjs","sources":["../../../../packages/ng-primitives/utils/src/forms/providers.ts","../../../../packages/ng-primitives/utils/src/observables/take-until-destroyed.ts","../../../../packages/ng-primitives/utils/src/forms/status.ts","../../../../packages/ng-primitives/utils/src/helpers/attributes.ts","../../../../packages/ng-primitives/utils/src/helpers/disposables.ts","../../../../packages/ng-primitives/utils/src/helpers/unique-id.ts","../../../../packages/ng-primitives/utils/src/helpers/validators.ts","../../../../packages/ng-primitives/utils/src/signals/index.ts","../../../../packages/ng-primitives/utils/src/ng-primitives-utils.ts"],"sourcesContent":["import { ExistingProvider, Type } from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/**\n * A simple helper function to provide a value accessor for a given type.\n * @param type The type to provide the value accessor for\n */\nexport function provideValueAccessor<T>(type: Type<T>): ExistingProvider {\n return { provide: NG_VALUE_ACCESSOR, useExisting: type, multi: true };\n}\n","/* eslint-disable @nx/workspace-take-until-destroyed */\nimport { DestroyRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { EMPTY, MonoTypeOperatorFunction, NEVER, pipe } from 'rxjs';\nimport { catchError, defaultIfEmpty, takeUntil } from 'rxjs/operators';\n\n/**\n * The built-in `takeUntilDestroyed` operator does not handle the case when the component is destroyed before the source observable emits.\n * This operator ensures that the source observable completes gracefully without throwing an error.\n * https://github.com/angular/angular/issues/54527#issuecomment-2098254508\n *\n * @internal\n */\nexport function safeTakeUntilDestroyed<T>(destroyRef?: DestroyRef): MonoTypeOperatorFunction<T> {\n return pipe(\n takeUntil(\n NEVER.pipe(\n takeUntilDestroyed(destroyRef),\n catchError(() => EMPTY),\n defaultIfEmpty(null),\n ),\n ),\n );\n}\n","import {\n DestroyRef,\n Injector,\n Signal,\n WritableSignal,\n effect,\n inject,\n signal,\n untracked,\n} from '@angular/core';\nimport { NgControl } from '@angular/forms';\nimport { onMount } from 'ng-primitives/state';\nimport { safeTakeUntilDestroyed } from '../observables/take-until-destroyed';\n\nexport interface NgpControlStatus {\n valid: boolean | null;\n invalid: boolean | null;\n pristine: boolean | null;\n dirty: boolean | null;\n touched: boolean | null;\n pending: boolean | null;\n disabled: boolean | null;\n}\n\n/**\n * Detects an Angular signal-forms interop control without importing any of the new\n * signal-form types. Interop controls expose a `field()` method which returns the\n * underlying FieldState.\n */\nfunction isInteropControl(control: NgControl | null | undefined): boolean {\n return !!control && typeof (control as any).field === 'function';\n}\n\n/**\n * Reads status from a control and updates the status signal.\n * Wrapped in try-catch to handle signal-forms interop controls where\n * the `field` input may not be available yet (throws NG0950).\n */\nfunction updateStatus(control: NgControl, status: WritableSignal<NgpControlStatus>): void {\n try {\n // For interop controls, read directly from the control (which has signal getters).\n // For classic controls, read from the underlying AbstractControl.\n const source = isInteropControl(control) ? control : ((control as any).control ?? control);\n\n const newStatus: NgpControlStatus = {\n valid: source.valid ?? null,\n invalid: source.invalid ?? null,\n pristine: source.pristine ?? null,\n dirty: source.dirty ?? null,\n touched: source.touched ?? null,\n pending: source.pending ?? null,\n disabled: source.disabled ?? null,\n };\n\n untracked(() => status.set(newStatus));\n } catch {\n // NG0950: Required input not available yet. The effect will re-run\n // when the signal input becomes available.\n }\n}\n\n/**\n * A utility function to get the status of an Angular form control as a reactive signal.\n * This function injects the NgControl and returns a signal that reflects the control's status.\n * It supports both classic reactive forms controls and signal-forms interop controls.\n * @internal\n */\n/**\n * Sets up event subscription for a given NgControl.\n * Only sets up the subscription - does not call updateStatus.\n */\nfunction setupEventSubscription(\n ngControl: NgControl,\n status: WritableSignal<NgpControlStatus>,\n destroyRef: DestroyRef,\n): void {\n // For classic controls, also subscribe to the events observable.\n const underlyingControl = (ngControl as any).control;\n if (underlyingControl?.events) {\n underlyingControl.events\n .pipe(safeTakeUntilDestroyed(destroyRef))\n .subscribe(() => updateStatus(ngControl, status));\n }\n}\n\nexport function controlStatus(): Signal<NgpControlStatus> {\n const injector = inject(Injector);\n const destroyRef = inject(DestroyRef);\n\n // Try to inject NgControl immediately for initial state\n const control = signal<NgControl | null>(inject(NgControl, { optional: true }));\n\n const status = signal<NgpControlStatus>({\n valid: null,\n invalid: null,\n pristine: null,\n dirty: null,\n touched: null,\n pending: null,\n disabled: null,\n });\n\n // If we have a control immediately, update initial status\n if (control()) {\n updateStatus(control()!, status);\n }\n\n onMount(() => {\n // Get the control (either from initial injection or from mount)\n const mountControl = control() || inject(NgControl, { optional: true });\n\n if (!mountControl) {\n return;\n }\n\n // Update control signal if it wasn't set before\n if (!control()) {\n control.set(mountControl);\n }\n\n // Update status to ensure latest values\n updateStatus(mountControl, status);\n\n // Set up event subscription for reactive updates\n setupEventSubscription(mountControl, status, destroyRef);\n });\n\n // Use an effect to reactively track status changes.\n // For signal-forms interop controls, the status properties are signals.\n // For classic controls, this will read the current values and establish\n // no signal dependencies, but we also subscribe to events below.\n effect(\n () => {\n const c = control();\n if (c) {\n updateStatus(c, status);\n }\n },\n { injector },\n );\n\n return status;\n}\n","import { afterRenderEffect, Signal } from '@angular/core';\n\nexport function booleanAttributeBinding(\n element: HTMLElement,\n attribute: string,\n value: Signal<boolean> | undefined,\n): void {\n if (!value) {\n return;\n }\n\n afterRenderEffect({\n write: () =>\n value() ? element.setAttribute(attribute, '') : element.removeAttribute(attribute),\n });\n}\n","import { DestroyRef, inject } from '@angular/core';\n\n/**\n * Disposable functions are a way to manage timers, intervals, and event listeners\n * that should be cleared when a component is destroyed.\n *\n * This is heavily inspired by Headless UI disposables:\n * https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-react/src/utils/disposables.ts\n */\nexport function injectDisposables() {\n const destroyRef = inject(DestroyRef);\n let isDestroyed = false;\n\n destroyRef.onDestroy(() => (isDestroyed = true));\n\n return {\n /**\n * Set a timeout that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @param delay The delay before the callback is executed\n * @returns A function to clear the timeout\n */\n setTimeout: (callback: () => void, delay: number) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const id = setTimeout(callback, delay);\n const cleanup = () => clearTimeout(id);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n /**\n * Set an interval that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @param delay The delay before the callback is executed\n * @param target\n * @param type\n * @param listener\n * @param options\n * @returns A function to clear the interval\n */\n addEventListener: <K extends keyof HTMLElementEventMap>(\n target: EventTarget,\n type: K,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n target.addEventListener(type, listener as EventListenerOrEventListenerObject, options);\n const cleanup = () =>\n target.removeEventListener(type, listener as EventListenerOrEventListenerObject, options);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n /**\n * Set an interval that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @param delay The delay before the callback is executed\n * @returns A function to clear the interval\n */\n setInterval: (callback: () => void, delay: number) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const id = setInterval(callback, delay);\n const cleanup = () => clearInterval(id);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n /**\n * Set a requestAnimationFrame that will be cleared when the component is destroyed.\n * @param callback The callback to execute\n * @returns A function to clear the requestAnimationFrame\n */\n requestAnimationFrame: (callback: FrameRequestCallback) => {\n if (isDestroyed) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const id = requestAnimationFrame(callback);\n const cleanup = () => cancelAnimationFrame(id);\n destroyRef.onDestroy(cleanup);\n return cleanup;\n },\n };\n}\n","/**\n * Store a map of unique ids for elements so that there are no collisions.\n */\nconst uniqueIdMap = new Map<string, number>();\n\n/**\n * Generate a unique id for an element\n * @param prefix - The prefix to use for the id\n * @returns The generated id\n */\nexport function uniqueId(prefix: string): string {\n const id = uniqueIdMap.get(prefix) ?? 0;\n uniqueIdMap.set(prefix, id + 1);\n return `${prefix}-${id}`;\n}\n","/**\n * Type validation utilities\n */\n\n/**\n * Checks if a value is a string\n * @param value - The value to check\n * @returns true if the value is a string, false otherwise\n */\nexport function isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n/**\n * Checks if a value is a number\n * @param value - The value to check\n * @returns true if the value is a number, false otherwise\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === 'number';\n}\n\n/**\n * Checks if a value is a boolean\n * @param value - The value to check\n * @returns true if the value is a boolean, false otherwise\n */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === 'boolean';\n}\n\n/**\n * Checks if a value is a function\n * @param value - The value to check\n * @returns true if the value is a function, false otherwise\n */\nexport function isFunction(value: unknown): value is CallableFunction {\n return typeof value === 'function';\n}\n\n/**\n * Checks if a value is a plain object (but not null or array)\n * @param value - The value to check\n * @returns true if the value is a plain object, false otherwise\n */\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Checks if a value is undefined\n * @param value - The value to check\n * @returns true if the value is undefined, false otherwise\n */\nexport function isUndefined(value: unknown): value is undefined {\n return typeof value === 'undefined';\n}\n\n/**\n * Checks if a value is null or undefined\n * @param value - The value to check\n * @returns true if the value is null or undefined, false otherwise\n */\nexport function isNil(value: unknown): value is null | undefined {\n return isUndefined(value) || value === null;\n}\n\n/**\n * Checks if a value is not null and not undefined\n * @param value - The value to check\n * @returns true if the value is not null and not undefined, false otherwise\n */\nexport function notNil<T>(value: T | null | undefined): value is T {\n return !isNil(value);\n}\n","import { effect, Injector, Signal, signal, untracked } from '@angular/core';\n\n/**\n * Listen for changes to a signal and call a function when the signal changes.\n * @param source\n * @param fn\n * @param options\n * @param options.injector\n * @internal\n */\nexport function onChange<T>(\n source: Signal<T>,\n fn: (value: T, previousValue: T | null | undefined) => void,\n options?: { injector: Injector },\n): void {\n const previousValue = signal(source());\n\n effect(\n () => {\n const value = source();\n if (value !== previousValue()) {\n untracked(() => fn(value, previousValue()));\n previousValue.set(value);\n }\n },\n { injector: options?.injector },\n );\n\n // call the fn with the initial value\n fn(source(), null);\n}\n\n/**\n * Listen for changes to a boolean signal and call one of two functions when the signal changes.\n * @param source\n * @param onTrue\n * @param onFalse\n * @param options\n */\nexport function onBooleanChange(\n source: Signal<boolean>,\n onTrue?: () => void,\n onFalse?: () => void,\n options?: { injector: Injector },\n): void {\n onChange(source, value => (value ? onTrue?.() : onFalse?.()), options);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "ng-primitives",
3
3
  "description": "Angular Primitives is a low-level headless UI component library with a focus on accessibility, customization, and developer experience. ",
4
4
  "license": "Apache-2.0",
5
- "version": "0.96.0",
5
+ "version": "0.97.0",
6
6
  "keywords": [
7
7
  "angular",
8
8
  "primitives",
package/portal/index.d.ts CHANGED
@@ -45,6 +45,48 @@ type NgpOffsetInput = NgpOffset | string;
45
45
  */
46
46
  declare function coerceOffset(value: NgpOffsetInput | null | undefined): NgpOffset;
47
47
 
48
+ /**
49
+ * Options for configuring shift behavior to keep the floating element in view.
50
+ * The shift middleware ensures the floating element stays visible by shifting it
51
+ * within the viewport when it would otherwise overflow.
52
+ */
53
+ interface NgpShiftOptions {
54
+ /**
55
+ * The minimum padding between the floating element and the viewport edges.
56
+ * Prevents the floating element from touching the edges of the viewport.
57
+ * @default 0
58
+ */
59
+ padding?: number;
60
+ /**
61
+ * The limiter function that determines how much the floating element can shift.
62
+ * Common limiters from @floating-ui/dom include:
63
+ * - limitShift: Limits shifting to prevent the reference element from being obscured
64
+ * @default undefined
65
+ */
66
+ limiter?: {
67
+ fn: (state: unknown) => {
68
+ x: number;
69
+ y: number;
70
+ };
71
+ options?: unknown;
72
+ };
73
+ }
74
+ /**
75
+ * Type representing all valid shift values.
76
+ * Can be a boolean (enable/disable), undefined, or an object with detailed options.
77
+ */
78
+ type NgpShift = boolean | NgpShiftOptions | undefined;
79
+ /**
80
+ * Input type for shift that also accepts string representations of booleans
81
+ */
82
+ type NgpShiftInput = NgpShift | string;
83
+ /**
84
+ * Transform function to coerce shift input values to the correct type
85
+ * @param value The input value to coerce
86
+ * @returns The coerced shift value
87
+ */
88
+ declare function coerceShift(value: NgpShiftInput | null | undefined): NgpShift;
89
+
48
90
  /**
49
91
  * Configuration options for creating an overlay
50
92
  * @internal
@@ -68,6 +110,8 @@ interface NgpOverlayConfig<T = unknown> {
68
110
  placement?: Signal<Placement>;
69
111
  /** Offset distance between the overlay and trigger. Can be a number or an object with axis-specific offsets */
70
112
  offset?: NgpOffset;
113
+ /** Shift configuration to keep the overlay in view. Can be a boolean, an object with options, or undefined */
114
+ shift?: NgpShift;
71
115
  /** Whether to enable flip behavior when space is limited */
72
116
  flip?: boolean;
73
117
  /** Delay before showing the overlay in milliseconds */
@@ -385,5 +429,5 @@ declare class NoopScrollStrategy implements ScrollStrategy {
385
429
  disable(): void;
386
430
  }
387
431
 
388
- export { BlockScrollStrategy, NgpComponentPortal, NgpOverlay, NgpPortal, NgpTemplatePortal, NoopScrollStrategy, coerceOffset, createOverlay, createPortal, injectOverlay, injectOverlayContext, provideOverlayContext, setupOverlayArrow };
389
- export type { NgpOffset, NgpOffsetInput, NgpOffsetOptions, NgpOverlayConfig, NgpOverlayContent, NgpOverlayTemplateContext, ScrollStrategy };
432
+ export { BlockScrollStrategy, NgpComponentPortal, NgpOverlay, NgpPortal, NgpTemplatePortal, NoopScrollStrategy, coerceOffset, coerceShift, createOverlay, createPortal, injectOverlay, injectOverlayContext, provideOverlayContext, setupOverlayArrow };
433
+ export type { NgpOffset, NgpOffsetInput, NgpOffsetOptions, NgpOverlayConfig, NgpOverlayContent, NgpOverlayTemplateContext, NgpShift, NgpShiftInput, NgpShiftOptions, ScrollStrategy };
package/utils/index.d.ts CHANGED
@@ -16,12 +16,6 @@ interface NgpControlStatus {
16
16
  pending: boolean | null;
17
17
  disabled: boolean | null;
18
18
  }
19
- /**
20
- * A utility function to get the status of an Angular form control as a reactive signal.
21
- * This function injects the NgControl and returns a signal that reflects the control's status.
22
- * It supports both classic reactive forms controls and signal-forms interop controls.
23
- * @internal
24
- */
25
19
  declare function controlStatus(): Signal<NgpControlStatus>;
26
20
 
27
21
  /**