Skip to content

feat(core): extract shared KbqCheckable primitive (#DS-3498) - #1883

Open
NikGurev wants to merge 4 commits into
mainfrom
feat/DS-3498
Open

feat(core): extract shared KbqCheckable primitive (#DS-3498)#1883
NikGurev wants to merge 4 commits into
mainfrom
feat/DS-3498

Conversation

@NikGurev

Copy link
Copy Markdown
Contributor

Summary

Checkbox and toggle each reimplemented the same checked/disabled/indeterminate/
tabIndex state, click-to-toggle algorithm, ARIA-checked computation, FocusMonitor
wiring, and ControlValueAccessor - toggle even imported checkbox's internals
(TransitionCheckState, click-action token) to avoid re-declaring them.

  • Add KbqCheckable (@koobiq/components/core), a hostDirective centralizing all
    of the above, including its own ControlValueAccessor registration so any
    future control built on it gets form support for free.
  • Wire KbqCheckbox and KbqToggleComponent onto it via hostDirectives with zero
    public API changes; toggle now depends on the generic KBQ_CHECKABLE_CLICK_ACTION
    token (falling back to KBQ_CHECKBOX_CLICK_ACTION for backwards compatibility)
    instead of reaching into the checkbox package.
  • Add an example-block-checkbox doc example showing a custom checkbox-like
    "card" control built directly on the primitive.

@NikGurev NikGurev self-assigned this Aug 11, 2026
@NikGurev NikGurev added the enhancement New feature or request label Aug 11, 2026
@github-actions

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit a2d2ced):

https://koobiq-next--prs-1883-s1a6lqnm.web.app

(expires Fri, 14 Aug 2026 16:38:56 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c

@NikGurev
NikGurev marked this pull request as ready for review August 12, 2026 05:59
* @docs-private
* @deprecated Use `TransitionCheckState` from `@koobiq/components/core` instead.
*/
export { TransitionCheckState };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

для чего нужно добавлять устаревший экспорт?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

для обратной совместимости

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

так давай так в комментарии и напишем))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вкладка с примерами выключена

может быть лучше оставить примитив приватным, не отдавать его в паблик апи, это нам развяжет руки для любого рефакторинга

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так там рефакторить нечего, функционал тоггла и чекбокса не менялся давно.
Наоборот хотелось также показать, что можно делать кастомные компоненты без особых затруднений (был такой запрос)

* applying it via `hostDirectives` gets `[(ngModel)]`/`formControl` support without wiring up its own.
* @docs-private
*/
export const KBQ_CHECKABLE_CONTROL_VALUE_ACCESSOR: any = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const KBQ_CHECKABLE_CONTROL_VALUE_ACCESSOR: any = {
export const KBQ_CHECKABLE_CONTROL_VALUE_ACCESSOR: Provider = {

@Input({ transform: booleanAttribute })
get disabled(): boolean {
return this._disabled;
return this.checkable.disabled();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

некоторые вещи можно прокинуть в директиву без обработки:

hostDirectives: [{ directive: KbqCheckable, inputs: ['disabled' ] }],

exportAs: 'kbqCheckable'
})
export class KbqCheckable implements ControlValueAccessor, OnDestroy {
private readonly focusMonitor = inject(FocusMonitor);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

фокус монитор в данной директиве только зеркалит свои методы, может быть стоит его удалить и сосредоточится на выделении?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

давай добавим unit тесты

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts the duplicated checkbox/toggle state machine into a shared KbqCheckable host directive in @koobiq/components/core. KbqCheckable centralizes the checked/disabled/indeterminate/tabIndex signals, the click-to-toggle algorithm, ARIA-checked computation, FocusMonitor wiring, and ControlValueAccessor registration. KbqCheckbox and KbqToggleComponent are rewired onto it via hostDirectives, TransitionCheckState and the click-action token/type are promoted to core (with deprecated aliases kept in the checkbox package for compatibility), and a new "block checkbox" doc example demonstrates building a custom control on the primitive.

Changes:

  • Add KbqCheckable primitive plus KbqCheckableClickAction, KBQ_CHECKABLE_CLICK_ACTION, KbqCheckableClickResult, and moved TransitionCheckState to core.
  • Rewire KbqCheckbox/KbqToggleComponent onto KbqCheckable, delegating state, CVA, focus, and click handling; keep public APIs stable via deprecated aliases.
  • Add a block-checkbox docs example and its registration/documentation.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/components/core/common-behaviors/checkable.ts New shared KbqCheckable directive with state, CVA, focus, and click logic.
packages/components/core/common-behaviors/index.ts Re-exports the new checkable module.
packages/components/checkbox/checkbox.ts Delegates state/CVA/focus/click to KbqCheckable; deprecates old CVA token and re-exports TransitionCheckState.
packages/components/checkbox/checkbox-config.ts Aliases KbqCheckboxClickAction to the core type.
packages/components/toggle/toggle.component.ts Rewires toggle onto KbqCheckable; adds KbqToggleClickAction; uses the checkable click-action token with a checkbox fallback provider.
packages/docs-examples/components/checkbox/block-checkbox/* New example component/styles built on KbqCheckable.
packages/docs-examples/components/checkbox/index.ts Registers BlockCheckboxExample.
packages/components-dev/checkbox/module.ts Adds the example to the dev app.
packages/components/checkbox/examples.checkbox.{en,ru}.md Documents the block checkbox example.
tools/public_api_guard/components/{core,checkbox,toggle}.api.md API snapshot updates reflecting the moved/added symbols.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +54 to +56
// Falls back to `KBQ_CHECKBOX_CLICK_ACTION` for backwards compatibility with apps that already
// configure it globally to control click behavior for both checkbox and toggle.
{ provide: KBQ_CHECKABLE_CLICK_ACTION, useExisting: KBQ_CHECKBOX_CLICK_ACTION }
Comment on lines +74 to +77
if (shouldToggle) {
this.checkable.toggle();
this.checkedChange.emit(this.checkable.checked());
}
Comment on lines 263 to 265
writeValue(value: any) {
this.checked = !!value;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants