Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to automatically forward all attributes to component props #35

Merged
merged 12 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ svelteRetag({
tagname: 'hello-world',

// Optional:
attributes: ['greetperson'], // Changes to these attributes will be reactively forwarded to your component
attributes: true, // Forward all attributes to your component, or set to explicit list of attributes, e.g. ['greetperson'] or leave empty
shadow: false, // Use the light DOM
href: '/your/stylesheet.css', // Only necessary if shadow is true
});
Expand All @@ -100,6 +100,7 @@ the [Lit-style naming convention](https://lit.dev/docs/components/properties/#ob
on your component would be automatically made available as `greetperson` on your custom element.

```html

<hello-world greetperson="Cris"></hello-world>
```

Expand All @@ -108,13 +109,13 @@ the [Hello World demo](https://github.com/patricknelson/svelte-retag/tree/main/d

### Options 🛠

| Option | Default | Description |
|--------------|:------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `component` | _(required)_ | The constructor for your Svelte component (from `import`) |
| `tagname` | _(required)_ | The custom element tag name to use ([must contain a dash](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)) |
| `attributes` | `[]` | Optional. List of attributes to reactively forward to your component (does not reflect changes inside the component). <br><br> **Important:** Attributes must be the lowercase version of your Svelte component props ([similar to Lit](https://lit.dev/docs/components/properties/#observed-attributes)). |
| `shadow` | `false` | Optional. Indicates if this component should use shadow DOM. <br/><br/> **Note:** Only basic support for shadow DOM is currently provided. See https://github.com/patricknelson/svelte-retag/issues/6. |
| `href` | `''` | Optional. URL to your stylesheet. Allows you to ensure your styles are included in the shadow DOM. This option is only useful when `shadow` is set to `true`. |
| Option | Default | Description |
|--------------|:------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `component` | _(required)_ | The constructor for your Svelte component (from `import`) |
| `tagname` | _(required)_ | The custom element tag name to use ([must contain a dash](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)) |
| `attributes` | `[]` | `Array` (legacy): Explicit list of attributes to reactively forward to your component. Attributes must be the lowercase version of your Svelte component props ([similar to Lit](https://lit.dev/docs/components/properties/#observed-attributes)). <br><br> `Boolean` (recommended): If set to `true`, will automatically forward all attributes to your component props. If `false`, will not forward anything. <br><br> **Note:** In v2, this option will be removed and all attributes will be forwarded by default (for consistency with Svelte 4's custom elements, see https://github.com/patricknelson/svelte-retag/issues/36). |
| `shadow` | `false` | Optional. Indicates if this component should use shadow DOM. <br/><br/> **Note:** Only basic support for shadow DOM is currently provided. See https://github.com/patricknelson/svelte-retag/issues/6. |
| `href` | `''` | Optional. URL to your stylesheet. Allows you to ensure your styles are included in the shadow DOM. This option is only useful when `shadow` is set to `true`. |

**Note:** For portability, `svelte-retag`'s API is fully backward compatible
with [`svelte-tag@^1.0.0`](https://github.com/crisward/svelte-tag).
Expand Down Expand Up @@ -151,14 +152,14 @@ Changes since forking from [`svelte-tag`](https://github.com/crisward/svelte-tag
- Add demos to vercel site (see https://github.com/patricknelson/svelte-retag/issues/11)
- Add step-by-step instructions and provided a simple MVP
example (https://github.com/patricknelson/svelte-retag/pull/24)
- Automatically forward all attributes to component (i.e. `attributes: true`) (https://github.com/patricknelson/svelte-retag/issues/34)

### v2

See the **[milestones page](https://github.com/patricknelson/svelte-retag/milestones)** for changes planned in upcoming
versions. Please be aware that until the version is officially released, the features slated for a particular version
are subject to change!


## Support & Contribution

**Features:** The API for this package is intentionally minimal and features that are outside of the scope of the core
Expand Down
1 change: 1 addition & 0 deletions demo/hello-world/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import HelloWorld from './HelloWorld.svelte';
svelteRetag({
component: HelloWorld,
tagname: 'hello-world',
attributes: true,
});
195 changes: 68 additions & 127 deletions demo/vercel/src/main.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import svelteRetag from '../../../index.js';

import './app.css';
import App from './App.svelte';
import Intro from './lib/Intro.svelte';
import Counter from './lib/Counter.svelte';
import ExampleTag from './lib/ExampleTag.svelte';
import { TabsWrapper, TabList, TabPanel, TabButton } from './lib/tabs';
import TabsDemo from './lib/TabsDemo.svelte';
import FeaturesInfo from './lib/FeaturesInfo.svelte';
import ExamplesInfo from './lib/ExamplesInfo.svelte';
import TabsInfo from './lib/TabsInfo.svelte';


/**
Expand All @@ -22,126 +13,76 @@ const shadow = !!params.get('shadow');
const shadowStylesheet = ''; // TODO: ISSUE-6: Find good solution for defining this. For now, hack is to inject style[data-vite-dev-id] into shadowRoot.


/**
* The definitions below are intentionally redundant and kept simple just to demonstrate syntax.
*
* This could easily be consolidated by wrapping your own wrapper function. For example, I might normally wrap this in
* a function like "defineTag(component, tag, attribs)".
*/

svelteRetag({
component: App,
tagname: 'app-tag',
attributes: ['pagetitle'],
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: Intro,
tagname: 'intro-tag',
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: FeaturesInfo,
tagname: 'features-info',
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: ExamplesInfo,
tagname: 'examples-info',
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: TabsInfo,
tagname: 'tabs-info',
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: Counter,
tagname: 'counter-tag',
attributes: ['count', 'award'],
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: ExampleTag,
tagname: 'example-tag',
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: TabPanel,
tagname: 'tab-panel',
hydratable,
debugMode,
shadow,
href: shadowStylesheet,
// Since we have a large number of components, we'll take advantage of Vite's glob import feature to automatically find
// and then define each svelteTag() component and derive the tagname from the filename (see autoDefine() function below).
// NOTE: This example uses eager loading, but you can also use lazy loading by removing the { eager: true } option.
autoDefine(import.meta.glob('./**/*.svelte', { eager: true }), {
// In case we encounter components that are only a single word, we must have a prefix or suffix to add.
suffix: 'tag',
}, () => {
// Extend with additional options on a per-tag basis, if desired. See usage below.
return {
debugMode,
hydratable,
shadow,
href: shadowStylesheet,
};
});


svelteRetag({
component: TabButton,
tagname: 'tab-button',
hydratable,
debugMode,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: TabsWrapper,
tagname: 'tabs-wrapper',
hydratable,
debugMode,
shadow,
href: shadowStylesheet,
});

svelteRetag({
component: TabList,
tagname: 'tab-list',
hydratable,
debugMode,
shadow,
href: shadowStylesheet,
});


/**
* Note: This is here for local testing and debugging only. This is just a demonstration of pure svelte-only context
* (without maintaining that context through svelte-retag).
*/

svelteRetag({
component: TabsDemo,
tagname: 'tabs-demo',
hydratable,
debugMode,
shadow,
href: shadowStylesheet,
});
// Usage: autoDefine(import.meta.glob('./**/*.svelte', { eager: true }), { prefix: 'my', suffix: 'tag' }, (tagname, component) => { return { attributes: true, shadow: false }; });
// WARNING: This autoDefine is experimental, and is not recommended for use in production yet. Especially for dynamic imports (i.e. without the eager option set).
// This is for demonstration purposes only, for now. Use at your own risk!

async function autoDefine(modules, {
prefix = '',
suffix = ''
} = {}, optionsCallback) {
// Force prefix to alphanumeric chars in case user accidentally included dashes or other characters.
prefix = prefix.toLowerCase().replace(/[^a-z0-9]/g, '');
for (const path in modules) {
let module = modules[path];

// Determine if this module was fetched asynchronously (returned as a callable function) or eagerly.
let component;
if (module instanceof Function) {
// TODO: Can be optimized by parallelling this execution. Right now this is called in order (one blocking the other).
// TODO: Alternatively, could
component = (await module()).default;
} else {
component = module.default;
}

// Break name up into words based on capitalized letters and join words with dashes and convert to lowercase so we can
// convert it to a tag name.
const name = path.match(/([^/]+)\.svelte$/)[1];
let words = name.match(/([A-Z][a-z]+)/g);

// If not using camel case, try to split on dashes or underscores and then failover to a default prefix (if still only one word).
if (!words) {
words = name.split(/[-_]/);
}
if (words.length === 1) {
words = [prefix, words[0], suffix].filter(Boolean); // Filter out empty strings.
}
const tagname = words.join('-').toLowerCase();

// Take additional options from callback.
const options = optionsCallback ? optionsCallback(tagname, component) : {};

// Go ahead and use svelte-retag to define tag now with inferred options (component and tag) and combine with customized options.
svelteRetag({
component,
tagname,

// Default to true, since this is likely to vary on a per-component basis. You can override this in the callback.
attributes: true,

// Merge customized options.
...options,
});
}

}
Loading