Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaylorhq committed Nov 27, 2024
1 parent 5a7a240 commit 8c8b823
Show file tree
Hide file tree
Showing 24 changed files with 8,991 additions and 7,008 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/push-dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
with:
branch: dist
token: ${{ secrets.GITHUB_TOKEN }}
working-directory: 'ember-curried-invokables'
working-directory: 'ember-curry-component'
1 change: 0 additions & 1 deletion .prettierrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

module.exports = {
plugins: ['prettier-plugin-ember-template-tag'],
singleQuote: true,
};
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Installation

- `git clone <repository-url>`
- `cd ember-curried-invokables`
- `cd ember-curry-component`
- `pnpm install`

## Linting
Expand All @@ -13,7 +13,7 @@

## Building the addon

- `cd ember-curried-invokables`
- `cd ember-curry-component`
- `pnpm build`

## Running tests
Expand Down
113 changes: 104 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,116 @@
# ember-curried-invokables
# ember-curry-component

[Short description of the addon.]

## Compatibility

- Ember.js v4.12 or above
- Embroider or ember-auto-import v2
Like Ember's builtin `(component)` helper, but with dynamic arguments, and JS compatibility.

## Installation

```
ember install ember-curried-invokables
ember install ember-curry-component
```

## Usage

[Longer description of how to use the addon in apps.]
### Simple static arguments

```gjs
import Component from "@glimmer/component";
import { getOwner } from "@ember/owner";
import { curryComponent } from "ember-curry-component";
import SomeOtherComponent from "./some-other-component";
class extends MyComponent {
get curriedComponent(){
const args = { name: "David" };
return curryComponent(SomeOtherComponent, args, getOwner(this))
}
<template>
<this.curriedComponent />
</template>
}
```

### Reactive arguments (option 1: per-argument reactivity)

```gjs
import Component from "@glimmer/component";
import { getOwner } from "@ember/owner";
import { curryComponent } from "ember-curry-component";
import SomeOtherComponent from "./some-other-component";
class extends MyComponent {
@tracked name = "David";
get curriedComponent() {
const args = { get name() { return this.name } };
return curryComponent(SomeOtherComponent, args, getOwner(this));
}
<template>
<this.curriedComponent />
</template>
}
```
When `this.name` is reassigned, the `@name` argument on the curried component will be invalidated. The getter will not be re-evaluated.

### Reactive arguments (option 2: rerender entire component)

```gjs
import Component from "@glimmer/component";
import { getOwner } from "@ember/owner";
import { curryComponent } from "ember-curry-component";
import SomeOtherComponent from "./some-other-component";
class extends MyComponent {
@tracked name = "David";
get curriedComponent(){
const args = { name: this.name };
return curryComponent(SomeOtherComponent, args, getOwner(this))
}
<template>
<this.curriedComponent />
</template>
}
```
When `this.name` is reassigned, the getter will be invalidated, and the curried component will be completely rerendered.

### As a helper

In `.gjs`/`.gjs` files, the curryComponent helper can be used directly in a template. In this case, the owner does not need to be passed explicitly.

```gjs
import SomeOtherComponent from "./some-other-component";
const args = { name: "david" }
<template>
{{#let (curryComponent MyComponent args) as |curriedComponent|}}
<curriedComponent />
{{/let}}
</templates>
```

### Caveats

In `<template>`, curried components cannot be rendered from the local scope. This will fail:

```gjs
// Do not copy!
const curried = curryComponent(MyComponent, args, owner)
<template><curried /></template>
```
You must wrap the invocation in `{{#let}}` instead:
```gjs
// Do not copy!
const curried = curryComponent(MyComponent, args, owner)
<template>
{{#let curried as |myComponent|}}
<myComponent />
{{/let}}
</template>
```

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion config/ember-cli-update.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaVersion": "1.0.0",
"projectName": "ember-curried-invokables",
"projectName": "ember-curry-component",
"packages": [
{
"name": "@embroider/addon-blueprint",
Expand Down
Empty file.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "ember-curried-invokables",
"version": "0.0.0",
"description": "The default blueprint for Embroider v2 addons.",
"name": "ember-curry-component",
"version": "0.1.0",
"description": "Like Ember's builtin `(component)` helper, but with dynamic arguments, and JS compatibility",
"keywords": [
"ember-addon"
],
"repository": "",
"repository": "https://github.com/davidtaylorhq/ember-curry-component",
"license": "MIT",
"author": "",
"exports": {
Expand Down Expand Up @@ -64,6 +64,12 @@
"version": 2,
"type": "addon",
"main": "addon-main.cjs",
"app-js": {}
"app-js": {},
"externals": [
"@glimmer/reference",
"@glimmer/runtime",
"@glimmer/util",
"@glimmer/vm"
]
}
}
File renamed without changes.
33 changes: 33 additions & 0 deletions ember-curry-component/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createComputeRef, createConstRef } from '@glimmer/reference';
import { createCapturedArgs, curry, EMPTY_POSITIONAL } from '@glimmer/runtime';
import { dict } from '@glimmer/util';
import * as vm from '@glimmer/vm';

// CurriedType only made available in vm from Ember 5.6.0. Fallback to hardcoded value.
const ComponentCurriedType = vm.CurriedType?.Component || 0;

export default function curryComponent(componentKlass, namedArgs, owner) {
let namedDict = dict();

if (!(typeof namedArgs === 'object' && namedArgs.constructor === Object)) {
throw 'Named arguments must be a simple object';
}

for (const [key, descriptor] of Object.entries(
Object.getOwnPropertyDescriptors(namedArgs),
)) {
if (descriptor.get) {
namedDict[key] = createComputeRef(() => namedArgs[key]);
} else {
namedDict[key] = createConstRef(namedArgs[key]);
}
}

return curry(
ComponentCurriedType,
componentKlass,
owner,
createCapturedArgs(namedDict, EMPTY_POSITIONAL),
false,
);
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"license": "MIT",
"author": "",
"scripts": {
"build": "pnpm --filter ember-curried-invokables build",
"build": "pnpm --filter ember-curry-component build",
"lint": "pnpm --filter '*' lint",
"lint:fix": "pnpm --filter '*' lint:fix",
"prepare": "pnpm build",
"start": "concurrently 'pnpm:start:*' --restart-after 5000 --prefix-colors cyan,white,yellow",
"start:addon": "pnpm --filter ember-curried-invokables start --no-watch.clearScreen",
"start:addon": "pnpm --filter ember-curry-component start --no-watch.clearScreen",
"start:test-app": "pnpm --filter test-app start",
"test": "pnpm --filter '*' test",
"test:ember": "pnpm --filter '*' test:ember"
Expand Down
Loading

0 comments on commit 8c8b823

Please sign in to comment.