@aquera/nile-elements 2.0.18 → 2.0.20

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.
Files changed (138) hide show
  1. package/README.md +10 -0
  2. package/demo/index.js +0 -1
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.esm.js +1 -1
  5. package/dist/index.js +1275 -490
  6. package/dist/nile-badge/index.cjs.js +1 -1
  7. package/dist/nile-badge/index.esm.js +1 -1
  8. package/dist/nile-badge/nile-badge.cjs.js +1 -1
  9. package/dist/nile-badge/nile-badge.cjs.js.map +1 -1
  10. package/dist/nile-badge/nile-badge.css.cjs.js +1 -1
  11. package/dist/nile-badge/nile-badge.css.cjs.js.map +1 -1
  12. package/dist/nile-badge/nile-badge.css.esm.js +24 -3
  13. package/dist/nile-badge/nile-badge.esm.js +5 -3
  14. package/dist/nile-breadcrumb/nile-breadcrumb.cjs.js +1 -1
  15. package/dist/nile-breadcrumb/nile-breadcrumb.cjs.js.map +1 -1
  16. package/dist/nile-breadcrumb/nile-breadcrumb.esm.js +2 -2
  17. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.cjs.js +1 -1
  18. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.cjs.js.map +1 -1
  19. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.css.cjs.js +1 -1
  20. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.css.cjs.js.map +1 -1
  21. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.css.esm.js +14 -2
  22. package/dist/nile-breadcrumb-item/nile-breadcrumb-item.esm.js +4 -4
  23. package/dist/nile-calendar/index.cjs.js +1 -1
  24. package/dist/nile-calendar/index.esm.js +1 -1
  25. package/dist/nile-calendar/nile-calendar.cjs.js +2 -1
  26. package/dist/nile-calendar/nile-calendar.cjs.js.map +1 -1
  27. package/dist/nile-calendar/nile-calendar.css.cjs.js +1 -1
  28. package/dist/nile-calendar/nile-calendar.css.cjs.js.map +1 -1
  29. package/dist/nile-calendar/nile-calendar.css.esm.js +357 -2
  30. package/dist/nile-calendar/nile-calendar.esm.js +365 -41
  31. package/dist/nile-calendar/timezones.cjs.js +1 -1
  32. package/dist/nile-calendar/timezones.cjs.js.map +1 -1
  33. package/dist/nile-calendar/timezones.esm.js +1 -1
  34. package/dist/nile-date-time-picker/index.cjs.js +2 -0
  35. package/dist/nile-date-time-picker/index.cjs.js.map +1 -0
  36. package/dist/nile-date-time-picker/index.esm.js +1 -0
  37. package/dist/nile-date-time-picker/nile-date-time-picker.cjs.js +2 -0
  38. package/dist/nile-date-time-picker/nile-date-time-picker.cjs.js.map +1 -0
  39. package/dist/nile-date-time-picker/nile-date-time-picker.css.cjs.js +2 -0
  40. package/dist/nile-date-time-picker/nile-date-time-picker.css.cjs.js.map +1 -0
  41. package/dist/nile-date-time-picker/nile-date-time-picker.css.esm.js +13 -0
  42. package/dist/nile-date-time-picker/nile-date-time-picker.esm.js +53 -0
  43. package/dist/nile-select/index.cjs.js +1 -1
  44. package/dist/nile-select/index.esm.js +1 -1
  45. package/dist/nile-select/nile-select.cjs.js +2 -2
  46. package/dist/nile-select/nile-select.cjs.js.map +1 -1
  47. package/dist/nile-select/nile-select.esm.js +1 -1
  48. package/dist/nile-select/virtual-scroll-helper.cjs.js +1 -1
  49. package/dist/nile-select/virtual-scroll-helper.cjs.js.map +1 -1
  50. package/dist/nile-select/virtual-scroll-helper.esm.js +48 -47
  51. package/dist/nile-virtual-select/nile-virtual-select.cjs.js +4 -4
  52. package/dist/nile-virtual-select/nile-virtual-select.cjs.js.map +1 -1
  53. package/dist/nile-virtual-select/nile-virtual-select.esm.js +2 -2
  54. package/dist/src/index.d.ts +1 -0
  55. package/dist/src/index.js +1 -0
  56. package/dist/src/index.js.map +1 -1
  57. package/dist/src/nile-badge/nile-badge.css.js +24 -3
  58. package/dist/src/nile-badge/nile-badge.css.js.map +1 -1
  59. package/dist/src/nile-badge/nile-badge.d.ts +7 -4
  60. package/dist/src/nile-badge/nile-badge.js +22 -5
  61. package/dist/src/nile-badge/nile-badge.js.map +1 -1
  62. package/dist/src/nile-badge/nile-badge.test.js +88 -0
  63. package/dist/src/nile-badge/nile-badge.test.js.map +1 -1
  64. package/dist/src/nile-breadcrumb/nile-breadcrumb.d.ts +7 -0
  65. package/dist/src/nile-breadcrumb/nile-breadcrumb.js +24 -1
  66. package/dist/src/nile-breadcrumb/nile-breadcrumb.js.map +1 -1
  67. package/dist/src/nile-breadcrumb/nile-breadcrumb.test.js +51 -0
  68. package/dist/src/nile-breadcrumb/nile-breadcrumb.test.js.map +1 -1
  69. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.css.js +12 -0
  70. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.css.js.map +1 -1
  71. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.d.ts +5 -0
  72. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.js +9 -0
  73. package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.js.map +1 -1
  74. package/dist/src/nile-calendar/nile-calendar.css.js +355 -0
  75. package/dist/src/nile-calendar/nile-calendar.css.js.map +1 -1
  76. package/dist/src/nile-calendar/nile-calendar.d.ts +175 -0
  77. package/dist/src/nile-calendar/nile-calendar.js +1726 -26
  78. package/dist/src/nile-calendar/nile-calendar.js.map +1 -1
  79. package/dist/src/nile-calendar/nile-calendar.test.d.ts +1 -0
  80. package/dist/src/nile-calendar/nile-calendar.test.js +3580 -0
  81. package/dist/src/nile-calendar/nile-calendar.test.js.map +1 -1
  82. package/dist/src/nile-calendar/timezones.d.ts +21 -0
  83. package/dist/src/nile-calendar/timezones.js +49 -0
  84. package/dist/src/nile-calendar/timezones.js.map +1 -1
  85. package/dist/src/nile-date-time-picker/index.d.ts +1 -0
  86. package/dist/src/nile-date-time-picker/index.js +2 -0
  87. package/dist/src/nile-date-time-picker/index.js.map +1 -0
  88. package/dist/src/nile-date-time-picker/nile-date-time-picker.css.d.ts +11 -0
  89. package/dist/src/nile-date-time-picker/nile-date-time-picker.css.js +49 -0
  90. package/dist/src/nile-date-time-picker/nile-date-time-picker.css.js.map +1 -0
  91. package/dist/src/nile-date-time-picker/nile-date-time-picker.d.ts +173 -0
  92. package/dist/src/nile-date-time-picker/nile-date-time-picker.js +1270 -0
  93. package/dist/src/nile-date-time-picker/nile-date-time-picker.js.map +1 -0
  94. package/dist/src/nile-date-time-picker/nile-date-time-picker.test.d.ts +143 -0
  95. package/dist/src/nile-date-time-picker/nile-date-time-picker.test.js +7277 -0
  96. package/dist/src/nile-date-time-picker/nile-date-time-picker.test.js.map +1 -0
  97. package/dist/src/nile-select/nile-select.d.ts +1 -0
  98. package/dist/src/nile-select/nile-select.js +16 -1
  99. package/dist/src/nile-select/nile-select.js.map +1 -1
  100. package/dist/src/nile-select/nile-select.test.js +430 -5
  101. package/dist/src/nile-select/nile-select.test.js.map +1 -1
  102. package/dist/src/nile-select/virtual-scroll-helper.js +7 -4
  103. package/dist/src/nile-select/virtual-scroll-helper.js.map +1 -1
  104. package/dist/src/nile-virtual-select/nile-virtual-select.d.ts +3 -1
  105. package/dist/src/nile-virtual-select/nile-virtual-select.js +14 -6
  106. package/dist/src/nile-virtual-select/nile-virtual-select.js.map +1 -1
  107. package/dist/src/nile-virtual-select/nile-virtual-select.test.js +115 -1
  108. package/dist/src/nile-virtual-select/nile-virtual-select.test.js.map +1 -1
  109. package/dist/src/version.js +1 -1
  110. package/dist/src/version.js.map +1 -1
  111. package/dist/tsconfig.tsbuildinfo +1 -1
  112. package/dist/utilities/animation-registry.cjs.js +1 -1
  113. package/dist/utilities/animation-registry.cjs.js.map +1 -1
  114. package/dist/utilities/animation-registry.esm.js +1 -1
  115. package/package.json +3 -1
  116. package/scripts/coverage-report.mjs +96 -0
  117. package/src/index.ts +1 -0
  118. package/src/nile-badge/nile-badge.css.ts +24 -3
  119. package/src/nile-badge/nile-badge.test.ts +102 -0
  120. package/src/nile-badge/nile-badge.ts +28 -5
  121. package/src/nile-breadcrumb/nile-breadcrumb.test.ts +57 -0
  122. package/src/nile-breadcrumb/nile-breadcrumb.ts +19 -0
  123. package/src/nile-breadcrumb-item/nile-breadcrumb-item.css.ts +12 -0
  124. package/src/nile-breadcrumb-item/nile-breadcrumb-item.ts +7 -0
  125. package/src/nile-calendar/nile-calendar.css.ts +355 -0
  126. package/src/nile-calendar/nile-calendar.test.ts +3793 -0
  127. package/src/nile-calendar/nile-calendar.ts +1760 -28
  128. package/src/nile-calendar/timezones.ts +57 -0
  129. package/src/nile-date-time-picker/index.ts +1 -0
  130. package/src/nile-date-time-picker/nile-date-time-picker.css.ts +54 -0
  131. package/src/nile-date-time-picker/nile-date-time-picker.test.ts +7886 -0
  132. package/src/nile-date-time-picker/nile-date-time-picker.ts +1153 -0
  133. package/src/nile-select/nile-select.test.ts +560 -5
  134. package/src/nile-select/nile-select.ts +21 -1
  135. package/src/nile-select/virtual-scroll-helper.ts +7 -4
  136. package/src/nile-virtual-select/nile-virtual-select.test.ts +139 -1
  137. package/src/nile-virtual-select/nile-virtual-select.ts +17 -6
  138. package/vscode-html-custom-data.json +200 -20
@@ -1,2 +1,2 @@
1
- System.register([],function(_export,_context){"use strict";var n,t;function o(n,t){return"rtl"===t.toLowerCase()?{keyframes:n.rtlKeyframes||n.keyframes,options:n.options}:n;}function r(t,o){n.set(t,function(n){return n!==null&&n!==void 0?n:{keyframes:[],options:{duration:0}};}(o));}function e(r,e,s){var a=t.get(r);if(a!==null&&a!==void 0&&a[e])return o(a[e],s.dir);var i=n.get(e);return i?o(i,s.dir):{keyframes:[],options:{duration:0}};}_export({g:e,s:r});return{setters:[],execute:function execute(){n=new Map(),t=new WeakMap();}};});
1
+ System.register([],function(_export,_context){"use strict";var n,t;function _typeof(o){"@babel/helpers - typeof";return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o;}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o;},_typeof(o);}function ownKeys(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);r&&(o=o.filter(function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable;})),t.push.apply(t,o);}return t;}function _objectSpread(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?ownKeys(Object(t),!0).forEach(function(r){_defineProperty(e,r,t[r]);}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):ownKeys(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r));});}return e;}function _defineProperty(e,r,t){return(r=_toPropertyKey(r))in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e;}function _toPropertyKey(t){var i=_toPrimitive(t,"string");return"symbol"==_typeof(i)?i:i+"";}function _toPrimitive(t,r){if("object"!=_typeof(t)||!t)return t;var e=t[Symbol.toPrimitive];if(void 0!==e){var i=e.call(t,r||"default");if("object"!=_typeof(i))return i;throw new TypeError("@@toPrimitive must return a primitive value.");}return("string"===r?String:Number)(t);}function o(n){return n!==null&&n!==void 0?n:{keyframes:[],options:{duration:0}};}function r(n,t){return"rtl"===t.toLowerCase()?{keyframes:n.rtlKeyframes||n.keyframes,options:n.options}:n;}function e(t,r){n.set(t,o(r));}function s(n,r,e){t.set(n,_objectSpread(_objectSpread({},t.get(n)),{},_defineProperty({},r,o(e))));}function a(o,e,s){var a=t.get(o);if(a!==null&&a!==void 0&&a[e])return r(a[e],s.dir);var i=n.get(e);return i?r(i,s.dir):{keyframes:[],options:{duration:0}};}_export({a:s,g:a,s:e});return{setters:[],execute:function execute(){n=new Map(),t=new WeakMap();}};});
2
2
  //# sourceMappingURL=animation-registry.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"animation-registry.cjs.js","sources":["../../../src/utilities/animation-registry.ts"],"sourcesContent":["export interface ElementAnimation {\n keyframes: Keyframe[];\n rtlKeyframes?: Keyframe[];\n options?: KeyframeAnimationOptions;\n}\n\nexport interface ElementAnimationMap {\n [animationName: string]: ElementAnimation;\n}\n\nexport interface GetAnimationOptions {\n /**\n * The component's directionality. When set to \"rtl\", `rtlKeyframes` will be preferred over `keyframes` where\n * available using getAnimation().\n */\n dir: string;\n}\n\nconst defaultAnimationRegistry = new Map<string, ElementAnimation>();\nconst customAnimationRegistry = new WeakMap<Element, ElementAnimationMap>();\n\nfunction ensureAnimation(animation: ElementAnimation | null) {\n return animation ?? { keyframes: [], options: { duration: 0 } };\n}\n\n//\n// Given an ElementAnimation, this function returns a new ElementAnimation where the keyframes property reflects either\n// keyframes or rtlKeyframes depending on the specified directionality.\n//\nfunction getLogicalAnimation(animation: ElementAnimation, dir: string) {\n if (dir.toLowerCase() === 'rtl') {\n return {\n keyframes: animation.rtlKeyframes || animation.keyframes,\n options: animation.options\n };\n }\n\n return animation;\n}\n\n/**\n * Sets a default animation. Components should use the `name.animation` for primary animations and `name.part.animation`\n * for secondary animations, e.g. `dialog.show` and `dialog.overlay.show`. For modifiers, use `drawer.showTop`.\n */\nexport function setDefaultAnimation(animationName: string, animation: ElementAnimation | null) {\n defaultAnimationRegistry.set(animationName, ensureAnimation(animation));\n}\n\n/** Sets a custom animation for the specified element. */\nexport function setAnimation(el: Element, animationName: string, animation: ElementAnimation | null) {\n customAnimationRegistry.set(el, { ...customAnimationRegistry.get(el), [animationName]: ensureAnimation(animation) });\n}\n\n/** Gets an element's animation. Falls back to the default if no animation is found. */\nexport function getAnimation(el: Element, animationName: string, options: GetAnimationOptions) {\n const customAnimation = customAnimationRegistry.get(el);\n\n // Check for a custom animation\n if (customAnimation?.[animationName]) {\n return getLogicalAnimation(customAnimation[animationName], options.dir);\n }\n\n // Check for a default animation\n const defaultAnimation = defaultAnimationRegistry.get(animationName);\n if (defaultAnimation) {\n return getLogicalAnimation(defaultAnimation, options.dir);\n }\n\n // Fall back to an empty animation\n return {\n keyframes: [],\n options: { duration: 0 }\n };\n}\n"],"names":["getLogicalAnimation","animation","dir","toLowerCase","keyframes","rtlKeyframes","options","setDefaultAnimation","animationName","defaultAnimationRegistry","set","duration","ensureAnimation","getAnimation","el","customAnimation","customAnimationRegistry","get","defaultAnimation","Map","WeakMap"],"mappings":"mEA6BA,QAASA,CAAAA,CAAoBC,CAAAA,CAAAA,CAA6BC,CACxD,CAAA,CAAA,MAA0B,QAAtBA,CAAIC,CAAAA,WAAAA,CAAAA,CAAAA,CACC,CACLC,SAAWH,CAAAA,CAAAA,CAAUI,cAAgBJ,CAAUG,CAAAA,SAAAA,CAC/CE,OAASL,CAAAA,CAAAA,CAAUK,SAIhBL,CACT,EAMgB,QAAAM,CAAAA,CAAoBC,CAAAA,CAAAA,CAAuBP,GACzDQ,CAAyBC,CAAAA,GAAAA,CAAIF,CAxB/B,CAAA,SAAyBP,GACvB,MAAOA,CAAAA,CAAAA,SAAAA,CAAAA,UAAAA,CAAAA,CAAa,CAAEG,SAAW,CAAA,EAAA,CAAIE,QAAS,CAAEK,QAAAA,CAAU,CAC5D,CAAA,CAAA,EAsB8CC,CAAgBX,CAC9D,CAAA,CAAA,UAQgBY,CAAAA,CAAaC,CAAAA,CAAAA,CAAaN,EAAuBF,CAC/D,CAAA,CAAA,GAAMS,CAAAA,CAAkBC,CAAAA,CAAAA,CAAwBC,IAAIH,CAGpD,CAAA,CAAA,GAAIC,UAAAA,YAAAA,EAAkBP,CACpB,CAAA,CAAA,MAAOR,CAAAA,EAAoBe,CAAgBP,CAAAA,CAAAA,CAAAA,CAAgBF,CAAQJ,CAAAA,GAAAA,CAAAA,CAIrE,GAAMgB,CAAAA,CAAmBT,CAAAA,CAAAA,CAAyBQ,IAAIT,CACtD,CAAA,CAAA,MAAIU,CAAAA,EACKlB,CAAoBkB,CAAAA,CAAAA,CAAkBZ,EAAQJ,GAIhD,CAAA,CAAA,CACLE,UAAW,EACXE,CAAAA,OAAAA,CAAS,CAAEK,QAAU,CAAA,CAAA,CAAA,CAEzB,kEAvDMF,EAA2B,GAAIU,CAAAA,GAAAA,CAAAA,CAAAA,CAC/BH,EAA0B,GAAII,CAAAA,OAAAA,CAAAA,CAAAA"}
1
+ {"version":3,"file":"animation-registry.cjs.js","sources":["../../../src/utilities/animation-registry.ts"],"sourcesContent":["export interface ElementAnimation {\n keyframes: Keyframe[];\n rtlKeyframes?: Keyframe[];\n options?: KeyframeAnimationOptions;\n}\n\nexport interface ElementAnimationMap {\n [animationName: string]: ElementAnimation;\n}\n\nexport interface GetAnimationOptions {\n /**\n * The component's directionality. When set to \"rtl\", `rtlKeyframes` will be preferred over `keyframes` where\n * available using getAnimation().\n */\n dir: string;\n}\n\nconst defaultAnimationRegistry = new Map<string, ElementAnimation>();\nconst customAnimationRegistry = new WeakMap<Element, ElementAnimationMap>();\n\nfunction ensureAnimation(animation: ElementAnimation | null) {\n return animation ?? { keyframes: [], options: { duration: 0 } };\n}\n\n//\n// Given an ElementAnimation, this function returns a new ElementAnimation where the keyframes property reflects either\n// keyframes or rtlKeyframes depending on the specified directionality.\n//\nfunction getLogicalAnimation(animation: ElementAnimation, dir: string) {\n if (dir.toLowerCase() === 'rtl') {\n return {\n keyframes: animation.rtlKeyframes || animation.keyframes,\n options: animation.options\n };\n }\n\n return animation;\n}\n\n/**\n * Sets a default animation. Components should use the `name.animation` for primary animations and `name.part.animation`\n * for secondary animations, e.g. `dialog.show` and `dialog.overlay.show`. For modifiers, use `drawer.showTop`.\n */\nexport function setDefaultAnimation(animationName: string, animation: ElementAnimation | null) {\n defaultAnimationRegistry.set(animationName, ensureAnimation(animation));\n}\n\n/** Sets a custom animation for the specified element. */\nexport function setAnimation(el: Element, animationName: string, animation: ElementAnimation | null) {\n customAnimationRegistry.set(el, { ...customAnimationRegistry.get(el), [animationName]: ensureAnimation(animation) });\n}\n\n/** Gets an element's animation. Falls back to the default if no animation is found. */\nexport function getAnimation(el: Element, animationName: string, options: GetAnimationOptions) {\n const customAnimation = customAnimationRegistry.get(el);\n\n // Check for a custom animation\n if (customAnimation?.[animationName]) {\n return getLogicalAnimation(customAnimation[animationName], options.dir);\n }\n\n // Check for a default animation\n const defaultAnimation = defaultAnimationRegistry.get(animationName);\n if (defaultAnimation) {\n return getLogicalAnimation(defaultAnimation, options.dir);\n }\n\n // Fall back to an empty animation\n return {\n keyframes: [],\n options: { duration: 0 }\n };\n}\n"],"names":["ensureAnimation","animation","keyframes","options","duration","getLogicalAnimation","dir","toLowerCase","rtlKeyframes","setDefaultAnimation","animationName","defaultAnimationRegistry","set","setAnimation","el","customAnimationRegistry","get","getAnimation","customAnimation","defaultAnimation","Map","WeakMap"],"mappings":"+8CAqBA,QAASA,CAAAA,CAAgBC,CAAAA,CAAAA,CAAAA,CACvB,MAAOA,CAAAA,CAAAA,SAAAA,CAAAA,UAAAA,CAAAA,CAAa,CAAEC,SAAW,CAAA,EAAA,CAAIC,OAAS,CAAA,CAAEC,QAAU,CAAA,CAAA,CAAA,CAC5D,EAMA,QAASC,CAAAA,CAAAA,CAAoBJ,CAA6BK,CAAAA,CAAAA,CAAAA,CACxD,MAA0B,KAAA,GAAtBA,EAAIC,WACC,CAAA,CAAA,CAAA,CACLL,UAAWD,CAAUO,CAAAA,YAAAA,EAAgBP,EAAUC,SAC/CC,CAAAA,OAAAA,CAASF,CAAUE,CAAAA,OAAAA,CAAAA,CAIhBF,CACT,EAMgB,QAAAQ,CAAAA,CAAoBC,CAAAA,CAAAA,CAAuBT,CACzDU,CAAAA,CAAAA,CAAAA,CAAyBC,GAAIF,CAAAA,CAAAA,CAAeV,EAAgBC,CAC9D,CAAA,CAAA,UAGgBY,CAAAA,CAAAA,CAAaC,CAAaJ,CAAAA,CAAAA,CAAuBT,GAC/Dc,CAAwBH,CAAAA,GAAAA,CAAIE,iCAASC,CAAAA,CAAwBC,IAAIF,CAAKJ,CAAAA,KAAAA,eAAAA,IAACA,CAAgBV,CAAAA,CAAAA,CAAgBC,CACzG,CAAA,EAAA,CAAA,UAGgBgB,CAAAA,CAAaH,CAAAA,CAAAA,CAAaJ,CAAuBP,CAAAA,CAAAA,CAAAA,CAC/D,GAAMe,CAAAA,CAAAA,CAAkBH,EAAwBC,GAAIF,CAAAA,CAAAA,CAAAA,CAGpD,GAAII,CAAAA,SAAAA,CAAAA,WAAAA,CAAAA,CAAkBR,CACpB,CAAA,CAAA,MAAOL,CAAAA,EAAoBa,CAAgBR,CAAAA,CAAAA,CAAAA,CAAgBP,EAAQG,GAIrE,CAAA,CAAA,GAAMa,CAAAA,EAAmBR,CAAyBK,CAAAA,GAAAA,CAAIN,CACtD,CAAA,CAAA,MAAIS,CAAAA,CACKd,CAAAA,CAAAA,CAAoBc,EAAkBhB,CAAQG,CAAAA,GAAAA,CAAAA,CAIhD,CACLJ,SAAAA,CAAW,EACXC,CAAAA,OAAAA,CAAS,CAAEC,QAAU,CAAA,CAAA,CAAA,CAEzB,sEAvDMO,CAAAA,CAA2B,GAAIS,CAAAA,GAC/BL,CAAAA,CAAAA,CAAAA,CAAAA,CAA0B,GAAIM,CAAAA,OAEpC,CAAA,CAAA"}
@@ -1 +1 @@
1
- const n=new Map,t=new WeakMap;function o(n,t){return"rtl"===t.toLowerCase()?{keyframes:n.rtlKeyframes||n.keyframes,options:n.options}:n}function r(t,o){n.set(t,function(n){return n??{keyframes:[],options:{duration:0}}}(o))}function e(r,e,s){const a=t.get(r);if(a?.[e])return o(a[e],s.dir);const i=n.get(e);return i?o(i,s.dir):{keyframes:[],options:{duration:0}}}export{e as g,r as s};
1
+ const n=new Map,t=new WeakMap;function o(n){return n??{keyframes:[],options:{duration:0}}}function r(n,t){return"rtl"===t.toLowerCase()?{keyframes:n.rtlKeyframes||n.keyframes,options:n.options}:n}function e(t,r){n.set(t,o(r))}function s(n,r,e){t.set(n,{...t.get(n),[r]:o(e)})}function a(o,e,s){const a=t.get(o);if(a?.[e])return r(a[e],s.dir);const i=n.get(e);return i?r(i,s.dir):{keyframes:[],options:{duration:0}}}export{s as a,a as g,e as s};
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Webcomponent nile-elements following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "nile-elements",
6
- "version": "2.0.18",
6
+ "version": "2.0.20",
7
7
  "main": "dist/src/index.js",
8
8
  "type": "module",
9
9
  "module": "dist/src/index.js",
@@ -134,6 +134,7 @@
134
134
  "./nile-color-swatch": "./dist/src/nile-color-swatch/index.js",
135
135
  "./nile-tree": "./dist/src/nile-tree/index.js",
136
136
  "./nile-tree-item": "./dist/src/nile-tree-item/index.js",
137
+ "./nile-date-time-picker": "./dist/src/nile-date-time-picker/index.js",
137
138
  "./nile-wysiwyg-editor": "./dist/src/nile-wysiwyg-editor/index.js",
138
139
  "./nile-wysiwyg-toolbar": "./dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js",
139
140
  "./nile-app-shell": "./dist/src/nile-app-shell/index.js",
@@ -197,6 +198,7 @@
197
198
  "tippy.js": "^6.3.7"
198
199
  },
199
200
  "devDependencies": {
201
+ "@aquera/nile-glyph": "^1.0.9",
200
202
  "@custom-elements-manifest/analyzer": "^0.4.17",
201
203
  "@open-wc/eslint-config": "^8.0.2",
202
204
  "@open-wc/testing": "^3.1.6",
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ // Builds coverage.json (consumed by the ui-playground Metrics > Testing tab) from the
3
+ // wtr lcov report, the Istanbul coverage-final.json (statements) and the test runner output.
4
+ // Usage (from a package dir): node scripts/coverage-report.mjs <lcov.info> <test-output.txt> <out.json>
5
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
6
+ import { execSync } from 'node:child_process';
7
+ import { dirname, join } from 'node:path';
8
+
9
+ const [lcovPath = 'coverage/lcov.info', outputPath, outPath = 'coverage/coverage.json'] = process.argv.slice(2);
10
+ // Run from the package directory (cwd), so this script can be shared by other packages.
11
+ const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
12
+
13
+ const ratio = (covered, total) => ({ covered, total, pct: total ? Math.round((covered / total) * 10000) / 100 : 100 });
14
+ // sMissing counts files without statement data, so a group's statements become null instead of a false 100%.
15
+ const blank = () => ({ files: 0, sMissing: 0, SH: 0, SF: 0, LH: 0, LF: 0, BRH: 0, BRF: 0, FNH: 0, FNF: 0 });
16
+ const relPath = p => p.replace(/\\/g, '/').replace(/^.*?(?:^|\/)src\//, '');
17
+
18
+ // Statements: lcov has no statement metric, so read them from Istanbul's coverage-final.json.
19
+ const statements = new Map();
20
+ const finalPath = join(dirname(lcovPath), 'coverage-final.json');
21
+ try {
22
+ const final = JSON.parse(readFileSync(finalPath, 'utf8'));
23
+ for (const [file, data] of Object.entries(final)) {
24
+ const counts = Object.values(data.s ?? {});
25
+ statements.set(relPath(file), { SH: counts.filter(c => c > 0).length, SF: counts.length });
26
+ }
27
+ } catch (e) {
28
+ statements.clear();
29
+ console.warn(`statements unavailable: cannot read ${finalPath} (${e.code ?? e.message})`);
30
+ }
31
+
32
+ const files = new Map();
33
+ let current = null;
34
+ for (const line of readFileSync(lcovPath, 'utf8').split('\n')) {
35
+ const [key, value] = [line.slice(0, line.indexOf(':')), line.slice(line.indexOf(':') + 1)];
36
+ if (key === 'SF') {
37
+ const rel = relPath(value);
38
+ const st = statements.get(rel);
39
+ current = { ...blank(), ...(st ?? { sMissing: 1 }), files: 1 };
40
+ files.set(rel, current);
41
+ } else if (current && key in current && key !== 'files') {
42
+ current[key] += Number(value) || 0;
43
+ } else if (line === 'end_of_record') {
44
+ current = null;
45
+ }
46
+ }
47
+
48
+ const metrics = g => ({
49
+ statements: g.sMissing ? null : ratio(g.SH, g.SF),
50
+ lines: ratio(g.LH, g.LF),
51
+ branches: ratio(g.BRH, g.BRF),
52
+ functions: ratio(g.FNH, g.FNF),
53
+ });
54
+
55
+ const groups = new Map();
56
+ const sum = blank();
57
+ const fileRows = [...files].map(([name, g]) => {
58
+ // Only real component folders (src/nile-*/) get a component row; shared code (internal, utilities, slot,
59
+ // version, index) is still in totals and files.
60
+ const group = name.split('/')[0];
61
+ const isComponent = name.includes('/') && group.startsWith('nile-');
62
+ if (isComponent && !groups.has(group)) groups.set(group, blank());
63
+ for (const k of Object.keys(sum)) {
64
+ if (isComponent) groups.get(group)[k] += g[k];
65
+ sum[k] += g[k];
66
+ }
67
+ return { name, ...metrics(g) };
68
+ }).sort((a, b) => a.name.localeCompare(b.name));
69
+
70
+ const components = [...groups].map(([name, g]) => ({ name, files: g.files, ...metrics(g) }))
71
+ .sort((a, b) => a.name.localeCompare(b.name));
72
+
73
+ // null when the counts cannot be parsed, so a broken run is not reported as "0 tests".
74
+ let tests = null;
75
+ if (outputPath && existsSync(outputPath)) {
76
+ const text = readFileSync(outputPath, 'utf8').replace(/\x1B\[[0-9;]*[mK]/g, '');
77
+ const summary = text.split('\n').filter(l => /\d+\s+passed.*\d+\s+failed/.test(l)).pop();
78
+ const passed = summary?.match(/(\d+)\s+passed/)?.[1];
79
+ const failed = summary?.match(/(\d+)\s+failed/)?.[1];
80
+ if (passed !== undefined && failed !== undefined) tests = { passed: Number(passed), failed: Number(failed), total: Number(passed) + Number(failed) };
81
+ }
82
+
83
+ const git = cmd => { try { return execSync(cmd, { encoding: 'utf8' }).trim(); } catch { return ''; } };
84
+ const report = {
85
+ package: pkg.name,
86
+ version: pkg.version,
87
+ branch: process.env.BITBUCKET_BRANCH || git('git rev-parse --abbrev-ref HEAD'),
88
+ commit: (process.env.BITBUCKET_COMMIT || git('git rev-parse HEAD')).slice(0, 7),
89
+ generatedAt: new Date().toISOString(),
90
+ tests,
91
+ totals: { files: sum.files, ...metrics(sum) },
92
+ components,
93
+ files: fileRows,
94
+ };
95
+ writeFileSync(outPath, JSON.stringify(report, null, 2) + '\n');
96
+ console.log(`coverage.json: ${fileRows.length} files, ${components.length} components, ${report.totals.lines.pct}% lines, ${report.totals.statements ? `${report.totals.statements.pct}%` : 'n/a'} statements, ${tests?.total ?? 'n/a'} tests -> ${outPath}`);
package/src/index.ts CHANGED
@@ -154,6 +154,7 @@ export { NileInlineSidebarItemHeader } from './nile-inline-sidebar-item-header';
154
154
  export { NileInlineSidebarItemBody } from './nile-inline-sidebar-item-body';
155
155
  export { NileMarkdown } from './nile-markdown';
156
156
  export { NileMarkdownEditor } from './nile-markdown-editor';
157
+ export { NileDateTimePicker } from './nile-date-time-picker';
157
158
 
158
159
  // nile-wysiwyg-editor — ProseMirror-powered rich text editor
159
160
  export {
@@ -27,7 +27,24 @@ export const styles = css`
27
27
  height: var(--nile-height-22px, var(--ng-height-24px));
28
28
  }
29
29
 
30
- .badge__content {
30
+ .badge__prefix,
31
+ .badge__suffix {
32
+ display: inline-flex;
33
+ flex: 0 0 auto;
34
+ align-items: center;
35
+ }
36
+
37
+ .badge--has-prefix .badge__prefix {
38
+ margin-inline-end: var(--nile-spacing-xs, var(--ng-spacing-xs));
39
+ }
40
+
41
+ .badge--has-suffix .badge__suffix {
42
+ margin-inline-start: var(--nile-spacing-xs, var(--ng-spacing-xs));
43
+ }
44
+
45
+ .badge__prefix,
46
+ .badge__content,
47
+ .badge__suffix {
31
48
  font-family: var(--nile-font-family-serif, var(--ng-font-family-body));
32
49
  font-size: var(--nile-type-scale-2, var(--ng-font-size-text-sm));
33
50
  font-style: normal;
@@ -209,7 +226,9 @@ export const styles = css`
209
226
  height: var(--nile-height-22px, var(--ng-height-22px));
210
227
  }
211
228
 
212
- .badge--sm .badge__content {
229
+ .badge--sm .badge__prefix,
230
+ .badge--sm .badge__content,
231
+ .badge--sm .badge__suffix {
213
232
  font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
214
233
  line-height: var(--nile-type-scale-5, var(--ng-line-height-text-xs));
215
234
  }
@@ -219,7 +238,9 @@ export const styles = css`
219
238
  height: var(--nile-height-28px, var(--ng-height-28px));
220
239
  }
221
240
 
222
- .badge--lg .badge__content {
241
+ .badge--lg .badge__prefix,
242
+ .badge--lg .badge__content,
243
+ .badge--lg .badge__suffix {
223
244
  font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));
224
245
  line-height: var(--nile-type-scale-5, var(--ng-line-height-text-sm));
225
246
  }
@@ -744,4 +744,106 @@ describe('NileBadge', () => {
744
744
  });
745
745
  });
746
746
  });
747
+
748
+ describe('prefix and suffix slots', () => {
749
+ const base = (el: NileBadge) => el.shadowRoot!.querySelector('[part="base"]')!;
750
+ const slotOf = (el: NileBadge, name: string) =>
751
+ el.shadowRoot!.querySelector<HTMLSlotElement>(`slot[name="${name}"]`)!;
752
+
753
+ it('110. should render prefix and suffix slots with parts', async () => {
754
+ const el = await fixture<NileBadge>(html`<nile-badge>Label</nile-badge>`);
755
+ expect(slotOf(el, 'prefix').getAttribute('part')).to.equal('prefix');
756
+ expect(slotOf(el, 'suffix').getAttribute('part')).to.equal('suffix');
757
+ });
758
+
759
+ it('111. should render prefix before content and suffix after it', async () => {
760
+ const el = await fixture<NileBadge>(html`<nile-badge>Label</nile-badge>`);
761
+ const parts = [...base(el).children].map(n => n.getAttribute('part'));
762
+ expect(parts).to.deep.equal(['prefix', 'content', 'suffix']);
763
+ });
764
+
765
+ it('112. should assign prefix content to the prefix slot', async () => {
766
+ const el = await fixture<NileBadge>(html`<nile-badge><span slot="prefix" id="p">●</span>Label</nile-badge>`);
767
+ const assigned = slotOf(el, 'prefix').assignedElements();
768
+ expect(assigned).to.have.length(1);
769
+ expect(assigned[0].id).to.equal('p');
770
+ });
771
+
772
+ it('113. should assign suffix content to the suffix slot', async () => {
773
+ const el = await fixture<NileBadge>(html`<nile-badge>Label<span slot="suffix" id="s">3</span></nile-badge>`);
774
+ const assigned = slotOf(el, 'suffix').assignedElements();
775
+ expect(assigned).to.have.length(1);
776
+ expect(assigned[0].id).to.equal('s');
777
+ });
778
+
779
+ it('114. should accept any element in the slots', async () => {
780
+ const el = await fixture<NileBadge>(html`
781
+ <nile-badge>
782
+ <img slot="prefix" alt="" />
783
+ Label
784
+ <button slot="suffix">x</button>
785
+ </nile-badge>
786
+ `);
787
+ expect(slotOf(el, 'prefix').assignedElements()[0].tagName).to.equal('IMG');
788
+ expect(slotOf(el, 'suffix').assignedElements()[0].tagName).to.equal('BUTTON');
789
+ });
790
+
791
+ it('115. should not add has-prefix or has-suffix classes when slots are empty', async () => {
792
+ const el = await fixture<NileBadge>(html`<nile-badge>Label</nile-badge>`);
793
+ expect(base(el).classList.contains('badge--has-prefix')).to.be.false;
794
+ expect(base(el).classList.contains('badge--has-suffix')).to.be.false;
795
+ });
796
+
797
+ it('116. should add has-prefix and has-suffix classes when slots are filled', async () => {
798
+ const el = await fixture<NileBadge>(html`
799
+ <nile-badge><span slot="prefix">a</span>Label<span slot="suffix">b</span></nile-badge>
800
+ `);
801
+ expect(base(el).classList.contains('badge--has-prefix')).to.be.true;
802
+ expect(base(el).classList.contains('badge--has-suffix')).to.be.true;
803
+ });
804
+
805
+ it('117. should update classes when slot content is added later', async () => {
806
+ const el = await fixture<NileBadge>(html`<nile-badge>Label</nile-badge>`);
807
+ const icon = document.createElement('span');
808
+ icon.slot = 'prefix';
809
+ el.prepend(icon);
810
+ await elementUpdated(el);
811
+ await el.updateComplete;
812
+ expect(base(el).classList.contains('badge--has-prefix')).to.be.true;
813
+ });
814
+
815
+ it('118. should take no space when prefix and suffix are empty', async () => {
816
+ const el = await fixture<NileBadge>(html`<nile-badge>Label</nile-badge>`);
817
+ expect(slotOf(el, 'prefix').getBoundingClientRect().width).to.equal(0);
818
+ expect(slotOf(el, 'suffix').getBoundingClientRect().width).to.equal(0);
819
+ });
820
+
821
+ it('119. should space filled slots from the content with the spacing token', () => {
822
+ const css = styles.cssText;
823
+ const rule = (selector: string) => css.slice(css.indexOf(`${selector} {`)).split('}')[0];
824
+ const token = 'var(--nile-spacing-xs, var(--ng-spacing-xs))';
825
+ expect(rule('.badge--has-prefix .badge__prefix')).to.contain(`margin-inline-end: ${token}`);
826
+ expect(rule('.badge--has-suffix .badge__suffix')).to.contain(`margin-inline-start: ${token}`);
827
+ });
828
+
829
+ it('120. should keep slot content out of the aria-label', async () => {
830
+ const el = await fixture<NileBadge>(html`
831
+ <nile-badge><span slot="prefix">icon</span>Active<span slot="suffix">12</span></nile-badge>
832
+ `);
833
+ expect(el.getAttribute('aria-label')).to.equal('Active');
834
+ });
835
+ it('121. should ignore comment nodes in the aria-label', async () => {
836
+ const el = document.createElement('nile-badge') as NileBadge;
837
+ el.append(document.createComment('?lit$123$'), 'Active', document.createComment('ng-container'));
838
+ document.body.append(el);
839
+ await el.updateComplete;
840
+ expect(el.getAttribute('aria-label')).to.equal('Active');
841
+ el.remove();
842
+ });
843
+
844
+ it('122. should keep the existing label for badges without slots', async () => {
845
+ const el = await fixture<NileBadge>(html`<nile-badge>New <b>beta</b></nile-badge>`);
846
+ expect(el.getAttribute('aria-label')).to.equal('New beta');
847
+ });
848
+ });
747
849
  });
@@ -15,6 +15,7 @@ import { styles } from './nile-badge.css';
15
15
  import '../nile-icon-button/nile-icon-button';
16
16
  import { classMap } from 'lit/directives/class-map.js';
17
17
  import { customElement, property } from 'lit/decorators.js';
18
+ import { HasSlotController } from '../internal/slot';
18
19
  import NileElement, { setupA11yMetadata } from '../internal/nile-element';
19
20
  import type { CSSResultGroup } from 'lit';
20
21
  import { A11yProperty } from '../internal/accessibility/a11y.property.enum';
@@ -35,27 +36,42 @@ import { A11yCustomValue } from '../internal/accessibility/a11y.custom-value.enu
35
36
  * @dependency nile-icon-button
36
37
  *
37
38
  * @slot - The badge's content.
38
- *
39
- * @event nile-remove - Emitted when the remove button is activated.
39
+ * @slot prefix - Content placed before the badge's content, such as an icon, dot or avatar.
40
+ * @slot suffix - Content placed after the badge's content, such as an icon, count or button.
40
41
  *
41
42
  * @csspart base - The component's base wrapper.
43
+ * @csspart prefix - The container that wraps the prefix slot.
42
44
  * @csspart content - The badge's content.
43
- * @csspart remove-button - The badge's remove button, an `<nile-icon-button>`.
44
- * @csspart remove-button__base - The remove button's exported `base` part.
45
+ * @csspart suffix - The container that wraps the suffix slot.
45
46
  */
46
47
 
47
48
  @customElement('nile-badge')
48
49
  export class NileBadge extends NileElement {
49
50
  static styles: CSSResultGroup = styles;
50
51
 
52
+ private readonly hasSlotController = new HasSlotController(this, 'prefix', 'suffix');
53
+
51
54
  connectedCallback(): void {
52
55
  super.connectedCallback();
53
56
  setupA11yMetadata(this, {
54
57
  [Role.Role]: A11yRole.Text,
55
- [A11yProperty.AriaLabel]: this.textContent?.trim() || A11yCustomValue.Badge,
58
+ [A11yProperty.AriaLabel]: this.getLabelText() || A11yCustomValue.Badge,
56
59
  });
57
60
  }
58
61
 
62
+ /** Text of the default slot only, so prefix and suffix content stays out of the label. */
63
+ private getLabelText(): string {
64
+ return [...this.childNodes]
65
+ .filter(
66
+ node =>
67
+ node.nodeType === Node.TEXT_NODE ||
68
+ (node.nodeType === Node.ELEMENT_NODE && !(node as Element).hasAttribute('slot'))
69
+ )
70
+ .map(node => node.textContent ?? '')
71
+ .join('')
72
+ .trim();
73
+ }
74
+
59
75
  /** The badge's theme variant. */
60
76
  @property({ reflect: true }) variant:
61
77
  | 'primary'
@@ -88,6 +104,9 @@ export class NileBadge extends NileElement {
88
104
  @property({ reflect: true }) size: 'sm' | 'md' | 'lg' = 'md';
89
105
 
90
106
  render() {
107
+ const hasPrefix = this.hasSlotController.test('prefix');
108
+ const hasSuffix = this.hasSlotController.test('suffix');
109
+
91
110
  return html`
92
111
  <span
93
112
  part="base"
@@ -117,9 +136,13 @@ export class NileBadge extends NileElement {
117
136
  'badge--sm': this.size === 'sm',
118
137
  'badge--md': this.size === 'md',
119
138
  'badge--lg': this.size === 'lg',
139
+ 'badge--has-prefix': hasPrefix,
140
+ 'badge--has-suffix': hasSuffix,
120
141
  })}
121
142
  >
143
+ <slot name="prefix" part="prefix" class="badge__prefix"></slot>
122
144
  <slot part="content" class="badge__content"></slot>
145
+ <slot name="suffix" part="suffix" class="badge__suffix"></slot>
123
146
  </span>
124
147
  `;
125
148
  }
@@ -104,4 +104,61 @@ describe('NileBreadcrumb', () => {
104
104
  it('98. slot is default (no name)', async () => { const el = await fixture<NileBreadcrumb>(html`<nile-breadcrumb><nile-breadcrumb-item>A</nile-breadcrumb-item></nile-breadcrumb>`); const slot = el.shadowRoot!.querySelector('slot'); expect(slot!.hasAttribute('name')).to.be.false; });
105
105
  it('99. no nile-divider', async () => { const el = await fixture<NileBreadcrumb>(html`<nile-breadcrumb><nile-breadcrumb-item>A</nile-breadcrumb-item></nile-breadcrumb>`); expect(el.shadowRoot!.querySelector('nile-divider')).to.be.null; });
106
106
  it('100. full integration', async () => { const el = await fixture<NileBreadcrumb>(html`<nile-breadcrumb class="b" id="b1" role="navigation" aria-label="Breadcrumb"><nile-breadcrumb-item>Home</nile-breadcrumb-item><nile-breadcrumb-item>Settings</nile-breadcrumb-item><nile-breadcrumb-item>Profile</nile-breadcrumb-item></nile-breadcrumb>`); expect(el.id).to.equal('b1'); expect(el.getAttribute('role')).to.equal('navigation'); expect(el.querySelectorAll('nile-breadcrumb-item').length).to.equal(3); expect(el.textContent).to.contain('Home'); expect(el.textContent).to.contain('Profile'); });
107
+
108
+ describe('variant', () => {
109
+ const tpl = (variant?: string) => variant
110
+ ? html`<nile-breadcrumb variant=${variant}><nile-breadcrumb-item>A</nile-breadcrumb-item><nile-breadcrumb-item>B</nile-breadcrumb-item></nile-breadcrumb>`
111
+ : html`<nile-breadcrumb><nile-breadcrumb-item>A</nile-breadcrumb-item><nile-breadcrumb-item>B</nile-breadcrumb-item></nile-breadcrumb>`;
112
+ const items = (el: NileBreadcrumb) => [...el.querySelectorAll('nile-breadcrumb-item')];
113
+
114
+ it('defaults to "default" and reflects', async () => {
115
+ const el = await fixture<NileBreadcrumb>(tpl());
116
+ expect(el.variant).to.equal('default');
117
+ expect(el.getAttribute('variant')).to.equal('default');
118
+ items(el).forEach(i => expect(i.variant).to.equal('default'));
119
+ });
120
+
121
+ it('propagates "outlined" to every item', async () => {
122
+ const el = await fixture<NileBreadcrumb>(tpl('outlined'));
123
+ await Promise.all(items(el).map(i => i.updateComplete));
124
+ items(el).forEach(i => {
125
+ expect(i.getAttribute('variant')).to.equal('outlined');
126
+ expect(i.shadowRoot!.querySelector('[part="item"]')!.classList.contains('nile-breadcrumb-item--outlined')).to.be.true;
127
+ });
128
+ });
129
+
130
+ it('keeps the existing text-colour classes when outlined', async () => {
131
+ const el = await fixture<NileBreadcrumb>(tpl('outlined'));
132
+ const [a, b] = items(el);
133
+ await a.updateComplete; await b.updateComplete;
134
+ expect(a.shadowRoot!.querySelector('[part="item"]')!.classList.contains('nile-breadcrumb-item__slot-text')).to.be.true;
135
+ expect(b.shadowRoot!.querySelector('[part="item"]')!.classList.contains('nile-breadcrumb-item__last-slot-text')).to.be.true;
136
+ });
137
+
138
+ it('updates items when variant changes at runtime', async () => {
139
+ const el = await fixture<NileBreadcrumb>(tpl());
140
+ el.variant = 'outlined';
141
+ await el.updateComplete;
142
+ items(el).forEach(i => expect(i.variant).to.equal('outlined'));
143
+ el.variant = 'default';
144
+ await el.updateComplete;
145
+ items(el).forEach(i => expect(i.variant).to.equal('default'));
146
+ });
147
+
148
+ it('applies the variant to items added later', async () => {
149
+ const el = await fixture<NileBreadcrumb>(tpl('outlined'));
150
+ const added = document.createElement('nile-breadcrumb-item');
151
+ added.textContent = 'C';
152
+ el.appendChild(added);
153
+ await new Promise(r => setTimeout(r));
154
+ expect(added.variant).to.equal('outlined');
155
+ });
156
+
157
+ it('no chip class in the default variant', async () => {
158
+ const el = await fixture<NileBreadcrumb>(tpl());
159
+ const [a] = items(el);
160
+ await a.updateComplete;
161
+ expect(a.shadowRoot!.querySelector('.nile-breadcrumb-item--outlined')).to.be.null;
162
+ });
163
+ });
107
164
  });
@@ -31,6 +31,12 @@ export class NileBreadcrumb extends NileElement {
31
31
  return [styles];
32
32
  }
33
33
 
34
+ /**
35
+ * Visual style of the items. `outlined` renders each item inside an outlined chip;
36
+ * text colours are unchanged. Propagated to every slotted `nile-breadcrumb-item`.
37
+ */
38
+ @property({ type: String, reflect: true }) variant: 'default' | 'outlined' = 'default';
39
+
34
40
  /* #endregion */
35
41
 
36
42
  /* #region Methods */
@@ -46,7 +52,20 @@ export class NileBreadcrumb extends NileElement {
46
52
  });
47
53
  }
48
54
 
55
+ protected updated(changedProperties: Map<string, unknown>) {
56
+ if (changedProperties.has('variant')) {
57
+ this.syncItemVariant();
58
+ }
59
+ }
60
+
61
+ private syncItemVariant() {
62
+ this.querySelectorAll('nile-breadcrumb-item').forEach(item => {
63
+ item.variant = this.variant;
64
+ });
65
+ }
66
+
49
67
  private changeLastItem() {
68
+ this.syncItemVariant();
50
69
  const items: any = [...this.querySelectorAll('nile-breadcrumb-item')];
51
70
  const itemslength = items.length;
52
71
  for (let i = 0; i < itemslength - 1; i++) {
@@ -46,6 +46,18 @@ export const styles = css`
46
46
  .nile-breadcrumb-item__last-slot-text {
47
47
  color: var(--nile-breadcrumb-color, var(--ng-colors-text-brand-secondary-700));
48
48
  }
49
+
50
+ .nile-breadcrumb-item--outlined {
51
+ padding: var(--nile-spacing-xs, var(--ng-spacing-xs)) var(--nile-spacing-md, var(--ng-spacing-md));
52
+ border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
53
+ border-radius: var(--nile-radius-sm, var(--ng-radius-md));
54
+ background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary));
55
+ }
56
+
57
+ .nile-breadcrumb-item--outlined.nile-breadcrumb-item__slot-text:hover,
58
+ .nile-breadcrumb-item--outlined.nile-breadcrumb-item__last-slot-text {
59
+ background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));
60
+ }
49
61
  `;
50
62
 
51
63
  export default [styles];
@@ -44,6 +44,12 @@ export class NileBreadcrumbItem extends NileElement {
44
44
 
45
45
  @property({ type: String }) separator = 'var(--nile-icon-arrow-right, var(--ng-icon-chevron-right))';
46
46
 
47
+ /**
48
+ * Visual style of the item. `outlined` wraps the text in an outlined chip.
49
+ * Set automatically from the parent `nile-breadcrumb`'s `variant`.
50
+ */
51
+ @property({ type: String, reflect: true }) variant: 'default' | 'outlined' = 'default';
52
+
47
53
  private handleClick() {
48
54
  if (this.isLast) {
49
55
  return;
@@ -57,6 +63,7 @@ export class NileBreadcrumbItem extends NileElement {
57
63
  class=${classMap({
58
64
  'nile-breadcrumb-item__slot-text': !this.isLast,
59
65
  'nile-breadcrumb-item__last-slot-text': this.isLast,
66
+ 'nile-breadcrumb-item--outlined': this.variant === 'outlined',
60
67
  })}
61
68
  @click=${this.handleClick}
62
69
  >