@tailng-ui/primitives 0.21.0 → 0.25.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.
- package/package.json +2 -2
- package/src/lib/form/autocomplete/tng-autocomplete.parts.d.ts.map +1 -1
- package/src/lib/form/autocomplete/tng-autocomplete.parts.js +1 -3
- package/src/lib/form/autocomplete/tng-autocomplete.parts.js.map +1 -1
- package/src/lib/form/listbox/option.directive.d.ts +1 -0
- package/src/lib/form/listbox/option.directive.d.ts.map +1 -1
- package/src/lib/form/listbox/option.directive.js +5 -1
- package/src/lib/form/listbox/option.directive.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailng-ui/primitives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tailng-ui/cdk": "^0.
|
|
19
|
+
"@tailng-ui/cdk": "^0.22.0"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@angular/core": "^21.1.0",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tng-autocomplete.parts.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/autocomplete/tng-autocomplete.parts.ts"],"names":[],"mappings":";AAUA,qBAIa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6C;IAC1E,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmC;IAEtD,OAAO,KAAK,OAAO,GAElB;IAGD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,sBAAsB,CAAU;IAG9D,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IAG9C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IAGjD,SAAS,KAAK,YAAY,IAAI,MAAM,GAAG,OAAO,CAE7C;IAGD,SAAS,KAAK,YAAY,IAAI,MAAM,GAAG,IAAI,CAE1C;IAGD,SAAS,KAAK,YAAY,IAAI,MAAM,GAAG,IAAI,CAG1C;IAGD,SAAS,KAAK,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAIlD;IAGD,SAAS,KAAK,WAAW,IAAI,MAAM,GAAG,IAAI,CAEzC;IAGD,SAAS,KAAK,cAAc,IAAI,MAAM,GAAG,IAAI,CAI5C;IAGD,SAAS,KAAK,eAAe,IAAI,MAAM,GAAG,IAAI,CAS7C;IAGD,SAAS,CAAC,OAAO,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"tng-autocomplete.parts.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/autocomplete/tng-autocomplete.parts.ts"],"names":[],"mappings":";AAUA,qBAIa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6C;IAC1E,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmC;IAEtD,OAAO,KAAK,OAAO,GAElB;IAGD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,sBAAsB,CAAU;IAG9D,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IAG9C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IAGjD,SAAS,KAAK,YAAY,IAAI,MAAM,GAAG,OAAO,CAE7C;IAGD,SAAS,KAAK,YAAY,IAAI,MAAM,GAAG,IAAI,CAE1C;IAGD,SAAS,KAAK,YAAY,IAAI,MAAM,GAAG,IAAI,CAG1C;IAGD,SAAS,KAAK,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAIlD;IAGD,SAAS,KAAK,WAAW,IAAI,MAAM,GAAG,IAAI,CAEzC;IAGD,SAAS,KAAK,cAAc,IAAI,MAAM,GAAG,IAAI,CAI5C;IAGD,SAAS,KAAK,eAAe,IAAI,MAAM,GAAG,IAAI,CAS7C;IAGD,SAAS,CAAC,OAAO,IAAI,IAAI;IAiBzB,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAsD/C,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;yCAxI1B,sBAAsB;2CAAtB,sBAAsB;CA6IlC;AAED,oHAAoH;AACpH,qBAIa,+BAA+B;IAE1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,gCAAgC,CAAU;yCAF7D,+BAA+B;2CAA/B,+BAA+B;CAG3C;AAED,oHAAoH;AACpH,qBAIa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6C;IAG1E,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,mBAAmB,CAAU;IAG3D,SAAS,CAAC,OAAO,IAAI,IAAI;yCAPd,mBAAmB;2CAAnB,mBAAmB;CAc/B;AAID,qBAIa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6C;IAC1E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IAGjD,QAAQ,CAAC,EAAE,SAAqB;IAGhC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAG,sBAAsB,CAAU;IAG9D,SAAS,KAAK,MAAM,IAAI,EAAE,GAAG,IAAI,CAEhC;IAGD,SAAS,KAAK,QAAQ,IAAI,MAAM,GAAG,IAAI,CAEtC;;yCAlBU,sBAAsB;2CAAtB,sBAAsB;CA4BlC"}
|
|
@@ -59,11 +59,9 @@ export class TngAutocompleteTrigger {
|
|
|
59
59
|
if (this.autocomplete._restoringFocus)
|
|
60
60
|
return;
|
|
61
61
|
if (!this.autocomplete.open()) {
|
|
62
|
-
this.autocomplete.openSelect();
|
|
63
|
-
// ✅ Emit empty query (or current query) on open-on-focus.
|
|
64
|
-
// This is the behavior your test expects.
|
|
65
62
|
const input = this.el.nativeElement;
|
|
66
63
|
const value = input?.value ?? '';
|
|
64
|
+
this.autocomplete.openSelect();
|
|
67
65
|
this.autocomplete.query.set(value);
|
|
68
66
|
this.autocomplete.queryChange.emit(value);
|
|
69
67
|
ensureActiveAndSync(this.listbox, (id) => this.autocomplete.setActiveDescendantId(id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tng-autocomplete.parts.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/autocomplete/tng-autocomplete.parts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACrG,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;;AAG7D,gHAAgH;AAChH,MAAM,yBAAyB,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AAM3F,MAAM,OAAO,sBAAsB;IAChB,YAAY,GAAG,MAAM,CAAkB,gBAAgB,CAAC,CAAC;IACzD,EAAE,GAAG,MAAM,CAAC,CAAA,UAAuB,CAAA,CAAC,CAAC;IAEtD,IAAY,OAAO;QACjB,OAAO,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC;IAC3C,CAAC;IAGkB,QAAQ,GAAG,sBAA+B,CAAC;IAG3C,IAAI,GAAG,UAAmB,CAAC;IAG3B,QAAQ,GAAG,SAAkB,CAAC;IAEjD,IACc,YAAY;QACxB,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACrD,CAAC;IAED,IACc,YAAY;QACxB,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAED,IACc,YAAY;QACxB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC;IAC9E,CAAC;IAED,IACc,oBAAoB;QAChC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QAC3C,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACpD,OAAO,IAAI,CAAC,YAAY,CAAC,qBAAqB,EAAE,CAAC;IACnD,CAAC;IAED,IACc,WAAW;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,CAAC;IAED,IACc,cAAc;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC;QACnC,IAAI,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;QACjD,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;IACrC,CAAC;IAED,IACc,eAAe;QAC3B,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC;QAC/C,IAAI,IAAI;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YACxC,IAAI,GAAG;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,CAAC;IAGS,OAAO;QACf,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;YAAE,OAAO;QACzC,IAAI,IAAI,CAAC,YAAY,CAAC,eAAe;YAAE,OAAO;QAC9C,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;YAE/B,0DAA0D;YAC1D,0CAA0C;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,aAAiC,CAAC;YACxD,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;YAEjC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAE1C,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IAGS,SAAS,CAAC,KAAoB;QACtC,6EAA6E;QAC7E,oFAAoF;QACpF,IACE,KAAK,CAAC,GAAG,KAAK,OAAO;YACrB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YACxB,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;YAC7B,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;YAC/B,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAC3B,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,IAAI,CAAC;YACtD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,eAAe,EAAE,CAAC;gBACxB,MAAM,KAAK,GAAI,IAAI,CAAC,EAAE,CAAC,aAAkC,EAAE,KAAK,IAAI,EAAE,CAAC;gBACvE,IAAI,CAAC,YAAY,CAAC,kBAAkB,GAAG,IAAI,CAAC;gBAC5C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;gBACzC,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;QACH,CAAC;QAED,6GAA6G;QAC7G,IACE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YACzB,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;YAC7B,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC;YACtB,KAAK,CAAC,GAAG,KAAK,GAAG;YACjB,CAAC,KAAK,CAAC,OAAO;YACd,CAAC,KAAK,CAAC,OAAO;YACd,CAAC,KAAK,CAAC,MAAM;YACb,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAC/C,CAAC;YACD,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;YAC/B,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC;YACvF,OAAO;QACT,CAAC;QAED,qBAAqB,CAAC,KAAK,EAAE;YAC3B,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;YACtC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YAC9B,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE;YAChD,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE;YACtC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,qBAAqB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC,EAAE,CAAC;SAC3E,EAAE;YACD,eAAe,EAAE,KAAK;YACtB,UAAU,EAAE,yBAAyB;YACrC,0BAA0B,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC;YACnD,YAAY,EAAE,KAAK,EAAE,uEAAuE;SAC7F,CAAC,CAAC;IACL,CAAC;IAGS,OAAO,CAAC,KAAY;QAC5B,MAAM,KAAK,GAAI,KAAK,CAAC,MAA2B,EAAE,KAAK,IAAI,EAAE,CAAC;QAC9D,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;uGA9IU,sBAAsB;2FAAtB,sBAAsB;;2FAAtB,sBAAsB;kBAJlC,SAAS;mBAAC;oBACT,QAAQ,EAAE,0BAA0B;oBACpC,QAAQ,EAAE,wBAAwB;iBACnC;;sBASE,WAAW;uBAAC,gBAAgB;;sBAG5B,WAAW;uBAAC,WAAW;;sBAGvB,WAAW;uBAAC,oBAAoB;;sBAGhC,WAAW;uBAAC,oBAAoB;;sBAKhC,WAAW;uBAAC,oBAAoB;;sBAKhC,WAAW;uBAAC,oBAAoB;;sBAMhC,WAAW;uBAAC,4BAA4B;;sBAOxC,WAAW;uBAAC,mBAAmB;;sBAK/B,WAAW;uBAAC,sBAAsB;;sBAOlC,WAAW;uBAAC,uBAAuB;;sBAYnC,YAAY;uBAAC,OAAO;;sBAmBpB,YAAY;uBAAC,SAAS,EAAE,CAAC,QAAQ,CAAC;;sBAsDlC,YAAY;uBAAC,OAAO,EAAE,CAAC,QAAQ,CAAC;;AAQnC,oHAAoH;AAKpH,MAAM,OAAO,+BAA+B;IAEvB,QAAQ,GAAG,gCAAyC,CAAC;uGAF7D,+BAA+B;2FAA/B,+BAA+B;;2FAA/B,+BAA+B;kBAJ3C,SAAS;mBAAC;oBACT,QAAQ,EAAE,mCAAmC;oBAC7C,QAAQ,EAAE,iCAAiC;iBAC5C;;sBAEE,WAAW;uBAAC,gBAAgB;;AAI/B,oHAAoH;AAKpH,MAAM,OAAO,mBAAmB;IACb,YAAY,GAAG,MAAM,CAAkB,gBAAgB,CAAC,CAAC;IAGvD,QAAQ,GAAG,mBAA4B,CAAC;IAGjD,OAAO;QACf,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;YAAE,OAAO;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,aAAa,CACzD,oCAAoC,CACf,CAAC;QACxB,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;uGAbU,mBAAmB;2FAAnB,mBAAmB;;2FAAnB,mBAAmB;kBAJ/B,SAAS;mBAAC;oBACT,QAAQ,EAAE,uBAAuB;oBACjC,QAAQ,EAAE,qBAAqB;iBAChC;;sBAIE,WAAW;uBAAC,gBAAgB;;sBAG5B,YAAY;uBAAC,OAAO;;AAUvB,MAAM,eAAe,GAAG,kBAAkB,CAAC,0BAA0B,CAAC,CAAC;AAMvE,MAAM,OAAO,sBAAsB;IAChB,YAAY,GAAG,MAAM,CAAkB,gBAAgB,CAAC,CAAC;IACzD,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAGxC,EAAE,GAAG,eAAe,EAAE,CAAC;IAGb,QAAQ,GAAG,sBAA+B,CAAC;IAE9D,IACc,MAAM;QAClB,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,IACc,QAAQ;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,CAAC;IAED;QACE,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,EAAE;YAC7B,IAAI,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;gBACjD,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;uGA3BU,sBAAsB;2FAAtB,sBAAsB;;2FAAtB,sBAAsB;kBAJlC,SAAS;mBAAC;oBACT,QAAQ,EAAE,0BAA0B;oBACpC,QAAQ,EAAE,wBAAwB;iBACnC;;sBAKE,WAAW;uBAAC,SAAS;;sBAGrB,WAAW;uBAAC,gBAAgB;;sBAG5B,WAAW;uBAAC,aAAa;;sBAKzB,WAAW;uBAAC,gBAAgB","sourcesContent":["import { DestroyRef, Directive, ElementRef, HostBinding, HostListener, inject } from '@angular/core';\nimport { createTngIdFactory } from '@tailng-ui/cdk';\nimport { ensureActiveAndSync, handleComboboxKeydown } from '../../internal/combobox';\nimport type { TngAutocomplete } from './tng-autocomplete';\nimport { TNG_AUTOCOMPLETE } from './tng-autocomplete.tokens';\nimport type { TngAutocompleteListboxApi } from './tng-autocomplete.listbox.types';\n\n/** Keys that open autocomplete when closed. ArrowDown/Up + Backspace/Delete (user editing clears selection). */\nconst AUTOCOMPLETE_KEYS_TO_OPEN = ['ArrowDown', 'ArrowUp', 'Backspace', 'Delete'] as const;\n\n@Directive({\n selector: '[tngAutocompleteTrigger]',\n exportAs: 'tngAutocompleteTrigger',\n})\nexport class TngAutocompleteTrigger {\n private readonly autocomplete = inject<TngAutocomplete>(TNG_AUTOCOMPLETE);\n private readonly el = inject(ElementRef<HTMLElement>);\n\n private get listbox(): TngAutocompleteListboxApi | null {\n return this.autocomplete.getListboxApi();\n }\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-trigger' as const;\n\n @HostBinding('attr.role')\n protected readonly role = 'combobox' as const;\n\n @HostBinding('attr.aria-haspopup')\n protected readonly haspopup = 'listbox' as const;\n\n @HostBinding('attr.aria-expanded')\n protected get ariaExpanded(): 'true' | 'false' {\n return this.autocomplete.open() ? 'true' : 'false';\n }\n\n @HostBinding('attr.aria-disabled')\n protected get ariaDisabled(): 'true' | null {\n return this.autocomplete.disabled() ? 'true' : null;\n }\n\n @HostBinding('attr.aria-controls')\n protected get ariaControls(): string | null {\n if (!this.autocomplete.open()) return null;\n return this.autocomplete.getContentId() ?? this.autocomplete.getListboxId();\n }\n\n @HostBinding('attr.aria-activedescendant')\n protected get ariaActiveDescendant(): string | null {\n if (!this.autocomplete.open()) return null;\n if (this.listbox) return this.listbox.getActiveId();\n return this.autocomplete.getActiveDescendantId();\n }\n\n @HostBinding('attr.aria-invalid')\n protected get ariaInvalid(): 'true' | null {\n return this.autocomplete.invalid() ? 'true' : null;\n }\n\n @HostBinding('attr.aria-labelledby')\n protected get ariaLabelledby(): string | null {\n const node = this.el.nativeElement;\n if (node.hasAttribute('aria-label')) return null;\n return this.autocomplete.labelId();\n }\n\n @HostBinding('attr.aria-describedby')\n protected get ariaDescribedby(): string | null {\n const ids: string[] = [];\n const desc = this.autocomplete.descriptionId();\n if (desc) ids.push(desc);\n if (this.autocomplete.invalid()) {\n const err = this.autocomplete.errorId();\n if (err) ids.push(err);\n }\n return ids.length ? ids.join(' ') : null;\n }\n\n @HostListener('focus')\n protected onFocus(): void {\n if (this.autocomplete.disabled()) return;\n if (this.autocomplete._restoringFocus) return;\n if (!this.autocomplete.open()) {\n this.autocomplete.openSelect();\n\n // ✅ Emit empty query (or current query) on open-on-focus.\n // This is the behavior your test expects.\n const input = this.el.nativeElement as HTMLInputElement;\n const value = input?.value ?? '';\n\n this.autocomplete.query.set(value);\n this.autocomplete.queryChange.emit(value);\n \n ensureActiveAndSync(this.listbox, (id) => this.autocomplete.setActiveDescendantId(id));\n }\n }\n\n @HostListener('keydown', ['$event'])\n protected onKeydown(event: KeyboardEvent): void {\n // Free-form create: Enter (only) with no active option → emit create, close.\n // Space is NOT used here so it can insert into input for typing (e.g. \"United St\").\n if (\n event.key === 'Enter' &&\n this.autocomplete.open() &&\n !this.autocomplete.disabled() &&\n this.autocomplete.allowCreate() &&\n !this.autocomplete.strict()\n ) {\n const hasActive = this.listbox?.getActiveId() != null;\n if (!hasActive) {\n event.preventDefault();\n event.stopPropagation();\n const query = (this.el.nativeElement as HTMLInputElement)?.value ?? '';\n this.autocomplete._createJustEmitted = true;\n this.autocomplete.create.emit({ query });\n queueMicrotask(() => this.autocomplete.close());\n return;\n }\n }\n\n // When closed, typeable keys (a-z, 0-9, etc.) open overlay without preventDefault so input receives the char\n if (\n !this.autocomplete.open() &&\n !this.autocomplete.disabled() &&\n event.key.length === 1 &&\n event.key !== ' ' &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !['Tab', 'Escape', 'Enter'].includes(event.key)\n ) {\n this.autocomplete.openSelect();\n ensureActiveAndSync(this.listbox, (id) => this.autocomplete.setActiveDescendantId(id));\n return;\n }\n\n handleComboboxKeydown(event, {\n disabled: this.autocomplete.disabled(),\n open: this.autocomplete.open(),\n openSelect: () => this.autocomplete.openSelect(),\n close: () => this.autocomplete.close(),\n listbox: this.listbox,\n setActiveDescendantId: (id) => this.autocomplete.setActiveDescendantId(id),\n }, {\n enableTypeahead: false,\n keysToOpen: AUTOCOMPLETE_KEYS_TO_OPEN,\n keysToOpenNoPreventDefault: ['Backspace', 'Delete'],\n spaceCommits: false, // Space inserts into input for typing (e.g. \"United St\" for filtering)\n });\n }\n\n @HostListener('input', ['$event'])\n protected onInput(event: Event): void {\n const value = (event.target as HTMLInputElement)?.value ?? '';\n this.autocomplete.query.set(value);\n this.autocomplete.queryChange.emit(value);\n }\n}\n\n/** Wrapper for trigger + optional icon. When present, overlay uses this for width/position (full control width). */\n@Directive({\n selector: '[tngAutocompleteTriggerContainer]',\n exportAs: 'tngAutocompleteTriggerContainer',\n})\nexport class TngAutocompleteTriggerContainer {\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-trigger-container' as const;\n}\n\n/** Slot for an icon (e.g. chevron) beside the trigger. Consumer provides markup. Matches Select's tngSelectIcon. */\n@Directive({\n selector: '[tngAutocompleteIcon]',\n exportAs: 'tngAutocompleteIcon',\n})\nexport class TngAutocompleteIcon {\n private readonly autocomplete = inject<TngAutocomplete>(TNG_AUTOCOMPLETE);\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-icon' as const;\n\n @HostListener('click')\n protected onClick(): void {\n if (this.autocomplete.disabled()) return;\n const trigger = this.autocomplete.hostElement.querySelector(\n '[data-slot=\"autocomplete-trigger\"]'\n ) as HTMLElement | null;\n trigger?.focus();\n }\n}\n\nconst createContentId = createTngIdFactory('tng-autocomplete-content');\n\n@Directive({\n selector: '[tngAutocompleteContent]',\n exportAs: 'tngAutocompleteContent',\n})\nexport class TngAutocompleteContent {\n private readonly autocomplete = inject<TngAutocomplete>(TNG_AUTOCOMPLETE);\n private readonly destroyRef = inject(DestroyRef);\n\n @HostBinding('attr.id')\n readonly id = createContentId();\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-content' as const;\n\n @HostBinding('attr.hidden')\n protected get hidden(): '' | null {\n return this.autocomplete.open() ? null : '';\n }\n\n @HostBinding('attr.aria-busy')\n protected get ariaBusy(): 'true' | null {\n return this.autocomplete.loading() ? 'true' : null;\n }\n\n constructor() {\n this.autocomplete.setContentId(this.id);\n this.destroyRef.onDestroy(() => {\n if (this.autocomplete.getContentId() === this.id) {\n this.autocomplete.setContentId(null);\n }\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tng-autocomplete.parts.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/autocomplete/tng-autocomplete.parts.ts"],"names":[],"mappings":"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","sourcesContent":["import { DestroyRef, Directive, ElementRef, HostBinding, HostListener, inject } from '@angular/core';\nimport { createTngIdFactory } from '@tailng-ui/cdk';\nimport { ensureActiveAndSync, handleComboboxKeydown } from '../../internal/combobox';\nimport type { TngAutocomplete } from './tng-autocomplete';\nimport { TNG_AUTOCOMPLETE } from './tng-autocomplete.tokens';\nimport type { TngAutocompleteListboxApi } from './tng-autocomplete.listbox.types';\n\n/** Keys that open autocomplete when closed. ArrowDown/Up + Backspace/Delete (user editing clears selection). */\nconst AUTOCOMPLETE_KEYS_TO_OPEN = ['ArrowDown', 'ArrowUp', 'Backspace', 'Delete'] as const;\n\n@Directive({\n selector: '[tngAutocompleteTrigger]',\n exportAs: 'tngAutocompleteTrigger',\n})\nexport class TngAutocompleteTrigger {\n private readonly autocomplete = inject<TngAutocomplete>(TNG_AUTOCOMPLETE);\n private readonly el = inject(ElementRef<HTMLElement>);\n\n private get listbox(): TngAutocompleteListboxApi | null {\n return this.autocomplete.getListboxApi();\n }\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-trigger' as const;\n\n @HostBinding('attr.role')\n protected readonly role = 'combobox' as const;\n\n @HostBinding('attr.aria-haspopup')\n protected readonly haspopup = 'listbox' as const;\n\n @HostBinding('attr.aria-expanded')\n protected get ariaExpanded(): 'true' | 'false' {\n return this.autocomplete.open() ? 'true' : 'false';\n }\n\n @HostBinding('attr.aria-disabled')\n protected get ariaDisabled(): 'true' | null {\n return this.autocomplete.disabled() ? 'true' : null;\n }\n\n @HostBinding('attr.aria-controls')\n protected get ariaControls(): string | null {\n if (!this.autocomplete.open()) return null;\n return this.autocomplete.getContentId() ?? this.autocomplete.getListboxId();\n }\n\n @HostBinding('attr.aria-activedescendant')\n protected get ariaActiveDescendant(): string | null {\n if (!this.autocomplete.open()) return null;\n if (this.listbox) return this.listbox.getActiveId();\n return this.autocomplete.getActiveDescendantId();\n }\n\n @HostBinding('attr.aria-invalid')\n protected get ariaInvalid(): 'true' | null {\n return this.autocomplete.invalid() ? 'true' : null;\n }\n\n @HostBinding('attr.aria-labelledby')\n protected get ariaLabelledby(): string | null {\n const node = this.el.nativeElement;\n if (node.hasAttribute('aria-label')) return null;\n return this.autocomplete.labelId();\n }\n\n @HostBinding('attr.aria-describedby')\n protected get ariaDescribedby(): string | null {\n const ids: string[] = [];\n const desc = this.autocomplete.descriptionId();\n if (desc) ids.push(desc);\n if (this.autocomplete.invalid()) {\n const err = this.autocomplete.errorId();\n if (err) ids.push(err);\n }\n return ids.length ? ids.join(' ') : null;\n }\n\n @HostListener('focus')\n protected onFocus(): void {\n if (this.autocomplete.disabled()) return;\n if (this.autocomplete._restoringFocus) return;\n if (!this.autocomplete.open()) {\n const input = this.el.nativeElement as HTMLInputElement;\n const value = input?.value ?? '';\n\n this.autocomplete.openSelect();\n\n this.autocomplete.query.set(value);\n this.autocomplete.queryChange.emit(value);\n \n ensureActiveAndSync(this.listbox, (id) => this.autocomplete.setActiveDescendantId(id));\n }\n }\n\n @HostListener('keydown', ['$event'])\n protected onKeydown(event: KeyboardEvent): void {\n // Free-form create: Enter (only) with no active option → emit create, close.\n // Space is NOT used here so it can insert into input for typing (e.g. \"United St\").\n if (\n event.key === 'Enter' &&\n this.autocomplete.open() &&\n !this.autocomplete.disabled() &&\n this.autocomplete.allowCreate() &&\n !this.autocomplete.strict()\n ) {\n const hasActive = this.listbox?.getActiveId() != null;\n if (!hasActive) {\n event.preventDefault();\n event.stopPropagation();\n const query = (this.el.nativeElement as HTMLInputElement)?.value ?? '';\n this.autocomplete._createJustEmitted = true;\n this.autocomplete.create.emit({ query });\n queueMicrotask(() => this.autocomplete.close());\n return;\n }\n }\n\n // When closed, typeable keys (a-z, 0-9, etc.) open overlay without preventDefault so input receives the char\n if (\n !this.autocomplete.open() &&\n !this.autocomplete.disabled() &&\n event.key.length === 1 &&\n event.key !== ' ' &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !['Tab', 'Escape', 'Enter'].includes(event.key)\n ) {\n this.autocomplete.openSelect();\n ensureActiveAndSync(this.listbox, (id) => this.autocomplete.setActiveDescendantId(id));\n return;\n }\n\n handleComboboxKeydown(event, {\n disabled: this.autocomplete.disabled(),\n open: this.autocomplete.open(),\n openSelect: () => this.autocomplete.openSelect(),\n close: () => this.autocomplete.close(),\n listbox: this.listbox,\n setActiveDescendantId: (id) => this.autocomplete.setActiveDescendantId(id),\n }, {\n enableTypeahead: false,\n keysToOpen: AUTOCOMPLETE_KEYS_TO_OPEN,\n keysToOpenNoPreventDefault: ['Backspace', 'Delete'],\n spaceCommits: false, // Space inserts into input for typing (e.g. \"United St\" for filtering)\n });\n }\n\n @HostListener('input', ['$event'])\n protected onInput(event: Event): void {\n const value = (event.target as HTMLInputElement)?.value ?? '';\n this.autocomplete.query.set(value);\n this.autocomplete.queryChange.emit(value);\n }\n}\n\n/** Wrapper for trigger + optional icon. When present, overlay uses this for width/position (full control width). */\n@Directive({\n selector: '[tngAutocompleteTriggerContainer]',\n exportAs: 'tngAutocompleteTriggerContainer',\n})\nexport class TngAutocompleteTriggerContainer {\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-trigger-container' as const;\n}\n\n/** Slot for an icon (e.g. chevron) beside the trigger. Consumer provides markup. Matches Select's tngSelectIcon. */\n@Directive({\n selector: '[tngAutocompleteIcon]',\n exportAs: 'tngAutocompleteIcon',\n})\nexport class TngAutocompleteIcon {\n private readonly autocomplete = inject<TngAutocomplete>(TNG_AUTOCOMPLETE);\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-icon' as const;\n\n @HostListener('click')\n protected onClick(): void {\n if (this.autocomplete.disabled()) return;\n const trigger = this.autocomplete.hostElement.querySelector(\n '[data-slot=\"autocomplete-trigger\"]'\n ) as HTMLElement | null;\n trigger?.focus();\n }\n}\n\nconst createContentId = createTngIdFactory('tng-autocomplete-content');\n\n@Directive({\n selector: '[tngAutocompleteContent]',\n exportAs: 'tngAutocompleteContent',\n})\nexport class TngAutocompleteContent {\n private readonly autocomplete = inject<TngAutocomplete>(TNG_AUTOCOMPLETE);\n private readonly destroyRef = inject(DestroyRef);\n\n @HostBinding('attr.id')\n readonly id = createContentId();\n\n @HostBinding('attr.data-slot')\n protected readonly dataSlot = 'autocomplete-content' as const;\n\n @HostBinding('attr.hidden')\n protected get hidden(): '' | null {\n return this.autocomplete.open() ? null : '';\n }\n\n @HostBinding('attr.aria-busy')\n protected get ariaBusy(): 'true' | null {\n return this.autocomplete.loading() ? 'true' : null;\n }\n\n constructor() {\n this.autocomplete.setContentId(this.id);\n this.destroyRef.onDestroy(() => {\n if (this.autocomplete.getContentId() === this.id) {\n this.autocomplete.setContentId(null);\n }\n });\n }\n}\n"]}
|
|
@@ -12,6 +12,7 @@ export declare class TngOptionDirective<T = unknown> implements AfterViewInit, O
|
|
|
12
12
|
get hostId(): string;
|
|
13
13
|
get ariaSelected(): 'true' | 'false';
|
|
14
14
|
get ariaDisabled(): 'true' | null;
|
|
15
|
+
readonly tabIndex = "-1";
|
|
15
16
|
get dataActive(): '' | null;
|
|
16
17
|
get dataSelected(): '' | null;
|
|
17
18
|
get dataDisabled(): '' | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"option.directive.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/listbox/option.directive.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EAKb,SAAS,EAKV,MAAM,eAAe,CAAC;;AAOvB,qBAGa,kBAAkB,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,aAAa,EAAE,SAAS;IAC9E,QAAQ,qDAA0B;IAClC,QAAQ,+CAAyB;IAEjC,OAAO,CAAC,EAAE,CAAmC;IAC7C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,EAAE,CAAc;IAGxB,OAAO,CAAC,UAAU,CAAiB;IAGnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAG3B;IAGH,IAAI,SAAY;IAEhB,IACI,MAAM,WAET;IAED,IACI,YAAY,IAAI,MAAM,GAAG,OAAO,CAEnC;IAED,IACI,YAAY,IAAI,MAAM,GAAG,IAAI,CAEhC;IAGD,IACI,UAAU,IAAI,EAAE,GAAG,IAAI,CAE1B;IAED,IACI,YAAY,IAAI,EAAE,GAAG,IAAI,CAE5B;IAED,IACI,YAAY,IAAI,EAAE,GAAG,IAAI,CAE5B;IAED,eAAe;IAWf,WAAW;IAOX,aAAa,CAAC,KAAK,EAAE,YAAY;IAcjC,OAAO,CAAC,UAAU;
|
|
1
|
+
{"version":3,"file":"option.directive.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/listbox/option.directive.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EAKb,SAAS,EAKV,MAAM,eAAe,CAAC;;AAOvB,qBAGa,kBAAkB,CAAC,CAAC,GAAG,OAAO,CAAE,YAAW,aAAa,EAAE,SAAS;IAC9E,QAAQ,qDAA0B;IAClC,QAAQ,+CAAyB;IAEjC,OAAO,CAAC,EAAE,CAAmC;IAC7C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,EAAE,CAAc;IAGxB,OAAO,CAAC,UAAU,CAAiB;IAGnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAG3B;IAGH,IAAI,SAAY;IAEhB,IACI,MAAM,WAET;IAED,IACI,YAAY,IAAI,MAAM,GAAG,OAAO,CAEnC;IAED,IACI,YAAY,IAAI,MAAM,GAAG,IAAI,CAEhC;IAGD,QAAQ,CAAC,QAAQ,QAAQ;IAGzB,IACI,UAAU,IAAI,EAAE,GAAG,IAAI,CAE1B;IAED,IACI,YAAY,IAAI,EAAE,GAAG,IAAI,CAE5B;IAED,IACI,YAAY,IAAI,EAAE,GAAG,IAAI,CAE5B;IAED,eAAe;IAWf,WAAW;IAOX,aAAa,CAAC,KAAK,EAAE,YAAY;IAcjC,OAAO,CAAC,UAAU;yCAtFP,kBAAkB;2CAAlB,kBAAkB;CA+F9B"}
|
|
@@ -28,6 +28,7 @@ export class TngOptionDirective {
|
|
|
28
28
|
get ariaDisabled() {
|
|
29
29
|
return this.disabled() ? 'true' : null;
|
|
30
30
|
}
|
|
31
|
+
tabIndex = '-1';
|
|
31
32
|
// ✅ Tailwind-friendly state attributes (presence based)
|
|
32
33
|
get dataActive() {
|
|
33
34
|
return this.listbox.isActive(this.id) ? '' : null;
|
|
@@ -75,7 +76,7 @@ export class TngOptionDirective {
|
|
|
75
76
|
return this.listbox.isValueSelected(value);
|
|
76
77
|
}
|
|
77
78
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TngOptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
78
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: TngOptionDirective, isStandalone: true, selector: "[tngOption]", inputs: { tngValue: { classPropertyName: "tngValue", publicName: "tngValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onPointerDown($event)" }, properties: { "attr.role": "this.role", "attr.id": "this.hostId", "attr.aria-selected": "this.ariaSelected", "attr.aria-disabled": "this.ariaDisabled", "attr.data-active": "this.dataActive", "attr.data-selected": "this.dataSelected", "attr.data-disabled": "this.dataDisabled" } }, ngImport: i0 });
|
|
79
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: TngOptionDirective, isStandalone: true, selector: "[tngOption]", inputs: { tngValue: { classPropertyName: "tngValue", publicName: "tngValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "pointerdown": "onPointerDown($event)" }, properties: { "attr.role": "this.role", "attr.id": "this.hostId", "attr.aria-selected": "this.ariaSelected", "attr.aria-disabled": "this.ariaDisabled", "attr.tabindex": "this.tabIndex", "attr.data-active": "this.dataActive", "attr.data-selected": "this.dataSelected", "attr.data-disabled": "this.dataDisabled" } }, ngImport: i0 });
|
|
79
80
|
}
|
|
80
81
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TngOptionDirective, decorators: [{
|
|
81
82
|
type: Directive,
|
|
@@ -94,6 +95,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
94
95
|
}], ariaDisabled: [{
|
|
95
96
|
type: HostBinding,
|
|
96
97
|
args: ['attr.aria-disabled']
|
|
98
|
+
}], tabIndex: [{
|
|
99
|
+
type: HostBinding,
|
|
100
|
+
args: ['attr.tabindex']
|
|
97
101
|
}], dataActive: [{
|
|
98
102
|
type: HostBinding,
|
|
99
103
|
args: ['attr.data-active']
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"option.directive.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/listbox/option.directive.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,OAAO,EAEL,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EAEZ,MAAM,EACN,MAAM,EACN,KAAK,EACL,MAAM,GACP,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;;AAEvC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAKlD,MAAM,OAAO,kBAAkB;IAC7B,QAAQ,GAAG,KAAK,8DAAiB,CAAC;IAClC,QAAQ,GAAG,KAAK,CAAU,KAAK,oDAAC,CAAC;IAEzB,EAAE,GAAG,MAAM,CAAC,CAAA,UAAuB,CAAA,CAAC,CAAC;IACrC,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC9B,EAAE,GAAG,QAAQ,EAAE,CAAC;IAExB,iEAAiE;IACzD,UAAU,GAAG,MAAM,CAAC,KAAK,sDAAC,CAAC;IAEnC,+DAA+D;IAC9C,aAAa,GAAG,MAAM,CAAC,GAAG,EAAE;QAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;YAAE,OAAO;QAC/B,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,CAAC,yDAAC,CAAC;IAGH,IAAI,GAAG,QAAQ,CAAC;IAEhB,IACI,MAAM;QACR,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,IACI,YAAY;QACd,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9C,CAAC;IAED,IACI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,CAAC;
|
|
1
|
+
{"version":3,"file":"option.directive.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/primitives/src/lib/form/listbox/option.directive.ts"],"names":[],"mappings":"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","sourcesContent":["// option.directive.ts\nimport {\n AfterViewInit,\n Directive,\n ElementRef,\n HostBinding,\n HostListener,\n OnDestroy,\n effect,\n inject,\n input,\n signal,\n} from '@angular/core';\n\nimport { createTngIdFactory } from '@tailng-ui/cdk';\nimport { TNG_LISTBOX } from './tokens';\n\nconst createId = createTngIdFactory('tng-option');\n\n@Directive({\n selector: '[tngOption]',\n})\nexport class TngOptionDirective<T = unknown> implements AfterViewInit, OnDestroy {\n tngValue = input<T | undefined>();\n disabled = input<boolean>(false);\n\n private el = inject(ElementRef<HTMLElement>);\n private listbox = inject(TNG_LISTBOX);\n private id = createId();\n\n // gate to ensure we only push updates after initial registration\n private registered = signal(false);\n\n // ✅ effect is created in injection context (field initializer)\n private readonly _syncDisabled = effect(() => {\n if (!this.registered()) return;\n this.listbox.updateOptionDisabled(this.id, this.disabled());\n });\n\n @HostBinding('attr.role')\n role = 'option';\n\n @HostBinding('attr.id')\n get hostId() {\n return this.id;\n }\n\n @HostBinding('attr.aria-selected')\n get ariaSelected(): 'true' | 'false' {\n return this.isSelected() ? 'true' : 'false';\n }\n\n @HostBinding('attr.aria-disabled')\n get ariaDisabled(): 'true' | null {\n return this.disabled() ? 'true' : null;\n }\n\n @HostBinding('attr.tabindex')\n readonly tabIndex = '-1';\n\n // ✅ Tailwind-friendly state attributes (presence based)\n @HostBinding('attr.data-active')\n get dataActive(): '' | null {\n return this.listbox.isActive(this.id) ? '' : null;\n }\n\n @HostBinding('attr.data-selected')\n get dataSelected(): '' | null {\n return this.isSelected() ? '' : null;\n }\n\n @HostBinding('attr.data-disabled')\n get dataDisabled(): '' | null {\n return this.disabled() ? '' : null;\n }\n\n ngAfterViewInit() {\n const value = this.tngValue();\n if (value === undefined) return;\n\n const text = this.el.nativeElement.textContent?.trim() ?? '';\n // pass text for typeahead\n this.listbox.registerOption(this.id, value, this.disabled(), text, this.el.nativeElement);\n\n this.registered.set(true);\n }\n\n ngOnDestroy() {\n this.listbox.unregisterOption(this.id);\n this.registered.set(false);\n // Angular will destroy directive + tear down effect automatically\n }\n\n @HostListener('pointerdown', ['$event'])\n onPointerDown(event: PointerEvent) {\n if (this.disabled()) {\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n if (event.button !== 0) return;\n\n // Prevent focus from moving / text selection, keep listbox behavior stable.\n event.preventDefault();\n\n this.listbox.handleOptionClick(this.id, event.shiftKey);\n }\n\n private isSelected(): boolean {\n if (this.listbox.isSelected(this.id)) return true;\n if (this.disabled()) return false;\n\n const value = this.tngValue();\n if (value === undefined) return false;\n\n return this.listbox.isValueSelected(value);\n }\n}\n"]}
|