@_linked/react 0.0.1
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.
- package/.context/jest-repro-bundler.config.js +20 -0
- package/.context/jest-repro.config.js +20 -0
- package/.context/notes.md +0 -0
- package/.context/todos.md +0 -0
- package/.context/tsconfig-repro-bundler.json +14 -0
- package/.context/tsconfig-repro-no-paths.json +12 -0
- package/.context/tsconfig-repro-node-modules-paths.json +16 -0
- package/.context/tsconfig-repro-node16.json +14 -0
- package/AGENTS.md +59 -0
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/docs/001-react-extraction.md +361 -0
- package/jest.config.js +20 -0
- package/lib/cjs/index.d.ts +4 -0
- package/lib/cjs/index.js +21 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/package.d.ts +10 -0
- package/lib/cjs/package.js +33 -0
- package/lib/cjs/package.js.map +1 -0
- package/lib/cjs/package.json +3 -0
- package/lib/cjs/utils/Hooks.d.ts +5 -0
- package/lib/cjs/utils/Hooks.js +54 -0
- package/lib/cjs/utils/Hooks.js.map +1 -0
- package/lib/cjs/utils/LinkedComponent.d.ts +52 -0
- package/lib/cjs/utils/LinkedComponent.js +322 -0
- package/lib/cjs/utils/LinkedComponent.js.map +1 -0
- package/lib/cjs/utils/LinkedComponentClass.d.ts +11 -0
- package/lib/cjs/utils/LinkedComponentClass.js +34 -0
- package/lib/cjs/utils/LinkedComponentClass.js.map +1 -0
- package/lib/esm/index.d.ts +4 -0
- package/lib/esm/index.js +5 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/package.d.ts +10 -0
- package/lib/esm/package.js +22 -0
- package/lib/esm/package.js.map +1 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/utils/Hooks.d.ts +5 -0
- package/lib/esm/utils/Hooks.js +50 -0
- package/lib/esm/utils/Hooks.js.map +1 -0
- package/lib/esm/utils/LinkedComponent.d.ts +52 -0
- package/lib/esm/utils/LinkedComponent.js +284 -0
- package/lib/esm/utils/LinkedComponent.js.map +1 -0
- package/lib/esm/utils/LinkedComponentClass.d.ts +11 -0
- package/lib/esm/utils/LinkedComponentClass.js +27 -0
- package/lib/esm/utils/LinkedComponentClass.js.map +1 -0
- package/package.json +57 -0
- package/scripts/dual-package.js +25 -0
- package/src/index.ts +4 -0
- package/src/package.ts +62 -0
- package/src/tests/react-component-behavior.test.tsx +578 -0
- package/src/tests/react-component-integration.test.tsx +378 -0
- package/src/utils/Hooks.ts +56 -0
- package/src/utils/LinkedComponent.ts +545 -0
- package/src/utils/LinkedComponentClass.tsx +37 -0
- package/tsconfig-cjs.json +8 -0
- package/tsconfig-esm.json +8 -0
- package/tsconfig-test.json +15 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: 'ts-jest',
|
|
4
|
+
testEnvironment: 'jsdom',
|
|
5
|
+
rootDir: '../src/tests',
|
|
6
|
+
testMatch: ['**/*.test.ts', '**/*.test.tsx'],
|
|
7
|
+
transform: {
|
|
8
|
+
'^.+\\.(ts|tsx)$': [
|
|
9
|
+
'ts-jest',
|
|
10
|
+
{
|
|
11
|
+
tsconfig: '<rootDir>/../../.context/tsconfig-repro-bundler.json',
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
moduleNameMapper: {
|
|
16
|
+
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
17
|
+
'^@_linked/react/(.*)$': '<rootDir>/../$1',
|
|
18
|
+
'^@_linked/react$': '<rootDir>/../index',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
preset: 'ts-jest',
|
|
4
|
+
testEnvironment: 'jsdom',
|
|
5
|
+
rootDir: '../src/tests',
|
|
6
|
+
testMatch: ['**/*.test.ts', '**/*.test.tsx'],
|
|
7
|
+
transform: {
|
|
8
|
+
'^.+\\.(ts|tsx)$': [
|
|
9
|
+
'ts-jest',
|
|
10
|
+
{
|
|
11
|
+
tsconfig: '<rootDir>/../../.context/tsconfig-repro-node-modules-paths.json',
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
moduleNameMapper: {
|
|
16
|
+
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
17
|
+
'^@_linked/react/(.*)$': '<rootDir>/../$1',
|
|
18
|
+
'^@_linked/react$': '<rootDir>/../index',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "../",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@_linked/react": ["./src/index"],
|
|
9
|
+
"@_linked/react/*": ["./src/*"]
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"include": ["../src/**/*.ts", "../src/**/*.tsx"],
|
|
13
|
+
"exclude": []
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "../",
|
|
5
|
+
"paths": {
|
|
6
|
+
"@_linked/react": ["./src/index"],
|
|
7
|
+
"@_linked/react/*": ["./src/*"]
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"include": ["../src/tests/react-component-integration.test.tsx"],
|
|
11
|
+
"exclude": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "../",
|
|
5
|
+
"paths": {
|
|
6
|
+
"@_linked/core": ["./node_modules/@_linked/core/lib/esm/index.d.ts"],
|
|
7
|
+
"@_linked/core/*": ["./node_modules/@_linked/core/lib/esm/*"],
|
|
8
|
+
"@_linked/rdf-mem-store": ["./node_modules/@_linked/rdf-mem-store/lib/esm/index.d.ts"],
|
|
9
|
+
"@_linked/rdf-mem-store/*": ["./node_modules/@_linked/rdf-mem-store/lib/esm/*"],
|
|
10
|
+
"@_linked/react": ["./src/index"],
|
|
11
|
+
"@_linked/react/*": ["./src/*"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"include": ["../src/tests/react-component-integration.test.tsx"],
|
|
15
|
+
"exclude": []
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": "../",
|
|
5
|
+
"module": "Node16",
|
|
6
|
+
"moduleResolution": "Node16",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@_linked/react": ["./src/index"],
|
|
9
|
+
"@_linked/react/*": ["./src/*"]
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"include": ["../src/tests/react-component-integration.test.tsx"],
|
|
13
|
+
"exclude": []
|
|
14
|
+
}
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# AGENTS.md — @_linked/react repository
|
|
2
|
+
|
|
3
|
+
## Repository structure
|
|
4
|
+
|
|
5
|
+
Single-package repository for `@_linked/react` (React bindings such as `linkedComponent` and `linkedSetComponent`). Runtime dependency target is `@_linked/core`; tests may also use `@_linked/rdf-mem-store`.
|
|
6
|
+
|
|
7
|
+
Tests: `npm test`
|
|
8
|
+
|
|
9
|
+
## Agent docs (`docs/`)
|
|
10
|
+
|
|
11
|
+
Files are numbered with a 3-digit prefix for ordering. Names should be explicit about contents (lowercase-dash format). Every file starts with YAML frontmatter:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
---
|
|
15
|
+
summary: One-line description of what this document covers
|
|
16
|
+
packages: [core, react]
|
|
17
|
+
---
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
ls docs/ # list all docs
|
|
22
|
+
head -4 docs/*.md # get summaries (or replace * with a specific file)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Each package also has a `README.md` with API docs and a `## Changelog` at the bottom.
|
|
26
|
+
|
|
27
|
+
## Planning and implementation workflow
|
|
28
|
+
|
|
29
|
+
### When to plan
|
|
30
|
+
|
|
31
|
+
Any task that requires significant code changes requires a plan. Simple checks, info gathering, and discussions do not. If it's not clear (small code changes), ask the user if he wants to plan first.
|
|
32
|
+
|
|
33
|
+
### Creating a plan
|
|
34
|
+
|
|
35
|
+
1. **Inspect the relevant code thoroughly** before writing anything. Read the source files, tests, and existing docs that relate to the task.
|
|
36
|
+
2. Create a new doc in `docs/` with the next 3-digit prefix (e.g. `005-add-filter-support.md`). Start with YAML frontmatter.
|
|
37
|
+
3. Write the plan with these sections:
|
|
38
|
+
- **Key considerations and choices** — tradeoffs, open questions, alternatives
|
|
39
|
+
- **Potential problems** — what could go wrong, edge cases
|
|
40
|
+
- **Phases** — ordered list of implementation steps. Each phase has a clear scope and describes how it will be validated. Small tasks: 1-2 phases. Larger tasks: more.
|
|
41
|
+
4. **Ask the user to review the plan before implementing.**
|
|
42
|
+
|
|
43
|
+
### Implementing phases
|
|
44
|
+
|
|
45
|
+
- **One commit per phase.** Include the plan doc update (marking the phase complete) in the same commit.
|
|
46
|
+
- **Every phase must be validated** — at minimum one relevant passing test.
|
|
47
|
+
- **After each phase, report to the user:**
|
|
48
|
+
- What was done
|
|
49
|
+
- Any deviations from the plan
|
|
50
|
+
- Problems encountered
|
|
51
|
+
- Validation results (pass/fail counts and what was tested)
|
|
52
|
+
- What you plan to do next
|
|
53
|
+
|
|
54
|
+
### Wrapping up
|
|
55
|
+
|
|
56
|
+
Before committing final changes or preparing a PR:
|
|
57
|
+
|
|
58
|
+
1. **Consolidate the plan doc** — collapse alternatives into the choices that were made, summarize implementation details and breaking changes, keep a brief problems section if relevant, remove anything redundant for future readers.
|
|
59
|
+
2. **Update `## Changelog`** in each affected package's `README.md` — user-facing entry covering behavior changes, new APIs, breaking changes, and migration steps.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Semantu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# @_linked/react
|
|
2
|
+
|
|
3
|
+
React bindings for `@_linked/core`.
|
|
4
|
+
|
|
5
|
+
`@_linked/react` takes a Linked query from `@_linked/core`'s [Schema-Parameterized Query DSL](../core/README.md#schema-parameterized-query-dsl) and maps the top-level query result keys to props for a React component.
|
|
6
|
+
|
|
7
|
+
This package provides:
|
|
8
|
+
- `linkedComponent(...)`
|
|
9
|
+
- `linkedSetComponent(...)`
|
|
10
|
+
- `LinkedComponentClass`
|
|
11
|
+
- `useStyles(...)`
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @_linked/react @_linked/core react react-dom
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Setup package exports
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import {
|
|
25
|
+
linkedComponent,
|
|
26
|
+
linkedSetComponent,
|
|
27
|
+
linkedShape,
|
|
28
|
+
} from '@_linked/react';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### `linkedComponent(...)`
|
|
32
|
+
|
|
33
|
+
`linkedComponent(...)` wraps a React component with a Linked query. You pass a query built with `Shape.query(...)` (which prepares query execution), not `Shape.select(...)` (which executes immediately). At render time, when you pass `of={{id: ...}}`, the wrapper applies the prepared query to that subject and injects the query result keys as props into your component.
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
const PersonCard = linkedComponent(
|
|
37
|
+
Person.query((p) => p.name),
|
|
38
|
+
({name, source, _refresh}) => (
|
|
39
|
+
<article>
|
|
40
|
+
<h3>{name}</h3>
|
|
41
|
+
<small>{source.id}</small>
|
|
42
|
+
<button onClick={() => _refresh()}>Reload</button>
|
|
43
|
+
</article>
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// External API: pass `of` as a node reference (`{id: string}`), Shape, or QResult.
|
|
48
|
+
<PersonCard of={{id: 'https://example.org/p1'}} />;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Props received by the wrapped component:
|
|
52
|
+
- Query result props: all top-level keys from the query result become direct props (for example `name`).
|
|
53
|
+
- `source`: the resolved shape instance for the input `of` subject.
|
|
54
|
+
- `_refresh(updatedProps?)`: rerun the query (`_refresh()`) or patch local query-result props before rerender (`_refresh({...})`).
|
|
55
|
+
- Custom props: any additional props you pass to the linked component are forwarded as normal.
|
|
56
|
+
|
|
57
|
+
#### `_refresh(updatedProps?)` on linked components
|
|
58
|
+
|
|
59
|
+
`_refresh` is injected into wrapped `linkedComponent(...)` render functions.
|
|
60
|
+
|
|
61
|
+
- `_refresh()` reruns the query and rerenders when results return.
|
|
62
|
+
- `_refresh(updatedProps)` merges `updatedProps` into current query result state and rerenders immediately (without fetching first).
|
|
63
|
+
- `updatedProps` is for query result keys only (for example `name`, `active` from your query), not regular additional props passed by parents.
|
|
64
|
+
|
|
65
|
+
Example use case: optimistic UI after a mutation.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
const PersonCard = linkedComponent(
|
|
69
|
+
Person.query((p) => [p.name, p.active]),
|
|
70
|
+
({id, name, active, _refresh, title}) => (
|
|
71
|
+
<div>
|
|
72
|
+
<h4>{title}</h4>
|
|
73
|
+
<span>{name}</span>
|
|
74
|
+
<button
|
|
75
|
+
onClick={async () => {
|
|
76
|
+
// Patch query-result keys immediately (name/active/id/etc.)
|
|
77
|
+
_refresh({active: !active}); // optimistic local query-result update
|
|
78
|
+
await saveActiveFlag(id, !active); // your write call
|
|
79
|
+
_refresh(); // optional: sync with store response
|
|
80
|
+
// Not for parent custom props like `title`; those come from parent rerender.
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
Toggle active
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `linkedSetComponent(...)`
|
|
91
|
+
|
|
92
|
+
Use `linkedSetComponent(...)` when you want to render a list of sources.
|
|
93
|
+
|
|
94
|
+
### `linkedSetComponent(...)` (direct query format)
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
const NameList = linkedSetComponent(
|
|
98
|
+
Person.query((p) => p.name),
|
|
99
|
+
({linkedData}) => (
|
|
100
|
+
<ul>
|
|
101
|
+
{(linkedData || []).map((person) => (
|
|
102
|
+
<li key={person.id}>{person.name}</li>
|
|
103
|
+
))}
|
|
104
|
+
</ul>
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `linkedSetComponent(...)` (named data-prop format)
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
const personQuery = Person.query((p) => [p.name, p.hobby]);
|
|
113
|
+
|
|
114
|
+
const NameList = linkedSetComponent({persons: personQuery}, ({persons}) => (
|
|
115
|
+
<ul>
|
|
116
|
+
{persons.map((person) => (
|
|
117
|
+
<li key={person.id}>{person.name}</li>
|
|
118
|
+
))}
|
|
119
|
+
</ul>
|
|
120
|
+
));
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Both formats are supported. For linked-set wrappers, the external API is also `of` (optional). Internally this becomes `sources` for the wrapped component.
|
|
124
|
+
|
|
125
|
+
## Render lifecycle and loading state
|
|
126
|
+
|
|
127
|
+
When `LinkedStorage` is initialized and data is not already preloaded in `of`:
|
|
128
|
+
- First render: returns a loading element.
|
|
129
|
+
- Query resolves: component rerenders with mapped query result props.
|
|
130
|
+
- Source changes (`of` changes): prior query result is cleared and query runs again.
|
|
131
|
+
|
|
132
|
+
Loading fallback is currently fixed to:
|
|
133
|
+
|
|
134
|
+
```html
|
|
135
|
+
<div class="ld-loader" role="status" aria-label="Loading"></div>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
There is no API prop to replace this element today. You can style it via CSS class `.ld-loader`.
|
|
139
|
+
|
|
140
|
+
## Linked set pagination API
|
|
141
|
+
|
|
142
|
+
When `linkedSetComponent(...)` has a limit (explicit query limit or default limit), wrapped props include:
|
|
143
|
+
- `query.nextPage()`
|
|
144
|
+
- `query.previousPage()`
|
|
145
|
+
- `query.setPage(pageIndex)`
|
|
146
|
+
- `query.setLimit(limit)`
|
|
147
|
+
|
|
148
|
+
There is no public `setOffset(...)` in the React query controller; use `setPage`, `nextPage`, or `previousPage`.
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import React from 'react';
|
|
154
|
+
|
|
155
|
+
const PeopleList = linkedSetComponent(
|
|
156
|
+
Person.query((p) => [p.name]).limit(5),
|
|
157
|
+
({linkedData = [], query}) => {
|
|
158
|
+
const [page, setPage] = React.useState(0);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<section>
|
|
162
|
+
<ul>
|
|
163
|
+
{linkedData.map((person) => (
|
|
164
|
+
<li key={person.id}>{person.name}</li>
|
|
165
|
+
))}
|
|
166
|
+
</ul>
|
|
167
|
+
|
|
168
|
+
<div>
|
|
169
|
+
<button
|
|
170
|
+
onClick={() => {
|
|
171
|
+
query?.previousPage();
|
|
172
|
+
setPage((p) => Math.max(0, p - 1));
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
Previous
|
|
176
|
+
</button>
|
|
177
|
+
|
|
178
|
+
<span>Page {page + 1}</span>
|
|
179
|
+
|
|
180
|
+
<button
|
|
181
|
+
onClick={() => {
|
|
182
|
+
query?.nextPage();
|
|
183
|
+
setPage((p) => p + 1);
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
Next
|
|
187
|
+
</button>
|
|
188
|
+
|
|
189
|
+
<label>
|
|
190
|
+
Page size
|
|
191
|
+
<select
|
|
192
|
+
defaultValue="5"
|
|
193
|
+
onChange={(e) => {
|
|
194
|
+
const nextLimit = Number(e.target.value);
|
|
195
|
+
query?.setLimit(nextLimit);
|
|
196
|
+
query?.setPage(0);
|
|
197
|
+
setPage(0);
|
|
198
|
+
}}
|
|
199
|
+
>
|
|
200
|
+
<option value="5">5</option>
|
|
201
|
+
<option value="10">10</option>
|
|
202
|
+
<option value="25">25</option>
|
|
203
|
+
</select>
|
|
204
|
+
</label>
|
|
205
|
+
</div>
|
|
206
|
+
</section>
|
|
207
|
+
);
|
|
208
|
+
},
|
|
209
|
+
);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Notes
|
|
213
|
+
|
|
214
|
+
- This package depends on `@_linked/core` query APIs and `preloadFor(...)` / `BoundComponent` behavior from core.
|
|
215
|
+
- `@_linked/react` itself does not provide RDF storage; use a store package and set a default store in `LinkedStorage` (for example `@_linked/rdf-mem-store`).
|
|
216
|
+
|
|
217
|
+
## Storage setup (example: `@_linked/rdf-mem-store`)
|
|
218
|
+
|
|
219
|
+
For local in-memory setup, register `@_linked/rdf-mem-store` as the default store:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
import {LinkedStorage} from '@_linked/core';
|
|
223
|
+
import {InMemoryStore} from '@_linked/rdf-mem-store';
|
|
224
|
+
|
|
225
|
+
LinkedStorage.setDefaultStore(new InMemoryStore());
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## TODO
|
|
229
|
+
|
|
230
|
+
- Add `setOffset` to `linkedSetComponent` query controller.
|
|
231
|
+
- Make loader configurable and/or switch to passing a loading-state prop.
|
|
232
|
+
|
|
233
|
+
## Development
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm run build
|
|
237
|
+
npm test
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Changelog
|
|
241
|
+
|
|
242
|
+
### 1.0.0 (from LINCD.js)
|
|
243
|
+
|
|
244
|
+
Initial extraction from the LINCD monolith. Moves React-specific linked component wrappers into a standalone package.
|
|
245
|
+
|
|
246
|
+
- `linkedComponent(...)` and `linkedSetComponent(...)` extracted from `lincd`.
|
|
247
|
+
- `LinkedComponentClass` base class for class-based linked components.
|
|
248
|
+
- `useStyles(...)` hook for component styling.
|
|
249
|
+
- Pagination API (`nextPage`, `previousPage`, `setPage`, `setLimit`) on linked set components.
|
|
250
|
+
- `_refresh(updatedProps?)` for optimistic UI updates on linked components.
|