@panoramax/web-viewer 4.0.3-develop-819928d9 → 4.0.3-develop-5497573e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><title>Panoramax Widgets</title><style>body{font-family:sans-serif;padding-bottom:100px}h1,h2,nav,p{text-align:center}.test-bench{display:flex;width:100%;max-width:600px;margin:7px auto;justify-content:space-between;align-items:center}.test-bench h3{margin:0}hr.test-sep{width:100%;max-width:600px}</style><script defer="defer" src="index.js"></script><link href="index.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><h1>Panoramax widgets</h1><p>Test page for common Panoramax viewer widgets</p><nav><a href="#button">Button</a> <a href="#link-button">Link Button</a> <a href="#copy-button">Copy button</a> <a href="#button-group">Button Group</a> <a href="#5stars">5-stars grade</a> <a href="#quality-score">Quality Score</a> <a href="#search-bar">Search bar</a> <a href="#progress-bar">Progress bar</a> <a href="#tabs">Tabs</a></nav><h2 id="button">Button</h2><div class="test-bench"><h3>Text + click</h3><pnx-button onclick='alert("Stuff")'>Click to do stuff</pnx-button></div><div class="test-bench"><h3>Icon</h3><pnx-button>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Active</h3><pnx-button active="true">Settings</pnx-button></div><div class="test-bench"><h3>CSS override</h3><div style="width:50%;display:flex;gap:5px;max-width:300px"><pnx-button style="width:50%">⚙️ Settings</pnx-button><pnx-button style="width:50%">🖨️ Print</pnx-button></div></div><hr class="test-sep"/><div class="test-bench"><h3>Full</h3><pnx-button kind="full">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Full active</h3><pnx-button kind="full" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Full L</h3><pnx-button kind="full" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Full XXL</h3><pnx-button kind="full" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Outline</h3><pnx-button kind="outline">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Outline active</h3><pnx-button kind="outline" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Outline L</h3><pnx-button kind="outline" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Outline XXL</h3><pnx-button kind="outline" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Inline</h3><pnx-button kind="inline">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Inline active</h3><pnx-button kind="inline" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Inline L</h3><pnx-button kind="inline" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Inline XXL</h3><pnx-button kind="inline" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Superinline</h3><pnx-button kind="superinline">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superinline active</h3><pnx-button kind="superinline" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superinline L</h3><pnx-button kind="superinline" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Superinline XXL</h3><pnx-button kind="superinline" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Flat</h3><pnx-button kind="flat">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Flat active</h3><pnx-button kind="flat" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Flat L</h3><pnx-button kind="flat" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Flat XXL</h3><pnx-button kind="flat" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Superflat</h3><pnx-button kind="superflat">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superflat active</h3><pnx-button kind="superflat" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superflat L</h3><pnx-button kind="superflat" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Superflat XXL</h3><pnx-button kind="superflat" size="xxl">⚙️</pnx-button></div><h2 id="link-button">Link Button</h2><div class="test-bench"><h3>None set</h3><pnx-link-button>Click for nothing</pnx-link-button></div><div class="test-bench"><h3>All set</h3><pnx-link-button target="_blank" href="https://panoramax.fr" title="Do stuff">Click to do stuff</pnx-link-button></div><h2 id="copy-button">Copy button</h2><div class="test-bench"><h3>Default + text</h3><pnx-copy-button text="copypasta1"></pnx-copy-button></div><div class="test-bench"><h3>Custom + text</h3><pnx-copy-button text="copypasta2">🌐 Share URL</pnx-copy-button></div><div class="test-bench"><h3>Default + input</h3><div style="display:flex;gap:5px;align-items:center"><input value="copypasta3" id="copy-input-1"/><pnx-copy-button input="copy-input-1"></pnx-copy-button></div></div><h2 id="button-group">Button Group</h2><div class="test-bench"><h3>Full Row x2</h3><div><pnx-button-group dir="row"><pnx-button>☹️</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Row x3</h3><div><pnx-button-group dir="row"><pnx-button>☹️</pnx-button><pnx-button>😐</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Row mixed</h3><div><pnx-button-group dir="row"><pnx-button>☹️</pnx-button><pnx-link-button>😐</pnx-link-button><pnx-copy-button>🙂</pnx-copy-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Column x2</h3><div><pnx-button-group dir="column"><pnx-button>☹️</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Column x3</h3><div><pnx-button-group dir="column"><pnx-button>☹️</pnx-button><pnx-button>😐</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Column mixed</h3><div><pnx-button-group dir="column"><pnx-button>☹️</pnx-button><pnx-link-button>😐</pnx-link-button><pnx-copy-button>🙂</pnx-copy-button></pnx-button-group></div></div><hr class="test-sep"/><div class="test-bench"><h3>Outline Row x2</h3><div><pnx-button-group dir="row"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Outline Row x3</h3><div><pnx-button-group dir="row"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">😐</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Outline Column x2</h3><div><pnx-button-group dir="column"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Outline Column x3</h3><div><pnx-button-group dir="column"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">😐</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><hr class="test-sep"/><div class="test-bench"><h3>Flat Row x2</h3><div><pnx-button-group dir="row"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Flat Row x3</h3><div><pnx-button-group dir="row"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Flat Column x2</h3><div><pnx-button-group dir="column"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Flat Column x3</h3><div><pnx-button-group dir="column"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><hr class="test-sep"/><div class="test-bench"><h3>Row XL</h3><div><pnx-button-group size="xl" dir="row"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Column XL</h3><div><pnx-button-group size="xl" dir="column"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><h2 id="5stars">5-stars grade</h2><div class="test-bench"><h3>With a grade</h3><pnx-grade stars="5"/></div><div class="test-bench"><h3>Without a grade</h3><pnx-grade/></div><h2 id="quality-score">Quality Score</h2><div class="test-bench"><h3>Classic no grade</h3><pnx-quality-score/></div><div class="test-bench"><h3>Classic 1/5</h3><pnx-quality-score grade="1"/></div><div class="test-bench"><h3>Classic 2/5</h3><pnx-quality-score grade="2"/></div><div class="test-bench"><h3>Classic 3/5</h3><pnx-quality-score grade="3"/></div><div class="test-bench"><h3>Classic 4/5</h3><pnx-quality-score grade="4"/></div><div class="test-bench"><h3>Classic 5/5</h3><pnx-quality-score grade="5"/></div><hr class="test-sep"/><div class="test-bench"><h3>Input</h3><pnx-quality-score input="qs"/></div><div class="test-bench"><h3>Input 1/5</h3><pnx-quality-score input="qs" grade="1"/></div><div class="test-bench"><h3>Input 2/5</h3><pnx-quality-score input="qs" grade="2"/></div><div class="test-bench"><h3>Input 3/5</h3><pnx-quality-score input="qs" grade="3"/></div><div class="test-bench"><h3>Input 4/5</h3><pnx-quality-score input="qs" grade="4"/></div><div class="test-bench"><h3>Input 5/5</h3><pnx-quality-score input="qs" grade="5"/></div><div class="test-bench"><h3>Input many</h3><pnx-quality-score input="qs" grade="1,3,5"/></div><h2 id="search-bar">Search bar</h2><div class="test-bench"><h3>Simple</h3><pnx-search-bar placeholder="Start your search"></pnx-search-bar></div><div class="test-bench"><h3>With pre</h3><pnx-search-bar placeholder="Look for a place"><span slot="pre">🗺️</span></pnx-search-bar></div><div class="test-bench"><h3>Reduced</h3><pnx-search-bar reduceable reduced placeholder="Look for a place"></pnx-search-bar></div><hr class="test-sep"/><div class="test-bench"><h3>Size md full</h3><pnx-search-bar size="md" placeholder="Look for a place"><span slot="pre">🗺️</span></pnx-search-bar></div><div class="test-bench"><h3>Size md reduced</h3><pnx-search-bar size="md" reduceable reduced placeholder="Look for a place"></pnx-search-bar></div><div class="test-bench"><h3>Size xxl full</h3><pnx-search-bar size="xxl" placeholder="Look for a place"><span slot="pre">🗺️</span></pnx-search-bar></div><div class="test-bench"><h3>Size xxl reduced</h3><pnx-search-bar size="xxl" reduceable reduced placeholder="Look for a place"></pnx-search-bar></div><h2 id="progress-bar">Progress bar</h2><div class="test-bench"><h3>Half</h3><pnx-progress-bar value="42"></pnx-progress-bar></div><div class="test-bench"><h3>Indeterminate</h3><pnx-progress-bar></pnx-progress-bar></div><div class="test-bench"><h3>Full</h3><pnx-progress-bar value="100"></pnx-progress-bar></div><h2 id="tabs">Tabs</h2><div class="test-bench"><h3>Many</h3><pnx-tabs><h4 slot="title">Tab 1</h4><div slot="content">Tab 1 content</div><h4 slot="title">Tab 2</h4><div slot="content">Tab 2 content</div><h4 slot="title">Tab 3</h4><div slot="content">Tab 3 content</div></pnx-tabs></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><title>Panoramax Widgets</title><style>body{font-family:sans-serif;padding-bottom:100px}h1,h2,nav,p{text-align:center}.test-bench{display:flex;width:100%;max-width:600px;margin:7px auto;justify-content:space-between;align-items:center}.test-bench h3{margin:0}hr.test-sep{width:100%;max-width:600px}</style><script defer="defer" src="index.js"></script><link href="index.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><h1>Panoramax widgets</h1><p>Test page for common Panoramax viewer widgets</p><nav><a href="#button">Button</a> <a href="#link-button">Link Button</a> <a href="#copy-button">Copy button</a> <a href="#button-group">Button Group</a> <a href="#5stars">5-stars grade</a> <a href="#quality-score">Quality Score</a> <a href="#search-bar">Search bar</a> <a href="#progress-bar">Progress bar</a> <a href="#tabs">Tabs</a> <a href="#semantics-editor">Semantics Editor</a></nav><h2 id="button">Button</h2><div class="test-bench"><h3>Text + click</h3><pnx-button onclick='alert("Stuff")'>Click to do stuff</pnx-button></div><div class="test-bench"><h3>Icon</h3><pnx-button>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Active</h3><pnx-button active="true">Settings</pnx-button></div><div class="test-bench"><h3>CSS override</h3><div style="width:50%;display:flex;gap:5px;max-width:300px"><pnx-button style="width:50%">⚙️ Settings</pnx-button><pnx-button style="width:50%">🖨️ Print</pnx-button></div></div><hr class="test-sep"/><div class="test-bench"><h3>Full</h3><pnx-button kind="full">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Full active</h3><pnx-button kind="full" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Full L</h3><pnx-button kind="full" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Full XXL</h3><pnx-button kind="full" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Outline</h3><pnx-button kind="outline">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Outline active</h3><pnx-button kind="outline" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Outline L</h3><pnx-button kind="outline" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Outline XXL</h3><pnx-button kind="outline" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Inline</h3><pnx-button kind="inline">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Inline active</h3><pnx-button kind="inline" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Inline L</h3><pnx-button kind="inline" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Inline XXL</h3><pnx-button kind="inline" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Superinline</h3><pnx-button kind="superinline">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superinline active</h3><pnx-button kind="superinline" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superinline L</h3><pnx-button kind="superinline" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Superinline XXL</h3><pnx-button kind="superinline" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Flat</h3><pnx-button kind="flat">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Flat active</h3><pnx-button kind="flat" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Flat L</h3><pnx-button kind="flat" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Flat XXL</h3><pnx-button kind="flat" size="xxl">⚙️</pnx-button></div><div class="test-bench"><h3>Superflat</h3><pnx-button kind="superflat">⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superflat active</h3><pnx-button kind="superflat" active>⚙️ Settings</pnx-button></div><div class="test-bench"><h3>Superflat L</h3><pnx-button kind="superflat" size="l">⚙️</pnx-button></div><div class="test-bench"><h3>Superflat XXL</h3><pnx-button kind="superflat" size="xxl">⚙️</pnx-button></div><h2 id="link-button">Link Button</h2><div class="test-bench"><h3>None set</h3><pnx-link-button>Click for nothing</pnx-link-button></div><div class="test-bench"><h3>All set</h3><pnx-link-button target="_blank" href="https://panoramax.fr" title="Do stuff">Click to do stuff</pnx-link-button></div><h2 id="copy-button">Copy button</h2><div class="test-bench"><h3>Default + text</h3><pnx-copy-button text="copypasta1"></pnx-copy-button></div><div class="test-bench"><h3>Custom + text</h3><pnx-copy-button text="copypasta2">🌐 Share URL</pnx-copy-button></div><div class="test-bench"><h3>Default + input</h3><div style="display:flex;gap:5px;align-items:center"><input value="copypasta3" id="copy-input-1"/><pnx-copy-button input="copy-input-1"></pnx-copy-button></div></div><h2 id="button-group">Button Group</h2><div class="test-bench"><h3>Full Row x2</h3><div><pnx-button-group dir="row"><pnx-button>☹️</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Row x3</h3><div><pnx-button-group dir="row"><pnx-button>☹️</pnx-button><pnx-button>😐</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Row mixed</h3><div><pnx-button-group dir="row"><pnx-button>☹️</pnx-button><pnx-link-button>😐</pnx-link-button><pnx-copy-button>🙂</pnx-copy-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Column x2</h3><div><pnx-button-group dir="column"><pnx-button>☹️</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Column x3</h3><div><pnx-button-group dir="column"><pnx-button>☹️</pnx-button><pnx-button>😐</pnx-button><pnx-button>🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Full Column mixed</h3><div><pnx-button-group dir="column"><pnx-button>☹️</pnx-button><pnx-link-button>😐</pnx-link-button><pnx-copy-button>🙂</pnx-copy-button></pnx-button-group></div></div><hr class="test-sep"/><div class="test-bench"><h3>Outline Row x2</h3><div><pnx-button-group dir="row"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Outline Row x3</h3><div><pnx-button-group dir="row"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">😐</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Outline Column x2</h3><div><pnx-button-group dir="column"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Outline Column x3</h3><div><pnx-button-group dir="column"><pnx-button kind="outline">☹️</pnx-button><pnx-button kind="outline">😐</pnx-button><pnx-button kind="outline">🙂</pnx-button></pnx-button-group></div></div><hr class="test-sep"/><div class="test-bench"><h3>Flat Row x2</h3><div><pnx-button-group dir="row"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Flat Row x3</h3><div><pnx-button-group dir="row"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Flat Column x2</h3><div><pnx-button-group dir="column"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Flat Column x3</h3><div><pnx-button-group dir="column"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><hr class="test-sep"/><div class="test-bench"><h3>Row XL</h3><div><pnx-button-group size="xl" dir="row"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><div class="test-bench"><h3>Column XL</h3><div><pnx-button-group size="xl" dir="column"><pnx-button kind="flat">☹️</pnx-button><pnx-button kind="flat">😐</pnx-button><pnx-button kind="flat">🙂</pnx-button></pnx-button-group></div></div><h2 id="5stars">5-stars grade</h2><div class="test-bench"><h3>With a grade</h3><pnx-grade stars="5"/></div><div class="test-bench"><h3>Without a grade</h3><pnx-grade/></div><h2 id="quality-score">Quality Score</h2><div class="test-bench"><h3>Classic no grade</h3><pnx-quality-score/></div><div class="test-bench"><h3>Classic 1/5</h3><pnx-quality-score grade="1"/></div><div class="test-bench"><h3>Classic 2/5</h3><pnx-quality-score grade="2"/></div><div class="test-bench"><h3>Classic 3/5</h3><pnx-quality-score grade="3"/></div><div class="test-bench"><h3>Classic 4/5</h3><pnx-quality-score grade="4"/></div><div class="test-bench"><h3>Classic 5/5</h3><pnx-quality-score grade="5"/></div><hr class="test-sep"/><div class="test-bench"><h3>Input</h3><pnx-quality-score input="qs"/></div><div class="test-bench"><h3>Input 1/5</h3><pnx-quality-score input="qs" grade="1"/></div><div class="test-bench"><h3>Input 2/5</h3><pnx-quality-score input="qs" grade="2"/></div><div class="test-bench"><h3>Input 3/5</h3><pnx-quality-score input="qs" grade="3"/></div><div class="test-bench"><h3>Input 4/5</h3><pnx-quality-score input="qs" grade="4"/></div><div class="test-bench"><h3>Input 5/5</h3><pnx-quality-score input="qs" grade="5"/></div><div class="test-bench"><h3>Input many</h3><pnx-quality-score input="qs" grade="1,3,5"/></div><h2 id="search-bar">Search bar</h2><div class="test-bench"><h3>Simple</h3><pnx-search-bar placeholder="Start your search"></pnx-search-bar></div><div class="test-bench"><h3>With pre</h3><pnx-search-bar placeholder="Look for a place"><span slot="pre">🗺️</span></pnx-search-bar></div><div class="test-bench"><h3>Reduced</h3><pnx-search-bar reduceable reduced placeholder="Look for a place"></pnx-search-bar></div><hr class="test-sep"/><div class="test-bench"><h3>Size md full</h3><pnx-search-bar size="md" placeholder="Look for a place"><span slot="pre">🗺️</span></pnx-search-bar></div><div class="test-bench"><h3>Size md reduced</h3><pnx-search-bar size="md" reduceable reduced placeholder="Look for a place"></pnx-search-bar></div><div class="test-bench"><h3>Size xxl full</h3><pnx-search-bar size="xxl" placeholder="Look for a place"><span slot="pre">🗺️</span></pnx-search-bar></div><div class="test-bench"><h3>Size xxl reduced</h3><pnx-search-bar size="xxl" reduceable reduced placeholder="Look for a place"></pnx-search-bar></div><h2 id="progress-bar">Progress bar</h2><div class="test-bench"><h3>Half</h3><pnx-progress-bar value="42"></pnx-progress-bar></div><div class="test-bench"><h3>Indeterminate</h3><pnx-progress-bar></pnx-progress-bar></div><div class="test-bench"><h3>Full</h3><pnx-progress-bar value="100"></pnx-progress-bar></div><h2 id="tabs">Tabs</h2><div class="test-bench"><h3>Many</h3><pnx-tabs><h4 slot="title">Tab 1</h4><div slot="content">Tab 1 content</div><h4 slot="title">Tab 2</h4><div slot="content">Tab 2 content</div><h4 slot="title">Tab 3</h4><div slot="content">Tab 3 content</div></pnx-tabs></div><h2 id="semantics-editor">Semantics editor</h2><script>window.addEventListener("load",(()=>{const e=document.getElementsByTagName("pnx-semantics-editor");for(let t=0;t<e.length;t++){const n=e[t];n.nextSibling;n.addEventListener("change",(e=>{console.log("Semantics change > evt =",e.detail,"| attr =",n.getAttribute("semantics"))}))}}))</script><div class="test-bench"><h3>Empty</h3><pnx-semantics-editor _t='{"pnx": {"semantics_editor_error": "The syntax is invalid. Your tags may look like:\nkey=value\nprefix|key=value\nprefix|key[qualif_key=qualif_val]=value"}}'></pnx-semantics-editor></div><div class="test-bench"><h3>Filled</h3><pnx-semantics-editor semantics="[{key: 'osm|traffic_sign', value: 'FR:A14b'}]"></pnx-semantics-editor></div><div class="test-bench"><h3>Many rows</h3><pnx-semantics-editor rows="5" semantics="[{key: 'osm|traffic_sign', value: 'FR:A14b'}]"></pnx-semantics-editor></div><div class="test-bench"><h3>Custom style</h3><style>#pnx-sem-ed3::part(text){color:#00f}</style><pnx-semantics-editor id="pnx-sem-ed3" semantics="[{key: 'osm|traffic_sign', value: 'FR:A14b'}]"></pnx-semantics-editor></div></body></html>
@@ -0,0 +1,79 @@
1
+ <a name="Panoramax.components.ui.SemanticsEditor"></a>
2
+
3
+ ## Panoramax.components.ui.SemanticsEditor ⇐ <code>[lit.LitElement](https://lit.dev/docs/api/LitElement/)</code>
4
+ **Kind**: static class of <code>Panoramax.components.ui</code>
5
+ **Extends**: <code>[lit.LitElement](https://lit.dev/docs/api/LitElement/)</code>
6
+ **Emits**: [<code>change</code>](#Panoramax.components.ui.SemanticsEditor+event_change)
7
+ **Element**: pnx-semantics-editor
8
+
9
+ * [.SemanticsEditor](#Panoramax.components.ui.SemanticsEditor) ⇐ <code>[lit.LitElement](https://lit.dev/docs/api/LitElement/)</code>
10
+ * [new SemanticsEditor()](#new_Panoramax.components.ui.SemanticsEditor_new)
11
+ * [.properties](#Panoramax.components.ui.SemanticsEditor+properties) : <code>Object</code>
12
+ * [.getDiff()](#Panoramax.components.ui.SemanticsEditor+getDiff) ⇒ <code>Array.&lt;object&gt;</code>
13
+ * ["change"](#Panoramax.components.ui.SemanticsEditor+event_change)
14
+
15
+ <a name="new_Panoramax.components.ui.SemanticsEditor_new"></a>
16
+
17
+ ### new SemanticsEditor()
18
+ Semantics Editor offer an easy-to-use input for adding or editing semantics tags.
19
+
20
+ It manipulates list of `{key: "mypanokey", value: "myvalue"}` entries through `semantics` attribute.
21
+
22
+ **Example**
23
+ ```html
24
+ <!-- Basic example -->
25
+ <pnx-semantics-editor
26
+ id="editor"
27
+ semantics=${mypic.semantics}
28
+ ._t=${viewer._t}
29
+ onchange=${e => console.log(e.detail.semantics)}
30
+ />
31
+
32
+ <!-- You can access editor and check its validity through native web browser functions -->
33
+ <script>
34
+ const editor = document.getElementById("editor");
35
+ console.log(editor.checkValidity()); // True if input is valid
36
+ </script>
37
+
38
+ <!-- You can change specifically style of textarea -->
39
+ <style>
40
+ pnx-semantics-editor::part(text) {
41
+ color: blue;
42
+ }
43
+ </style>
44
+ ```
45
+ <a name="Panoramax.components.ui.SemanticsEditor+properties"></a>
46
+
47
+ ### semanticsEditor.properties : <code>Object</code>
48
+ Component properties.
49
+
50
+ **Kind**: instance property of [<code>SemanticsEditor</code>](#Panoramax.components.ui.SemanticsEditor)
51
+ **Properties**
52
+
53
+ | Name | Type | Default | Description |
54
+ | --- | --- | --- | --- |
55
+ | [semantics] | <code>object</code> | | The `semantics` field of a picture or annotation feature. It is updated when field changes, but reflect the whole list of new tags (not delta needed by API). If you want delta, please use getDiff function. |
56
+ | [rows] | <code>number</code> | <code>3</code> | The amount of rows shown for textarea |
57
+
58
+ <a name="Panoramax.components.ui.SemanticsEditor+getDiff"></a>
59
+
60
+ ### semanticsEditor.getDiff() ⇒ <code>Array.&lt;object&gt;</code>
61
+ Get current delta between initial tags and user changes.
62
+
63
+ **Kind**: instance method of [<code>SemanticsEditor</code>](#Panoramax.components.ui.SemanticsEditor)
64
+ **Returns**: <code>Array.&lt;object&gt;</code> - The list of tag changes (in API format)
65
+ <a name="Panoramax.components.ui.SemanticsEditor+event_change"></a>
66
+
67
+ ### "change"
68
+ Event for value change.
69
+
70
+ Note that this event is launched only on valid input.
71
+
72
+ **Kind**: event emitted by [<code>SemanticsEditor</code>](#Panoramax.components.ui.SemanticsEditor)
73
+ **Properties**
74
+
75
+ | Name | Type | Description |
76
+ | --- | --- | --- |
77
+ | detail.semantics | <code>Array.&lt;object&gt;</code> | The new tags list (in API semantics property format) |
78
+ | detail.delta | <code>Array.&lt;object&gt;</code> | The delta between old and current tags (expected by API) |
79
+
package/docs/reference.md CHANGED
@@ -58,6 +58,7 @@ Basic UI components:
58
58
  - [ProgressBar](./reference/components/ui/ProgressBar.md) : a progress bar.
59
59
  - [QualityScore](./reference/components/ui/QualityScore.md) : a A/B/C/D/E grade display and input.
60
60
  - [SearchBar](./reference/components/ui/SearchBar.md) : a search bar.
61
+ - [SemanticsEditor](./reference/components/ui/SemanticsEditor.md) : input field for editing semantic tags of a picture/annotation.
61
62
  - [SemanticsTable](./reference/components/ui/SemanticsTable.md) : table for showing complete semantic tags of a picture/annotation.
62
63
  - [TogglableGroup](./reference/components/ui/TogglableGroup.md) : an helper for showing a menu on button click.
63
64
 
package/mkdocs.yml CHANGED
@@ -104,6 +104,7 @@ nav:
104
104
  - ProgressBar: 'reference/components/ui/ProgressBar.md'
105
105
  - QualityScore: 'reference/components/ui/QualityScore.md'
106
106
  - SearchBar: 'reference/components/ui/SearchBar.md'
107
+ - SemanticsEditor: 'reference/components/ui/SemanticsEditor.md'
107
108
  - SemanticsTable: 'reference/components/ui/SemanticsTable.md'
108
109
  - TogglableGroup: 'reference/components/ui/TogglableGroup.md'
109
110
  - utils:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "4.0.3-develop-819928d9",
3
+ "version": "4.0.3-develop-5497573e",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
@@ -39,6 +39,7 @@
39
39
  <a href="#search-bar">Search bar</a>
40
40
  <a href="#progress-bar">Progress bar</a>
41
41
  <a href="#tabs">Tabs</a>
42
+ <a href="#semantics-editor">Semantics Editor</a>
42
43
  </nav>
43
44
 
44
45
  <h2 id="button">Button</h2>
@@ -302,5 +303,36 @@
302
303
  <div slot="content">Tab 3 content</div>
303
304
  </pnx-tabs>
304
305
  </div>
306
+
307
+ <h2 id="semantics-editor">Semantics editor</h2>
308
+ <script>
309
+ window.addEventListener("load", () => {
310
+ const semeditors = document.getElementsByTagName("pnx-semantics-editor");
311
+ for(let i=0; i < semeditors.length; i++) {
312
+ const semeditor = semeditors[i];
313
+ const div = semeditor.nextSibling;
314
+ semeditor.addEventListener("change", e => {
315
+ console.log("Semantics change > evt =", e.detail, "| attr =", semeditor.getAttribute("semantics"));
316
+ });
317
+ }
318
+ });
319
+ </script>
320
+ <div class="test-bench">
321
+ <h3>Empty</h3>
322
+ <pnx-semantics-editor _t='{"pnx": {"semantics_editor_error": "The syntax is invalid. Your tags may look like:\nkey=value\nprefix|key=value\nprefix|key[qualif_key=qualif_val]=value"}}'></pnx-semantics-editor>
323
+ </div>
324
+ <div class="test-bench">
325
+ <h3>Filled</h3>
326
+ <pnx-semantics-editor semantics="[{key: 'osm|traffic_sign', value: 'FR:A14b'}]"></pnx-semantics-editor>
327
+ </div>
328
+ <div class="test-bench">
329
+ <h3>Many rows</h3>
330
+ <pnx-semantics-editor rows="5" semantics="[{key: 'osm|traffic_sign', value: 'FR:A14b'}]"></pnx-semantics-editor>
331
+ </div>
332
+ <div class="test-bench">
333
+ <h3>Custom style</h3>
334
+ <style>#pnx-sem-ed3::part(text) { color: blue; }</style>
335
+ <pnx-semantics-editor id="pnx-sem-ed3" semantics="[{key: 'osm|traffic_sign', value: 'FR:A14b'}]"></pnx-semantics-editor>
336
+ </div>
305
337
  </body>
306
338
  </html>
@@ -341,7 +341,7 @@ export default class Basic extends LitElement {
341
341
  static GetJSONConverter() {
342
342
  return {
343
343
  fromAttribute: (value) => {
344
- return typeof value === "object" ? value : JSON5.parse(value);
344
+ return typeof value === "object" || Array.isArray(value) ? value : JSON5.parse(value);
345
345
  },
346
346
  toAttribute: (value) => JSON.stringify(value)
347
347
  };
@@ -0,0 +1,148 @@
1
+ import { LitElement, html, css } from "lit";
2
+ import Basic from "../core/Basic";
3
+ import { parseSemanticsString, computeDiffTags } from "../../utils/semantics";
4
+ import { textarea } from "../styles";
5
+ import JSON5 from "json5";
6
+
7
+ /**
8
+ * Semantics Editor offer an easy-to-use input for adding or editing semantics tags.
9
+ *
10
+ * It manipulates list of `{key: "mypanokey", value: "myvalue"}` entries through `semantics` attribute.
11
+ *
12
+ * @class Panoramax.components.ui.SemanticsEditor
13
+ * @element pnx-semantics-editor
14
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
15
+ * @fires Panoramax.components.ui.SemanticsEditor#change
16
+ * @example
17
+ * ```html
18
+ * <!-- Basic example -->
19
+ * <pnx-semantics-editor
20
+ * id="editor"
21
+ * semantics=${mypic.semantics}
22
+ * ._t=${viewer._t}
23
+ * onchange=${e => console.log(e.detail.semantics)}
24
+ * />
25
+ *
26
+ * <!-- You can access editor and check its validity through native web browser functions -->
27
+ * <script>
28
+ * const editor = document.getElementById("editor");
29
+ * console.log(editor.checkValidity()); // True if input is valid
30
+ * </script>
31
+ *
32
+ * <!-- You can change specifically style of textarea -->
33
+ * <style>
34
+ * pnx-semantics-editor::part(text) {
35
+ * color: blue;
36
+ * }
37
+ * </style>
38
+ * ```
39
+ */
40
+ export default class SemanticsEditor extends LitElement {
41
+ static styles = [textarea, css`
42
+ textarea:invalid {
43
+ border: 2px solid var(--red);
44
+ background-color:var(--beige);
45
+ }
46
+ `];
47
+
48
+ /**
49
+ * Component properties.
50
+ * @memberof Panoramax.components.ui.SemanticsEditor#
51
+ * @type {Object}
52
+ * @property {object} [semantics] The `semantics` field of a picture or annotation feature. It is updated when field changes, but reflect the whole list of new tags (not delta needed by API). If you want delta, please use getDiff function.
53
+ * @property {number} [rows=3] The amount of rows shown for textarea
54
+ */
55
+ static properties = {
56
+ semantics: {converter: Basic.GetJSONConverter(), reflect: true},
57
+ rows: {type: Number},
58
+ _valid: {state: true},
59
+ _t: {converter: Basic.GetJSONConverter()},
60
+ _firstSemantics: {state: true},
61
+ };
62
+
63
+ constructor() {
64
+ super();
65
+ this.semantics = null;
66
+ this._valid = true;
67
+ this._firstSemantics = null;
68
+ this.rows = 3;
69
+ }
70
+
71
+ /** @private */
72
+ connectedCallback() {
73
+ super.connectedCallback();
74
+ this._firstSemantics = this.semantics === null ? [] : JSON5.parse(JSON5.stringify(this.semantics));
75
+ }
76
+
77
+ /**
78
+ * Get current delta between initial tags and user changes.
79
+ * @memberof Panoramax.components.ui.SemanticsEditor#
80
+ * @returns {object[]} The list of tag changes (in API format)
81
+ */
82
+ getDiff() {
83
+ return computeDiffTags(this._firstSemantics || [], this.semantics);
84
+ }
85
+
86
+ /** @private */
87
+ _onInput(e) {
88
+ const tarea = e.target;
89
+ try {
90
+ this.semantics = parseSemanticsString(tarea.value) || [];
91
+ this._valid = true;
92
+ } catch (err) {
93
+ if(err.message !== "Invalid tags") { console.error(err); }
94
+ this._valid = false;
95
+ }
96
+ }
97
+
98
+ /** @private */
99
+ _onBlur(e) {
100
+ const prevValidity = e.target.checkValidity();
101
+
102
+ if(this._valid) {
103
+ e.target.setCustomValidity("");
104
+
105
+ /**
106
+ * Event for value change.
107
+ *
108
+ * Note that this event is launched only on valid input.
109
+ *
110
+ * @event Panoramax.components.ui.SemanticsEditor#change
111
+ * @type {CustomEvent}
112
+ * @property {object[]} detail.semantics The new tags list (in API semantics property format)
113
+ * @property {object[]} detail.delta The delta between old and current tags (expected by API)
114
+ */
115
+ this.dispatchEvent(new CustomEvent("change", {
116
+ detail: {
117
+ semantics: this.semantics || [],
118
+ delta: computeDiffTags(this._firstSemantics || [], this.semantics),
119
+ }
120
+ }));
121
+ }
122
+ else if(prevValidity) { // Do not call again if already shows up, to fix Chrome issue
123
+ e.target.setCustomValidity(this._t?.pnx.semantics_editor_error || "Invalid syntax");
124
+ e.target.reportValidity();
125
+ }
126
+ }
127
+
128
+ /** @private */
129
+ render() {
130
+ /* eslint-disable indent */
131
+ return html`
132
+ <textarea
133
+ part="text"
134
+ autocomplete="off"
135
+ autocorrect="off"
136
+ autocapitalize="off"
137
+ spellcheck="false"
138
+ placeholder="key1=value1\nprefix|key2=value2"
139
+ @input=${this._onInput}
140
+ @blur=${this._onBlur}
141
+ rows=${this.rows}
142
+ .value=${(this.semantics || []).map(s => `${s.key}=${s.value}`).join("\n")}
143
+ ></textarea>
144
+ `;
145
+ }
146
+ }
147
+
148
+ customElements.define("pnx-semantics-editor", SemanticsEditor);
@@ -19,6 +19,7 @@ export {default as Popup} from "./Popup";
19
19
  export {default as ProgressBar} from "./ProgressBar";
20
20
  export {default as QualityScore} from "./QualityScore";
21
21
  export {default as SearchBar} from "./SearchBar";
22
+ export {default as SemanticsEditor} from "./SemanticsEditor";
22
23
  export {default as SemanticsTable} from "./SemanticsTable";
23
24
  export {default as TogglableGroup} from "./TogglableGroup";
24
25
  import * as widgets from "./widgets";
@@ -199,6 +199,7 @@
199
199
  "P279": "subclass of",
200
200
  "P361": "part of"
201
201
  },
202
+ "semantics_editor_error": "The syntax is invalid. Your tags may look like:\nkey=value\nprefix|key=value\nprefix|key[qualif_key=qualif_val]=value\n\nMax key length is 256 characters, max value length 2048 characters.",
202
203
  "report": "Report",
203
204
  "report_auth": "This report will be sent using your account \"{a}\"",
204
205
  "report_nature_label": "Nature of the issue",
@@ -199,6 +199,7 @@
199
199
  "P279": "sous-classe de",
200
200
  "P361": "partie de"
201
201
  },
202
+ "semantics_editor_error": "La syntaxe est invalide. Vos attributs doivent avoir cette forme:\nclé=valeur\npréfixe|clé=valeur\npréfixe|clé[qualif_clé=qualif_val]=valeur\n\nLongueur max des clés : 256 caractères, max des valeurs 2048 caractères.",
202
203
  "report": "Signaler",
203
204
  "report_auth": "Ce signalement sera envoyé en utilisant votre compte \"{a}\"",
204
205
  "report_nature_label": "Nature du problème",
@@ -79,7 +79,7 @@ const KNOWN_PREFIXES = {
79
79
  * @private
80
80
  */
81
81
  export function decodeBasicTag(tag) {
82
- const firstEqual = (tag || "").indexOf("=");
82
+ const firstEqual = (tag || "").lastIndexOf("=");
83
83
  if(firstEqual < 0) { return null; }
84
84
  return {
85
85
  key: decodeKey(tag.substring(0, firstEqual)),
@@ -99,6 +99,7 @@ export function decodeKey(key = "") {
99
99
 
100
100
  const [, prefix, subkey, qualifies ] = match;
101
101
  return {
102
+ key: key,
102
103
  prefix: prefix || "",
103
104
  subkey,
104
105
  qualifies: decodeBasicTag(qualifies),
@@ -106,6 +107,57 @@ export function decodeKey(key = "") {
106
107
  }
107
108
 
108
109
 
110
+ /**
111
+ * Transforms a string containing a list of tags in a ready-to-use parsed object list.
112
+ * @param {string} str The string to read (each tag separated by newline `\n`)
113
+ * @returns {object[]} List of API-formatted tags
114
+ */
115
+ export function parseSemanticsString(str) {
116
+ const parsedTags = str.split("\n").map(t => {
117
+ const parts = decodeBasicTag(t);
118
+ if(
119
+ parts
120
+ && parts.key.key.length <= 256
121
+ && parts.value.length <= 2048
122
+ ) {
123
+ return { key: parts.key.key, value: parts.value };
124
+ } else { return null; }
125
+ });
126
+
127
+ if(parsedTags.findIndex(v => !v) >= 0) {
128
+ if(str.trim().length === 0 && parsedTags.length === 1) { return []; }
129
+ throw new Error("Invalid tags");
130
+ }
131
+
132
+ return parsedTags;
133
+ }
134
+
135
+
136
+ /**
137
+ * Computes the difference between two set of API tags.
138
+ * API expects a delta between old & new, so result contains only
139
+ * added and deleted tags.
140
+ * @param {object[]} prev The previous set of tags
141
+ * @param {object[]} next The new set of tags
142
+ * @returns {object[]} The new set of tags, with only added/deleted tags
143
+ */
144
+ export function computeDiffTags(prev = [], next = []) {
145
+ const res = [];
146
+
147
+ // Look for new values
148
+ (next || [])
149
+ .filter(nt => prev.find(pt => pt.key == nt.key && pt.value == nt.value) === undefined)
150
+ .forEach(t => res.push({key: t.key, value: t.value, action: "add"}));
151
+
152
+ // Look for prev values missing in next
153
+ (prev || [])
154
+ .filter(pt => next.find(nt => pt.key == nt.key && pt.value == nt.value) === undefined)
155
+ .forEach(t => res.push({key: t.key, value: t.value, action: "delete"}));
156
+
157
+ return res;
158
+ }
159
+
160
+
109
161
  /**
110
162
  * Transforms raw API semantics properties into ready-to-display container.
111
163
  * @param {object[]} tags The API semantics tags
@@ -1,4 +1,6 @@
1
- import { decodeKey, decodeBasicTag, groupByPrefix } from "../../src/utils/semantics";
1
+ import {
2
+ decodeKey, decodeBasicTag, computeDiffTags, groupByPrefix
3
+ } from "../../src/utils/semantics";
2
4
 
3
5
  describe("decodeBasicTag", () => {
4
6
  test("should return null if no equal sign is present", () => {
@@ -7,14 +9,14 @@ describe("decodeBasicTag", () => {
7
9
 
8
10
  test("should correctly decode a tag with an equal sign", () => {
9
11
  expect(decodeBasicTag("key=value")).toEqual({
10
- key: { prefix: "", subkey: "key", qualifies: null },
12
+ key: { key: "key", prefix: "", subkey: "key", qualifies: null },
11
13
  value: "value"
12
14
  });
13
15
  });
14
16
 
15
17
  test("should correctly decode a tag with a prefix", () => {
16
18
  expect(decodeBasicTag("osm|key=value")).toEqual({
17
- key: { prefix: "osm", subkey: "key", qualifies: null },
19
+ key: { key: "osm|key", prefix: "osm", subkey: "key", qualifies: null },
18
20
  value: "value"
19
21
  });
20
22
  });
@@ -23,6 +25,7 @@ describe("decodeBasicTag", () => {
23
25
  describe("decodeKey", () => {
24
26
  test("should return default structure if key does not match regex", () => {
25
27
  expect(decodeKey("panoKey")).toEqual({
28
+ key: "panoKey",
26
29
  prefix: "",
27
30
  subkey: "panoKey",
28
31
  qualifies: null
@@ -31,6 +34,7 @@ describe("decodeKey", () => {
31
34
 
32
35
  test("should correctly decode a key with a prefix", () => {
33
36
  expect(decodeKey("osm|traffic_sign")).toEqual({
37
+ key: "osm|traffic_sign",
34
38
  prefix: "osm",
35
39
  subkey: "traffic_sign",
36
40
  qualifies: null
@@ -39,21 +43,46 @@ describe("decodeKey", () => {
39
43
 
40
44
  test("should correctly decode a key with qualifiers", () => {
41
45
  expect(decodeKey("detection_model[camera_mount=pole]")).toEqual({
46
+ key: "detection_model[camera_mount=pole]",
42
47
  prefix: "",
43
48
  subkey: "detection_model",
44
- qualifies: { key: { prefix: "", subkey: "camera_mount", qualifies: null }, value: "pole" }
49
+ qualifies: { key: { key: "camera_mount", prefix: "", subkey: "camera_mount", qualifies: null }, value: "pole" }
45
50
  });
46
51
  });
47
52
 
48
53
  test("should correctly decode a key with a prefix and qualifiers", () => {
49
54
  expect(decodeKey("osm|source[osm|traffic_sign=stop]")).toEqual({
55
+ key: "osm|source[osm|traffic_sign=stop]",
50
56
  prefix: "osm",
51
57
  subkey: "source",
52
- qualifies: { key: { prefix: "osm", subkey: "traffic_sign", qualifies: null }, value: "stop" }
58
+ qualifies: { key: { key: "osm|traffic_sign", prefix: "osm", subkey: "traffic_sign", qualifies: null }, value: "stop" }
53
59
  });
54
60
  });
55
61
  });
56
62
 
63
+ describe("computeDiffTags", () => {
64
+ it("should return new set of tags when no tag is missing in next", () => {
65
+ const prev = [{key: "tag1", value: "value1"}, {key: "tag2", value: "value2"}];
66
+ const next = [{key: "tag1", value: "value1"}, {key: "tag2", value: "value2"}, {key: "tag3", value: "value3"}];
67
+
68
+ expect(computeDiffTags(prev, next)).toEqual([{key: "tag3", value: "value3", action: "add"}]);
69
+ });
70
+
71
+ it("should return new set of tags with missing tags marked as delete when some tag is missing in next", () => {
72
+ const prev = [{key: "tag1", value: "value1"}, {key: "tag2", value: "value2"}];
73
+ const next = [{key: "tag1", value: "value1"}];
74
+
75
+ expect(computeDiffTags(prev, next)).toEqual([{key: "tag2", value: "value2", action: "delete"}]);
76
+ });
77
+
78
+ it("should return new set of tags with all tags marked as delete when all tags are missing in next", () => {
79
+ const prev = [{key: "tag1", value: "value1"}, {key: "tag2", value: "value2"}];
80
+ const next = [];
81
+
82
+ expect(computeDiffTags(prev, next)).toEqual([{key: "tag1", value: "value1", action: "delete"}, {key: "tag2", value: "value2", action: "delete"}]);
83
+ });
84
+ });
85
+
57
86
  describe("groupByPrefix", () => {
58
87
  test("should group tags by prefix", () => {
59
88
  const tags = [