Skip to content

Commit

Permalink
Issue #10: WIP: Working on setting context and nested context
Browse files Browse the repository at this point in the history
  • Loading branch information
patricknelson committed Sep 28, 2023
1 parent 10c7c03 commit 89b7403
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 2 deletions.
48 changes: 48 additions & 0 deletions demo/src/lib/TabsDemo.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script>
import { TabsWrapper, TabList, TabPanel, TabButton } from './tabs';
</script>

<TabsWrapper>
<TabList>
<TabButton>one</TabButton>
<TabButton>two</TabButton>
<TabButton>three</TabButton>
</TabList>

<TabPanel>
<TabsWrapper>
<TabList>
<TabButton>nested one</TabButton>
<TabButton>nested two</TabButton>
<TabButton>nested three</TabButton>
</TabList>

<TabPanel>
<h2>First nested panel</h2>
</TabPanel>

<TabPanel>
<h2>Second nested panel</h2>
</TabPanel>

<TabPanel>
<h2>Third nested panel</h2>
</TabPanel>
</TabsWrapper>
</TabPanel>

<TabPanel>
<h2>Second panel</h2>
</TabPanel>

<TabPanel>
<h2>Third panel</h2>
</TabPanel>
</TabsWrapper>


<style>
:global(body > *) {
width: 600px;
}
</style>
29 changes: 29 additions & 0 deletions demo/src/lib/tabs/TabButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script>
import { getContext } from 'svelte';
import { TABS } from './TabsWrapper.svelte';
const tab = {};
const { registerTab, selectTab, selectedTab } = getContext(TABS);
registerTab(tab);
</script>

<style>
button {
background: none;
border: none;
border-bottom: 2px solid white;
border-radius: 0;
margin: 0;
color: #ccc;
}
.selected {
border-bottom: 2px solid teal;
color: #333;
}
</style>

<button class:selected="{$selectedTab === tab}" on:click="{() => selectTab(tab)}">
<slot></slot>
</button>
9 changes: 9 additions & 0 deletions demo/src/lib/tabs/TabList.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="tab-list">
<slot></slot>
</div>

<style>
.tab-list {
border-bottom: 1px solid teal;
}
</style>
27 changes: 27 additions & 0 deletions demo/src/lib/tabs/TabPanel.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script>
import { getContext } from 'svelte';
import { TABS } from './TabsWrapper.svelte';
const panel = {};
const testContext = getContext(TABS);
console.log('interior panel context', testContext);
const { registerPanel, selectedPanel } = testContext;
registerPanel(panel);
$: active = ($selectedPanel === panel);
</script>

<div class:active>
<slot/>
</div>


<style>
div {
display: none;
}
div.active{
display: block;
}
</style>
50 changes: 50 additions & 0 deletions demo/src/lib/tabs/TabsWrapper.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script context="module">
export const TABS = {};
</script>

<script>
import { setContext, onDestroy } from 'svelte';
import { writable } from 'svelte/store';
const tabs = [];
const panels = [];
const selectedTab = writable(null);
const selectedPanel = writable(null);
setContext(TABS, {
registerTab: tab => {
tabs.push(tab);
selectedTab.update(current => current || tab);
onDestroy(() => {
const i = tabs.indexOf(tab);
tabs.splice(i, 1);
selectedTab.update(current => current === tab ? (tabs[i] || tabs[tabs.length - 1]) : current);
});
},
registerPanel: panel => {
panels.push(panel);
selectedPanel.update(current => current || panel);
onDestroy(() => {
const i = panels.indexOf(panel);
panels.splice(i, 1);
selectedPanel.update(current => current === panel ? (panels[i] || panels[panels.length - 1]) : current);
});
},
selectTab: tab => {
const i = tabs.indexOf(tab);
selectedTab.set(tab);
selectedPanel.set(panels[i]);
},
selectedTab,
selectedPanel
});
</script>

<div class="tabs">
<slot></slot>
</div>
4 changes: 4 additions & 0 deletions demo/src/lib/tabs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as TabsWrapper } from './TabsWrapper.svelte';
export { default as TabList } from './TabList.svelte';
export { default as TabPanel } from './TabPanel.svelte';
export { default as TabButton } from './TabButton.svelte';
35 changes: 35 additions & 0 deletions demo/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,38 @@ svelteRetag({
tagname: 'example-tag',
hydratable,
});


/**
* TabsWrapper.svelte for testing context
*
* TODO: WIP
*/

import TabsDemo from './lib/TabsDemo.svelte';
import { TabsWrapper, TabList, TabPanel, TabButton } from './lib/tabs';

svelteRetag({
component: TabsDemo,
tagname: 'tabs-demo',
});

svelteRetag({
component: TabsWrapper,
tagname: 'tabs-wrapper',
});

svelteRetag({
component: TabList,
tagname: 'tab-list',
});

svelteRetag({
component: TabPanel,
tagname: 'tab-panel',
});

svelteRetag({
component: TabButton,
tagname: 'tab-button',
});
77 changes: 77 additions & 0 deletions demo/tabs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/svelte-retag-favicon.svg"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>svelte-retag demo (tabs demo)</title>
<script type="module" src="src/main.js"></script>
</head>
<body>

<div id="app">
<h1>Tabs demo</h1>
<p>Setup to demonstrate context. Thanks to
<a href="https://stackoverflow.com/users/1990514/amir-pournasserian" target="_blank" rel="noopener">AmirPournasserian</a>
from
<a href="https://stackoverflow.com/questions/75024281/how-to-avoid-overwriting-context-in-nested-components-like-tabs-in-svelte" target="_blank" rel="noopener">
this StackOverflow post</a> for a majority of the tab code (ideal for testing).</p>

<!--<tabs-demo></tabs-demo>-->


<!--<tabs-wrapper>
<tab-list>
<tab-button>button</tab-button>
</tab-list>
<tab-panel>
tab content
</tab-panel>
</tabs-wrapper>-->

<tabs-wrapper>
<tab-list>
<tab-button>one</tab-button>
<tab-button>two</tab-button>
<tab-button>three</tab-button>
</tab-list>

<tab-panel>
fails when nesting
<!-- TODO: ISSUE-10: Fails when nesting -->
<!--<tabs-wrapper>
<tab-list>
<tab-button>nested one</tab-button>
<tab-button>nested two</tab-button>
<tab-button>nested three</tab-button>
</tab-list>
<tab-panel>
<h2>First nested panel</h2>
</tab-panel>
<tab-panel>
<h2>Second nested panel</h2>
</tab-panel>
<tab-panel>
<h2>Third nested panel</h2>
</tab-panel>
</tabs-wrapper>-->
</tab-panel>

<tab-panel>
<h2>Second panel</h2>
</tab-panel>

<tab-panel>
<h2>Third panel</h2>
</tab-panel>
</tabs-wrapper>


</div>

</body>
</html>
1 change: 1 addition & 0 deletions demo/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default defineConfig({
'index.html',
'hydratable.html',
'hydratable.source.html', // Used for generating content in hydratable.html above.
'tabs.html',
],

output: {
Expand Down
33 changes: 31 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export default function(opts) {
// Temporarily instantiate the component ahead of time just so we can get its available properties (statically
// available). Note that we're doing it here in the constructor in case this component has context (so it may
// normally only be instantiated from within another component).
const propInstance = new opts.component({ target: document.createElement('div') });
// TODO: ISSUE-10: Fails since it also needs context. Make this more consistent or find cleaner method.
const context = this._getAncestorContext();
const propInstance = new opts.component({ target: document.createElement('div'), context });
this.propMap = new Map();
for(let key of Object.keys(propInstance.$$.props)) {
this.propMap.set(key.toLowerCase(), key);
Expand Down Expand Up @@ -276,6 +278,24 @@ export default function(opts) {
}
}


/**
* TODO: ISSUE-10: Doc
*/
_getAncestorContext() {
let node = this;
while (node.parentNode) {
if (node?.componentInstance?.$$?.context) {
console.log(this, 'found context in', node);
console.log(node?.componentInstance?.$$?.context);
return node?.componentInstance?.$$?.context;
}
node = node.parentNode;
}
return undefined;
}


/**
* Renders (or rerenders) the Svelte component into this custom element based on the latest properties and slots
* (with slots initialized elsewhere).
Expand Down Expand Up @@ -315,9 +335,18 @@ export default function(opts) {
props[this._translateAttribute(attr.name)] = attr.value;
}



// Instantiate component into our root now, which is either the "light DOM" (i.e. directly under this element) or
// in the shadow DOM.
this.componentInstance = new opts.component({ target: this._root, props: props });
const context = this._getAncestorContext(this);
this.componentInstance = new opts.component({ target: this._root, props: props, context });

/*let that = this;
this.componentInstance = new opts.component({ target: this._root, props: props, get context() {
return that._getAncestorContext(that._root);
} });*/

}

/**
Expand Down

0 comments on commit 89b7403

Please sign in to comment.