@descope/web-components-ui 3.1.5 → 3.1.6

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.
@@ -17717,7 +17717,7 @@ const getValueType = (value) => {
17717
17717
  };
17718
17718
 
17719
17719
  const renderCodeSnippet = (value, lang) =>
17720
- `<descope-code-snippet lang="${lang}" class="row-details__value code">${escapeXML(value)}</descope-code-snippet>`;
17720
+ `<descope-code-snippet copy-button="true" lang="${lang}" class="row-details__value code">${escapeXML(value)}</descope-code-snippet>`;
17721
17721
 
17722
17722
  const renderText = (text) =>
17723
17723
  `<div class="row-details__value text" title="${text}">${escapeXML(text)}</div>`;
@@ -17745,16 +17745,31 @@ const defaultRowDetailsValueRenderer = (value) => {
17745
17745
  return renderText(value);
17746
17746
  };
17747
17747
 
17748
+ const isCodeSnippetValue = (value) => {
17749
+ const type = getValueType(value);
17750
+ return (
17751
+ type === 'object' ||
17752
+ type === 'xml' ||
17753
+ (type === 'array' && value.some((v) => getValueType(v) === 'object'))
17754
+ );
17755
+ };
17756
+
17748
17757
  const defaultRowDetailsRenderer = (item, itemLabelsMapping) => `
17749
17758
  <div class="row-details">
17750
17759
  ${Object.entries(item)
17751
- .map(
17752
- ([key, value]) =>
17753
- `<div class="row-details__item" >
17754
- <div class="row-details__label">${itemLabelsMapping[key] || toTitle(key)}</div>
17760
+ .map(([key, value]) => {
17761
+ const label = itemLabelsMapping[key] || toTitle(key);
17762
+ if (isCodeSnippetValue(value)) {
17763
+ return `<details class="row-details__item">
17764
+ <summary class="row-details__label">${label}</summary>
17755
17765
  ${defaultRowDetailsValueRenderer(value)}
17756
- </div>`
17757
- )
17766
+ </details>`;
17767
+ }
17768
+ return `<div class="row-details__item">
17769
+ <div class="row-details__label">${label}</div>
17770
+ ${defaultRowDetailsValueRenderer(value)}
17771
+ </div>`;
17772
+ })
17758
17773
  .join('\n')}
17759
17774
  </div>
17760
17775
  `;
@@ -17782,6 +17797,18 @@ const GridMixin = (superclass) =>
17782
17797
  };
17783
17798
 
17784
17799
  this.baseElement.rowDetailsRenderer = this.#rowDetailsRenderer.bind(this);
17800
+
17801
+ // Stop wheel events from propagating to vaadin-grid when scrolling
17802
+ // inside code snippets, so touchpad horizontal scroll works
17803
+ this.baseElement.addEventListener(
17804
+ 'wheel',
17805
+ (e) => {
17806
+ if (e.target.closest('descope-code-snippet')) {
17807
+ e.stopPropagation();
17808
+ }
17809
+ },
17810
+ true
17811
+ );
17785
17812
  }
17786
17813
 
17787
17814
  // this renders the details panel content
@@ -18092,6 +18119,14 @@ const GridClass = compose$1(
18092
18119
  grid-column: 1 / -1;
18093
18120
  order: 2;
18094
18121
  }
18122
+ vaadin-grid details.row-details__item {
18123
+ padding: 0;
18124
+ }
18125
+ vaadin-grid details.row-details__item > summary.row-details__label {
18126
+ cursor: pointer;
18127
+ list-style: revert;
18128
+ display: revert;
18129
+ }
18095
18130
  vaadin-grid .row-details__value.text {
18096
18131
  overflow: hidden;
18097
18132
  text-overflow: ellipsis;
@@ -18099,8 +18134,7 @@ const GridClass = compose$1(
18099
18134
  }
18100
18135
  vaadin-grid .row-details__value.code {
18101
18136
  margin-top: 5px;
18102
- max-height: 120px;
18103
- overflow: scroll;
18137
+ overflow-x: auto;
18104
18138
  font-size: 0.85em;
18105
18139
  }
18106
18140
  vaadin-grid vaadin-icon.toggle-details-button {
@@ -20031,18 +20065,24 @@ const decode = (input) => {
20031
20065
 
20032
20066
  const tpl = (input, inline) => (inline ? input : `<pre>${input}</pre>`);
20033
20067
 
20068
+ var copyIconSrc = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogIDxyZWN0IHg9IjkiIHk9IjkiIHdpZHRoPSIxMyIgaGVpZ2h0PSIxMyIgcng9IjIiIHJ5PSIyIj48L3JlY3Q+CiAgPHBhdGggZD0iTTUgMTVINGEyIDIgMCAwIDEtMi0yVjRhMiAyIDAgMCAxIDItMmg5YTIgMiAwIDAgMSAyIDJ2MSI+PC9wYXRoPgo8L3N2Zz4K";
20069
+
20070
+ var checkIconSrc = "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iY2hlY2staWNvbiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KICA8cG9seWxpbmUgcG9pbnRzPSIyMCA2IDkgMTcgNCAxMiI+PC9wb2x5bGluZT4KPC9zdmc+Cg==";
20071
+
20034
20072
  const componentName$e = getComponentName$1('code-snippet');
20035
20073
 
20036
- let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: componentName$e, baseSelector: ':host > code' }) {
20074
+ let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: componentName$e, baseSelector: ':host > .wrapper' }) {
20037
20075
  static get observedAttributes() {
20038
- return ['lang', 'inline'];
20076
+ return ['lang', 'inline', 'copy-button'];
20039
20077
  }
20040
20078
 
20041
20079
  constructor() {
20042
20080
  super();
20043
20081
 
20044
20082
  this.attachShadow({ mode: 'open' }).innerHTML = `
20045
- <code class="hljs"></code>
20083
+ <div class="wrapper">
20084
+ <code class="hljs"></code>
20085
+ </div>
20046
20086
  `;
20047
20087
 
20048
20088
  injectStyle(
@@ -20051,6 +20091,10 @@ let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: c
20051
20091
  display: inline-block;
20052
20092
  width: 100%;
20053
20093
  }
20094
+ .wrapper {
20095
+ position: relative;
20096
+ width: 100%;
20097
+ }
20054
20098
  code {
20055
20099
  display: block;
20056
20100
  width: 100%;
@@ -20061,6 +20105,38 @@ let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: c
20061
20105
  pre {
20062
20106
  margin: 0;
20063
20107
  }
20108
+ .copy-btn {
20109
+ position: absolute;
20110
+ top: 8px;
20111
+ right: 8px;
20112
+ display: flex;
20113
+ align-items: center;
20114
+ justify-content: center;
20115
+ padding: 0;
20116
+ border-style: solid;
20117
+ cursor: pointer;
20118
+ opacity: 0;
20119
+ transition: opacity 150ms ease, background 150ms ease;
20120
+ }
20121
+ .wrapper:hover .copy-btn,
20122
+ .copy-btn:focus-visible {
20123
+ opacity: 1;
20124
+ }
20125
+ .copy-btn descope-icon {
20126
+ width: 16px;
20127
+ height: 16px;
20128
+ pointer-events: none;
20129
+ flex-shrink: 0;
20130
+ }
20131
+ .copy-btn .check-icon {
20132
+ display: none;
20133
+ }
20134
+ .copy-btn.copied .check-icon {
20135
+ display: block;
20136
+ }
20137
+ .copy-btn.copied descope-icon:not(.check-icon) {
20138
+ display: none;
20139
+ }
20064
20140
  `,
20065
20141
  this
20066
20142
  );
@@ -20072,11 +20148,15 @@ let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: c
20072
20148
  this.lang = this.getAttribute('lang');
20073
20149
  this.isInline = this.getAttribute('inline') === 'true';
20074
20150
 
20151
+ if (this.getAttribute('copy-button') === 'true') {
20152
+ this.#initCopyButton();
20153
+ }
20154
+
20075
20155
  observeChildren$1(this, this.#renderSnippet.bind(this));
20076
20156
  }
20077
20157
 
20078
20158
  get contentNode() {
20079
- return this.shadowRoot.querySelector(this.baseSelector);
20159
+ return this.shadowRoot.querySelector('code');
20080
20160
  }
20081
20161
 
20082
20162
  attributeChangedCallback(attrName, oldValue, newValue) {
@@ -20091,10 +20171,58 @@ let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: c
20091
20171
  this.lang = newValue;
20092
20172
  }
20093
20173
 
20174
+ if (attrName === 'copy-button') {
20175
+ if (newValue === 'true') {
20176
+ this.#initCopyButton();
20177
+ } else {
20178
+ this.#destroyCopyButton();
20179
+ }
20180
+ }
20181
+
20094
20182
  this.#renderSnippet();
20095
20183
  }
20096
20184
  }
20097
20185
 
20186
+ // ── Copy button ────────────────────────────────────────────────────────────
20187
+
20188
+ #initCopyButton() {
20189
+ if (this.shadowRoot.querySelector('.copy-btn')) return;
20190
+
20191
+ const btn = document.createElement('button');
20192
+ btn.className = 'copy-btn';
20193
+ btn.type = 'button';
20194
+ btn.setAttribute('aria-label', 'Copy code');
20195
+ const copyIcon = document.createElement('descope-icon');
20196
+ copyIcon.setAttribute('src', copyIconSrc);
20197
+
20198
+ const checkIcon = document.createElement('descope-icon');
20199
+ checkIcon.setAttribute('src', checkIconSrc);
20200
+ checkIcon.classList.add('check-icon');
20201
+
20202
+ btn.appendChild(copyIcon);
20203
+ btn.appendChild(checkIcon);
20204
+ btn.addEventListener('click', () => this.#handleCopyClick());
20205
+
20206
+ this.shadowRoot.querySelector('.wrapper').appendChild(btn);
20207
+ }
20208
+
20209
+ #destroyCopyButton() {
20210
+ this.shadowRoot.querySelector('.copy-btn')?.remove();
20211
+ }
20212
+
20213
+ #handleCopyClick() {
20214
+ const btn = this.shadowRoot.querySelector('.copy-btn');
20215
+ navigator.clipboard
20216
+ .writeText(decode(this.textContent))
20217
+ .then(() => {
20218
+ btn.classList.add('copied');
20219
+ setTimeout(() => btn.classList.remove('copied'), 2000);
20220
+ })
20221
+ .catch(() => {});
20222
+ }
20223
+
20224
+ // ── Snippet rendering ──────────────────────────────────────────────────────
20225
+
20098
20226
  #renderSnippet() {
20099
20227
  const sanitized = decode(this.textContent);
20100
20228
  const language = this.lang;
@@ -20110,6 +20238,8 @@ let CodeSnippet$1 = class CodeSnippet extends createBaseClass({ componentName: c
20110
20238
  }
20111
20239
  };
20112
20240
 
20241
+ const copyBtn = { selector: () => '.copy-btn' };
20242
+
20113
20243
  const {
20114
20244
  root,
20115
20245
  docTag,
@@ -20261,6 +20391,16 @@ const CodeSnippetClass = compose$1(
20261
20391
  propertyTextColor: { ...property, property: 'color' },
20262
20392
  punctuationTextColor: { ...punctuation, property: 'color' },
20263
20393
  tagTextColor: { ...tag, property: 'color' },
20394
+ copyButtonSize: [
20395
+ { ...copyBtn, property: 'width' },
20396
+ { ...copyBtn, property: 'height' },
20397
+ ],
20398
+ copyButtonBorderRadius: { ...copyBtn, property: 'border-radius' },
20399
+ copyButtonBorderWidth: { ...copyBtn, property: 'border-width' },
20400
+ copyButtonBorderColor: { ...copyBtn, property: 'border-color' },
20401
+ copyButtonBgColor: { ...copyBtn, property: 'background-color' },
20402
+ copyButtonHoverBgColor: { selector: () => '.copy-btn:hover', property: 'background-color' },
20403
+ copyButtonColor: { ...copyBtn, property: 'color' },
20264
20404
  },
20265
20405
  }),
20266
20406
  draggableMixin,
@@ -20298,6 +20438,13 @@ const dark = {
20298
20438
  };
20299
20439
 
20300
20440
  const CodeSnippet = {
20441
+ [vars$c.copyButtonSize]: '32px',
20442
+ [vars$c.copyButtonBorderRadius]: globalRefs$6.radius.xs,
20443
+ [vars$c.copyButtonBorderWidth]: globalRefs$6.border.xs,
20444
+ [vars$c.copyButtonBorderColor]: globalRefs$6.colors.surface.light,
20445
+ [vars$c.copyButtonBgColor]: globalRefs$6.colors.surface.highlight,
20446
+ [vars$c.copyButtonHoverBgColor]: globalRefs$6.colors.surface.light,
20447
+ [vars$c.copyButtonColor]: globalRefs$6.colors.surface.contrast,
20301
20448
  [vars$c.rootBgColor]: globalRefs$6.colors.surface.main,
20302
20449
  [vars$c.rootTextColor]: globalRefs$6.colors.surface.contrast,
20303
20450
  [vars$c.docTagTextColor]: light.color2,