`
25 |
26 | The value of the text field.
27 |
28 | ## Param: `label`
29 |
30 |
31 |
32 | **type**: `string`
33 |
34 | > ↑ `Content` if HTML label is supported.
35 |
36 | The label of the text field.
37 |
38 | ## Param: `placeholder`
39 |
40 |
41 |
42 | **type**: `string`
43 |
44 | The placeholder of the text field.
45 |
46 | > May be combined with `label`.
47 |
48 | ## Param: `disabled`
49 |
50 | = `false`
51 |
52 | **type**: `boolean`
53 |
54 | Whether the text field is disabled.
55 |
56 | ## Triggered when: `input`
57 |
58 | **data type**: `string`
59 |
60 | When the value of the text field is changed, the new value will be emitted.
61 |
--------------------------------------------------------------------------------
/docs/tutorial/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | page: true
3 | title: Tutorial
4 | sidebar: false
5 | aside: false
6 | footer: false
7 | returnToTop: false
8 | ---
9 |
10 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-1/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | $app([Basics], _ => {
5 | _.h1("👋 Hello, Refina!");
6 | });
7 |
8 | declare module "refina" {
9 | interface Plugins {
10 | Basics: typeof Basics;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-10/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | import JSConfetti from "js-confetti";
4 |
5 | const confetti = new JSConfetti();
6 | confetti.addConfetti();
7 |
8 | $app([Basics], _ => {
9 | _.$css`
10 | all: unset;
11 | font-size: xx-large;
12 | font-weight: bold;
13 | text-align: center;
14 | margin: 3em 30%;`;
15 | _.button("🎉 Congratulations!") && confetti.addConfetti();
16 | });
17 |
18 | declare module "refina" {
19 | interface Plugins {
20 | Basics: typeof Basics;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-10/description.md:
--------------------------------------------------------------------------------
1 | # You Did It!
2 |
3 | You have finished the tutorial!
4 |
5 | At this point, you should have a good idea of what it's like to work with Refina. However, we covered a lot of things really fast and glossed over the details, so definitely keep learning! As a next step, you can:
6 |
7 | - Set up a real Refina project on your machine by following the [Quick Start](../../../guide/quick-start).
8 | - Go through the [Main Guide](../../../guide/essentials/application), which covers all the topics we learned so far in greater details, and much more.
9 |
10 | We can't wait to see what you build next!
11 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-10/import-map.json:
--------------------------------------------------------------------------------
1 | {
2 | "imports": {
3 | "js-confetti": "https://cdn.jsdelivr.net/npm/js-confetti/+esm"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-2/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | // Build your app here
5 |
6 | declare module "refina" {
7 | interface Plugins {
8 | // Add your plugins here
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-2/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | $app([Basics], _ => {
5 | _.h1("Hello, world!");
6 | _.p(_ => {
7 | _.t`This is a `;
8 | _.a("link", "https://refina.dev");
9 | _.t`.`;
10 | });
11 | });
12 |
13 | declare module "refina" {
14 | interface Plugins {
15 | Basics: typeof Basics;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-3/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | $app([Basics], _ => {
5 | _.h1("Make me styled!");
6 | });
7 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-3/App/styles.css:
--------------------------------------------------------------------------------
1 | .my-class {
2 | /* Add your CSS here */
3 | }
4 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-3/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | $app([Basics], _ => {
5 | _.$cls`my-class`;
6 | _.$css`color:red;font-size:2rem`;
7 | _.h1("Make me styled!");
8 | });
9 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-3/_hint/App/styles.css:
--------------------------------------------------------------------------------
1 | .my-class {
2 | text-transform: uppercase;
3 | }
4 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-3/description.md:
--------------------------------------------------------------------------------
1 | # Add Styles and Class Names
2 |
3 | In the previous step, we learned how to render components using some functions in the context object, which are called **component functions**. There are also **directives** in the context object. In this step, we will learn how to use two directives: `_.$cls` and `_.$css`.
4 |
5 | `_.$cls` is used to add class names to the next component. It can be used both as a function and a tag function:
6 |
7 | ```ts
8 | _.$cls`foo`;
9 | _.div();
10 | //
11 | ```
12 |
13 | `_.$css` is used to add styles to the next component. It works the same as `_.$cls`:
14 |
15 | ```ts
16 | _.$css`color:red`;
17 | _.div();
18 | //
19 | ```
20 |
21 | Also, you can use `_.$cls` and `_.$css` together, and for multiple times:
22 |
23 | ```ts
24 | _.$cls`foo`;
25 | _.$cls`bar`;
26 | _.$css`color:red`;
27 | _.div();
28 | //
29 | ```
30 |
31 | Now let's try to add some styles and class names to the `` element on the right.
32 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-4/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | // Should declare states here?
5 |
6 | $app([Basics], _ => {
7 | // Or here?
8 |
9 | _.p("Count is 0!");
10 | });
11 |
12 | declare module "refina" {
13 | interface Plugins {
14 | Basics: typeof Basics;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-4/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | let count = 0;
5 |
6 | $app([Basics], _ => {
7 | // Not here!
8 |
9 | _.p(`Count is ${count}!`);
10 | });
11 |
12 | declare module "refina" {
13 | interface Plugins {
14 | Basics: typeof Basics;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-4/description.md:
--------------------------------------------------------------------------------
1 | # Using States
2 |
3 | States in Refina are just plain JavaScript values. You can use them to store data and render dynamic content.
4 |
5 | ```ts
6 | let message = "Hello World!";
7 | let person = {
8 | id: 1,
9 | name: "John Doe",
10 | };
11 |
12 | $app([Basics], _ => {
13 | _.h1(message);
14 | _.p(`Hello ${person.name}!`);
15 | });
16 | ```
17 |
18 | :::warning States should be declared outside the main function.
19 |
20 | Variables declared inside the main function are not states. They are just local temporary variables, which are re-created every time the main function is called.
21 |
22 | :::
23 |
24 | Just like in JSX, you can render components conditionally using `if` statements or other operators:
25 |
26 | ```ts
27 | let cond = true;
28 | let value: number | null | undefined;
29 |
30 | $app([Basics], _ => {
31 | if (cond) {
32 | _.h1("Hello World!");
33 | } else {
34 | _.h1("Hello Refina!");
35 | }
36 |
37 | cond && _.p("cond is truthy.");
38 | value === 1 && _.p("value is 1.");
39 | value ?? _.p("value is null or undefined.");
40 | });
41 | ```
42 |
43 | Now, try to create a "count" state yourself, and use it to render the content of `_.p`.
44 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-5/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | $app([Basics], _ => {
5 | _.h1(`You have clicked the button 0 times.`);
6 | _.button("Click me!");
7 | });
8 |
9 | declare module "refina" {
10 | interface Plugins {
11 | Basics: typeof Basics;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-5/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | let count = 0;
5 |
6 | $app([Basics], _ => {
7 | _.h1(`You have clicked the button ${count} times.`);
8 | if (_.button("Click me!")) {
9 | count++;
10 | }
11 | });
12 |
13 | declare module "refina" {
14 | interface Plugins {
15 | Basics: typeof Basics;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-6/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | let name = "";
5 |
6 | $app([Basics], _ => {
7 | if (_.textInput(name)) {
8 | name = _.$ev;
9 | }
10 | _.p(`Hello ${name}!`);
11 | });
12 |
13 | declare module "refina" {
14 | interface Plugins {
15 | Basics: typeof Basics;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-6/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app, model } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | const name = model("");
5 |
6 | $app([Basics], _ => {
7 | _.textInput(name);
8 | if (name.value.length > 0) {
9 | _.button("Clear") && (name.value = "");
10 | }
11 | _.p(`Hello ${name}!`);
12 | });
13 |
14 | declare module "refina" {
15 | interface Plugins {
16 | Basics: typeof Basics;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-7/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app, model } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | let id = 0;
5 | let newTodo = model("");
6 |
7 | let todos = [
8 | { id: id++, text: "Learn HTML" },
9 | { id: id++, text: "Learn JavaScript" },
10 | { id: id++, text: "Learn Refina" },
11 | ];
12 |
13 | function remove(id: number) {
14 | todos = todos.filter(todo => todo.id !== id);
15 | }
16 |
17 | $app([Basics], _ => {
18 | _.textInput(newTodo);
19 | _.button("Add Todo") && todos.push({ id: id++, text: newTodo.value });
20 |
21 | // Finish the following code to render the list of todos.
22 | // _.ul(todos, ...);
23 | });
24 |
25 | declare module "refina" {
26 | interface Plugins {
27 | Basics: typeof Basics;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-7/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app, model } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | let id = 0;
5 | let newTodo = model("");
6 |
7 | let todos = [
8 | { id: id++, text: "Learn HTML" },
9 | { id: id++, text: "Learn JavaScript" },
10 | { id: id++, text: "Learn Refina" },
11 | ];
12 |
13 | function remove(id: number) {
14 | todos = todos.filter(todo => todo.id !== id);
15 | }
16 |
17 | $app([Basics], _ => {
18 | _.textInput(newTodo);
19 | _.button("Add Todo") && todos.push({ id: id++, text: newTodo.value });
20 |
21 | _.ul(todos, "id", item =>
22 | _.li(_ => {
23 | _.span(item.text);
24 | _.button("❌") && remove(item.id);
25 | }),
26 | );
27 | });
28 |
29 | declare module "refina" {
30 | interface Plugins {
31 | Basics: typeof Basics;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-7/description.md:
--------------------------------------------------------------------------------
1 | # List Rendering
2 |
3 | We can use the `_.for` component to render a list of elements based on a source array:
4 |
5 | ```ts
6 | const todos = [
7 | { id: 1, text: "Buy milk" },
8 | { id: 2, text: "Do homework" },
9 | { id: 3, text: "Read a book" },
10 | ];
11 |
12 | $app([Basics], _ => {
13 | _.for(todos, "id", item => {
14 | _.p(item.text);
15 | });
16 | });
17 | ```
18 |
19 | Notice how we are also giving each todo object a unique key via the second parameter of `_.for`. The key allows Refina to accurately move each `
` to match the position of its corresponding object in the array.
20 |
21 | The key can be an object key of each item in the list, or a function that returns a unique key for each item. `bySelf` and `byIndex` are two built-in functions that can be used as the key.
22 |
23 | Some components have the same effect as `_.for`, e.g. `_.ul` and `_.ol`.
24 |
25 | Now, let's finish the todo manager.
26 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-8/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | $app([Basics], _ => {
5 | _.h1("Title");
6 | _.div(_ => {
7 | _.h2("Part 1");
8 | _.p("Content 1");
9 | _.a("share", "#");
10 | });
11 | _.div(_ => {
12 | _.h2("Part 2");
13 | _.p("Content 2");
14 | _.a("share", "#");
15 | });
16 | _.div(_ => {
17 | _.h2("Part 3");
18 | _.p("Content 3");
19 | _.a("share", "#");
20 | });
21 | _.div(_ => {
22 | _.h2("Part 4");
23 | _.p("Content 4");
24 | _.a("share", "#");
25 | });
26 | });
27 |
28 | declare module "refina" {
29 | interface Plugins {
30 | Basics: typeof Basics;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-8/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | import PartView from "./PartView";
4 |
5 | $app([Basics], _ => {
6 | _.h1("Title");
7 | _(PartView)("Part 1", "Content 1");
8 | _(PartView)("Part 2", "Content 2");
9 | _(PartView)("Part 3", "Content 3");
10 | _(PartView)("Part 4", "Content 4");
11 | });
12 |
13 | declare module "refina" {
14 | interface Plugins {
15 | Basics: typeof Basics;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-8/_hint/App/PartView.ts:
--------------------------------------------------------------------------------
1 | import { $view, Content, _ } from "refina";
2 |
3 | export default $view((title: string, content: Content) => {
4 | _.h1(title);
5 | _.p(content);
6 | });
7 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-8/description.md:
--------------------------------------------------------------------------------
1 | # Views
2 |
3 | A view is a function that renders a part of the page.
4 |
5 | It is used to split the page into multiple parts, which can be rendered separately, and reused.
6 |
7 | To define a view in Refina, you can use the `$view` function:
8 |
9 | ```ts
10 | import { $view, _ } from "refina";
11 |
12 | export default $view((id: number) => {
13 | _.h1(`Card ${id}`);
14 | });
15 | ```
16 |
17 | To use a view, just call the context object:
18 |
19 | ```ts
20 | import { $app } from "refina";
21 | import CardView from "./CardView";
22 |
23 | $app([], _ => {
24 | _(CardView)("1");
25 | _(CardView)("2");
26 | _(CardView)("3");
27 | });
28 | ```
29 |
30 | Now, let's try to use views to extract the duplicated code into a view, and use the view to render the content.
31 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-9/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 |
3 | /*
4 | Rewrite the following HTML using low-level rendering functions.
5 |
6 |
10 | */
11 |
12 | $app([], _ => {});
13 |
--------------------------------------------------------------------------------
/docs/tutorial/src/step-9/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 |
3 | /*
4 | Rewrite the following HTML using low-level rendering functions.
5 |
6 |
10 | */
11 |
12 | $app([], _ => {
13 | _.$css`position:fixed;`;
14 | _._svgSvg(
15 | {
16 | id: "svg1",
17 | width: 100,
18 | height: 100,
19 | },
20 | _ => {
21 | _._svgPath({
22 | d: "M 10 10 H 90 V 90 H 10 Z",
23 | fill: "red",
24 | });
25 | _._svgCircle({
26 | cx: 50,
27 | cy: 50,
28 | r: 40,
29 | stroke: "blue",
30 | fill: "none",
31 | });
32 | },
33 | );
34 | });
35 |
--------------------------------------------------------------------------------
/docs/tutorial/tutorial.data.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import { createMarkdownRenderer } from "vitepress";
3 | import { readExamples } from "../helpers/loader";
4 | import type { ExampleData } from "../helpers/utils";
5 |
6 | export declare const data: Record;
7 |
8 | export default {
9 | watch: "./src/**",
10 | async load() {
11 | const md = await createMarkdownRenderer(process.cwd(), undefined, "/");
12 | const files = readExamples(path.resolve(__dirname, "./src"));
13 | for (const step in files) {
14 | const stepFiles = files[step];
15 | const desc = stepFiles["description.md"] as string;
16 | if (desc) {
17 | stepFiles["description.md"] = md.render(desc);
18 | }
19 | }
20 | return files;
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/docs/zh/README.md:
--------------------------------------------------------------------------------
1 | # Refina.js Docs
2 |
3 | Visit the docs at https\://refina.vercel.app/ and https\://refinajs.github.io/refina.
4 |
5 | Some of the code and content are borrowed from [Vue.js docs](https://github.com/vuejs/docs). Thanks to their great work!
6 |
--------------------------------------------------------------------------------
/docs/zh/guide/apis/app-hooks.md:
--------------------------------------------------------------------------------
1 | # 钩子
2 |
3 | 钩子是在特定时机被调用的回调函数。
4 |
5 | 钩子可由插件添加,也可以编程式地注册。
6 |
7 | ## 一次性钩子
8 |
9 | 一次性钩子在调用一次后会被移除。
10 |
11 | 调用 `_.$app.pushOnetimeHook` 以添加一次性钩子。
12 |
13 | 一次性钩子可以通过 `_.$app.onetimeHooks` 访问。
14 |
15 | ## 永久钩子
16 |
17 | 永久钩子在被调用后不会被移除。
18 |
19 | 调用 `_.$app.pushPermanentHook` 以添加永久钩子。
20 |
21 | 永久钩子可以通过 `_.$app.permanentHooks` 访问。
22 |
23 | ## `initContext` 钩子
24 |
25 | 在该钩子中初始化上下文对象。
26 |
27 | ## `beforeMain` 钩子
28 |
29 | 在应用的主函数被调用之前调用,无论处于 `UPDATE` 或 `RECV` 状态下。
30 |
31 | :::tip
32 |
33 | `app.runtimeData` 在此钩子中可用。
34 |
35 | :::
36 |
37 | ## `afterMain` 钩子
38 |
39 | 在应用的主函数被调用之后调用,无论处于 `UPDATE` 或 `RECV` 状态下。
40 |
41 | :::tip
42 |
43 | `app.runtimeData` 在此钩子中可用。
44 |
45 | :::
46 |
47 | ## `beforeModifyDOM` 钩子
48 |
49 | 在应用主函数在 `UPDATE` 状态下的调用完成后、在更新 DOM 的树结构、设置类名与样式之前调用。
50 |
51 | :::tip
52 |
53 | `app.runtimeData` 在此钩子中可用。
54 |
55 | :::
56 |
57 | ## `afterModifyDOM` 钩子
58 |
59 | 在应用主函数在 `UPDATE` 状态下的调用完成并且完成更新 DOM 的树结构、设置类名与样式之后调用。
60 |
61 | :::tip
62 |
63 | `app.runtimeData` 在此钩子中可用。
64 |
65 | :::
66 |
67 | ## `onError` 钩子
68 |
69 | 当主函数运行时抛出未捕获的错误时调用。
70 |
71 | :::info
72 |
73 | 一个将错误输出至控制台的钩子已经由 `Prelude` 插件自动添加。
74 |
75 | :::
76 |
--------------------------------------------------------------------------------
/docs/zh/guide/essentials/app-state.md:
--------------------------------------------------------------------------------
1 | # 应用状态
2 |
3 | 应用实例总是处于以下3个状态中的一个:
4 |
5 | - `IDLE`: 主函数不在运行。
6 | - `UPDATE`: 渲染并更新页面中。
7 | - `RECV`: 接收并处理事件中。
8 |
9 | 
10 |
11 | ## `IDLE` 状态
12 |
13 | 当 `UPDATE` 状态结束后,应用进入 `IDLE` 状态。
14 |
15 | ## `UPDATE` 状态
16 |
17 | 有三种方式会使得应用进入 `UPDATE` 状态:
18 |
19 | - 应用被挂载(初次创建)。
20 | - 应用实例的 `update` 方法被调用。
21 | - 事件队列被清空。
22 |
23 | 在 `UPDATE` 状态下,应用将生成 DOM tree 并更新页面。
24 |
25 | :::tip
26 |
27 | 多个 `UPDATE` 调用请求将被合并成一个。
28 |
29 | :::
30 |
31 | :::danger
32 |
33 | 不应当在 `UPDATE` 状态下改变变量。
34 |
35 | 以下代码是错误的。它会造成未定义行为。
36 |
37 | ```ts
38 | let count = 0;
39 | $app([Basics], _ => {
40 | _.p(`Count is: ${count}`);
41 | count++; // count 在 UPDATE 状态下也会被改变
42 | });
43 | ```
44 |
45 | :::
46 |
47 | ## `RECV` 状态
48 |
49 | 当侦听到事件时,应用进入 `RECV` 状态。
50 |
51 | 只有在这个状态下,事件型组件的返回值可能是真值(当它就是事件的接收者时)。
52 |
53 | 当 `RECV` 状态结束后,应用会进入 `UPDATE` 状态以更新页面。
54 |
55 | :::danger
56 |
57 | 不能在 `RECV` 状态下修改 DOM。
58 |
59 | 以下代码是错误的。它会造成未定义行为。
60 |
61 | ```ts
62 | $app([Basics], _ => {
63 | if (_.button("Click me")) {
64 | _.p("Hello");
65 | }
66 | });
67 | ```
68 |
69 | :::
70 |
--------------------------------------------------------------------------------
/docs/zh/guide/essentials/application.md:
--------------------------------------------------------------------------------
1 | # 创建一个 Refina 应用
2 |
3 | ## 应用实例
4 |
5 | 每个 Refina 应用通过调用 `$app` 函数创建:
6 |
7 | ```ts
8 | import { $app } from "refina";
9 |
10 | $app([], _ => {
11 | // 应用主体 (主函数)
12 | // ...
13 | });
14 | ```
15 |
16 | ## 使用插件
17 |
18 | 所有组件和工具函数都通过插件提供。所以插件是必不可少的一部分。
19 |
20 | `$app` 的第一个参数可以是插件列表。
21 |
22 | ```ts
23 | $app([Plugin1, Plugin2(param1, param2), Plugin3], _ => {
24 | // ...
25 | });
26 | ```
27 |
28 | 但是,TypeScript 并不知道有哪些插件被使用,除非显式地声明它们 :
29 |
30 | ```ts
31 | declare module "refina" {
32 | interface Plugins {
33 | Plugin1: typeof Plugin1;
34 | Plugin2: typeof Plugin2;
35 | Plugin3: typeof Plugin3;
36 | }
37 | }
38 | ```
39 |
40 | 事实上,属于Refina核心的组件和工具函数由名为 `Prelude` 插件的提供。这个插件在创建应用时会被自动添加。
41 |
42 | ## 主函数
43 |
44 | 主函数是页面的主体,负责构建页面和处理事件。
45 |
46 | 不但应用(`App`)有主函数,每个视图(`View`)和组件(`Component`)也有主函数。
47 |
48 | 主函数的第一个参数是上下文对象。通过上下文对象可以进行渲染组件、处理事件等等操作。
49 |
50 | :::warning
51 |
52 | 必须将上下文对象(即第一个参数)命名为 `_`。
53 |
54 | 否则,编译时转换将不会工作,并将产生运行时错误。
55 |
56 | :::
57 |
58 | ## 根元素
59 |
60 | 根元素是应用挂载在 DOM 中的容器元素。
61 |
62 | 根元素默认是 `"#app"`。
63 |
64 | 你可以用 `root` 选项自定义根元素。
65 |
66 | ```ts
67 | $app(
68 | { plugins: [], root: "#my-root" },
69 | _ => {
70 | // ...
71 | },
72 | "my-root",
73 | );
74 | ```
75 |
--------------------------------------------------------------------------------
/docs/zh/guide/essentials/conditional.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # 条件渲染
6 |
7 | 就像 JSX 中一样,你可以直接使用普通的 `if-else` 语句来根据条件决定是否渲染元素。
8 |
9 | ```ts
10 | let count = 0;
11 |
12 | $app([Basics], _ => {
13 | _.p(`Count is: ${count}`);
14 |
15 | _.button(`Add`) && count++;
16 |
17 | if (count > 0) {
18 | _.button("Reset") && (count = 0);
19 | }
20 | });
21 | ```
22 |
23 | **运行结果**
24 |
25 |
26 |
27 | :::tip
28 |
29 | 可以使用 `&&` 运算符来根据条件渲染组件:
30 |
31 | ```ts
32 | input.length > 0 && _.button("Clear");
33 | ```
34 |
35 | :::
36 |
--------------------------------------------------------------------------------
/docs/zh/guide/essentials/context.md:
--------------------------------------------------------------------------------
1 | # 上下文对象
2 |
3 | Refina 的很多 API 通过上下文对象提供。
4 |
5 | 上下文对象被用于渲染组件、处理事件,等等。
6 |
7 | 上下文对象有3类成员:
8 |
9 | - **组件函数**:调用它们以渲染组件/元素。
10 | - **工具函数**:一些实用工具,并不渲染组件/元素。
11 | - **指令**:一些特殊的方法与属性。
12 |
13 | :::warning
14 |
15 | 如果你想使用除了指令外的上下文对象的属性,你必须将上下文对象命名为 `_`。
16 |
17 | 否则,编译时转换将不会工作,并将产生运行时错误。
18 |
19 | :::
20 |
21 | ## 组件函数
22 |
23 | 渲染组件的唯一方法是调用其组件函数。
24 |
25 | 有3种组件函数:
26 |
27 | 1. **文本节点**:即`_.t`。
28 | 2. **底层元素**:原始的 DOM 元素,名称有 `_` 作为前缀,如 `_._div` 和 `_._svgPath`。
29 | 3. **插件提供的组件函数**:比如由 `Basics` 插件提供的 `_.button` 、由 `MdUI` 插件提供的 `_.mdButton`。 它们的名称不含有 `_` 前缀。 它们的名称不含有 `_` 前缀。
30 |
31 | ## 工具函数
32 |
33 | 这些函数作为工具被使用,比如用来控制渲染顺序、设置定时器等等。
34 |
35 | 它们的名称也不含有 `_` 前缀。
36 |
37 | 由 Refina 核心提供的使用函数参见 [Utility Context Functions](/guide/apis/util-funcs.md)。
38 |
39 | ## 指令
40 |
41 | 指令是上下文对象上一些特殊的属性与方法。
42 |
43 | 它们的名称都有 `$` 前缀。并且它们不会经过编译时转换。
44 |
45 | 由 Refina 核心提供的指令参见 [Directives](/guide/apis/directives.md)。
46 |
--------------------------------------------------------------------------------
/docs/zh/guide/essentials/list.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # 列表渲染
7 |
8 | 就像在 Vue.js 中一样,在渲染列表时需要为每个元素指定唯一的 `key`。
9 |
10 | 因此,不可以使用普通的循环来渲染列表。因为它们不接收 `key`。
11 |
12 | 需要使用 `_.for` 或 `_.forTimes` 等上下文你函数来渲染列表。
13 |
14 | ```ts
15 | import { bySelf } from "refina";
16 |
17 | const items = ["Apple", "Banana", "Orange"];
18 |
19 | $app([Basics], _ => {
20 | _.for(items, bySelf, item => {
21 | _.p(item);
22 | });
23 | });
24 | ```
25 |
26 | **运行结果**
27 |
28 |
29 |
30 | ## key 生成器 {#key-generator}
31 |
32 | `_.for` 的第二个参数是一个 key 生成器,用于为每个元素提供一个唯一的 key。
33 |
34 | key 生成器可以是一个形式为 `(item, index) => key` 的函数,也可以是被作为 key 的元素属性的属性名。
35 |
36 | Refina 提供了两个 key 生成器:
37 |
38 | - `bySelf`: 将元素本身作为 key。
39 | - `byIndex`: 将元素的索引作为 key。
40 |
41 | ## 重复一定次数 {#for-times}
42 |
43 | 可以使用 `_.forTimes` 来将渲染重复数次。
44 |
45 | `_.forTimes` 没有 key 生成器。它将索引作为 key。
46 |
47 | ```ts
48 | $app([Basics], _ => {
49 | _.forTimes(5, index => {
50 | _.p(`This is the ${index + 1}th paragraph.`);
51 | });
52 | });
53 | ```
54 |
55 | **运行结果**
56 |
57 |
58 |
--------------------------------------------------------------------------------
/docs/zh/guide/why.md:
--------------------------------------------------------------------------------
1 | # 为什么选 Refina
2 |
3 | ## 前端的困境
4 |
5 | 对于大部分前端页面,长什么样、要做什么,往往只需要三言两语就能说明白。 但是若要通过传统的前端框架实现它,往往需要写数百行代码。
6 |
7 | 传统的前端框架,比如 Vue,配合组件库,比如 Vuetify,允许你编写出细节完善、视觉美观的应用。 但是即使是一些“大厂”,也没能总是产出那么高质量的应用,因为开发效率的原因。 这即是说,即使开发者有能力,往往也会由于没有时间而无法做出相当完美的应用。 结果就是,许多传统前端框架的能力几乎得不到发挥,而为拥有这些能力所作的一些设计反而在很多情况下称为了负担。
8 |
9 | 除了传统前端框架,我们其实需要一个**首先关注开发效率**,其次是功能的完整性,再其次是运行时性能的前端框架。
10 |
11 | ## 解决方案
12 |
13 | - **类 ImGUI 的状态管理机制**
14 |
15 | 省去状态管理带来的一切心智负担。 就像 [Svelte](https://svelte.dev/) 那样无需手动为数据添加响应性,并且不需要复杂的编译器。
16 |
17 | - **无需写结束标签**
18 |
19 | 结束标签(如``)对开发效率造成了很大的影响,而它们事实上并不必要。
20 |
21 | - **按位置传入参数,而非按名称**
22 |
23 | 这样,开发者就不需要写出参数的名称。这节省了很多时间与代码量。
24 |
25 | - **通过返回值监听事件**
26 |
27 | 通过检查返回值来判断事件是否发生,就不需要使用回调函数,减少了代码复杂度。
28 |
29 | - **纯 TypeScript 构建页面**
30 |
31 | 无需像 JSX 或 Vue SFC 那样的 DSL。 渲染和逻辑使用同样的、普通的 TypeScript 语法。相关的工具链也都完全兼容。
32 |
33 | ## 效果
34 |
35 | Refina 仍在积极开发中,但它已经在开发效率上体现了非常大的优势。
36 |
37 | 基于数个真实项目,Refina 相比其他前端框架**减少了大约 `30%~40%` 的代码量**,**开发效率提升至约 `1.4~1.6` 倍**。
38 |
--------------------------------------------------------------------------------
/docs/zh/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 | title: Refina
4 | titleTemplate: 高度提炼的前端框架
5 | hero:
6 | name: Refina
7 | text: 高度提炼的前端框架
8 | tagline: 帮助你极大地减少工作量。
9 | image:
10 | src: /logo.svg
11 | alt: Refina Logo
12 | actions:
13 | - theme: brand
14 | text: 快速上手
15 | link: /zh/guide/introduction
16 | - theme: alt
17 | text: English Version
18 | link: /
19 | - theme: alt
20 | text: 为什么选 Refina?
21 | link: /zh/guide/why
22 | - theme: alt
23 | text: 在 GitHub 上查看
24 | link: https://github.com/refinajs/refina
25 | features:
26 | - icon: 💡
27 | title: 类 ImGUI 的状态管理机制
28 | details: 省去状态管理带来的一切心智负担。
29 | - icon: ⚡️
30 | title: 大大减少代码量
31 | details: 无需写元素的结束标签,并按位置传入参数而非按名称传入参数。
32 | - icon: 🧩
33 | title: 纯 TypeScript
34 | details: 无需使用 DSL。 所有的 TypeScript 功能和开发工具都可用。
35 | - icon: 🔥
36 | title: 热模块替换
37 | details: 热模块替换(HMR)运行您在开发时更新页面内容而无需刷新页面,不丢失页面的状态。
38 | - icon: 🔩
39 | title: 插件系统
40 | details: 可以通过插件扩充 Refina 的功能。 包括组件、工具函数、指令等等。
41 | - icon: 🔑
42 | title: 完整的 TS 类型
43 | details: 所有 API 都有良好的类型。甚至一些动态的状态也有类型提示。
44 | ---
45 |
--------------------------------------------------------------------------------
/docs/zh/misc/contact.md:
--------------------------------------------------------------------------------
1 | # 关于我们
2 |
3 | ## 关于作者
4 |
5 | - **邮箱**: [kermanx@qq.com](mailto:kermanx@qq.com)
6 | - **GitHub**: [KermanX](https://github.com/KermanX)
7 |
8 | ## QQ 群
9 |
10 | - **群号**: 488240549
11 |
12 | ## Discord 服务器
13 |
14 | - **邀请链接**: [Join Refina.js](https://discord.gg/2hjDhfpgzK)
15 |
16 | > 该服务器可能并不活跃。 如果你有任何问题,欢迎通过邮箱或QQ群联系。
17 |
--------------------------------------------------------------------------------
/docs/zh/misc/playground.md:
--------------------------------------------------------------------------------
1 |
3 |
4 |
13 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/button.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `Button` Component
7 |
8 | The main function of the `Button` component is to trigger an action when clicked.
9 |
10 | TriggerComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | if (_.xButton("Click me", disabled)) {
16 | alert("Clicked!");
17 | }
18 | ```
19 |
20 | ## Param: `children`
21 |
22 | **type**: `D`
23 |
24 | The content of the button.
25 |
26 | ## Param: `disabled`
27 |
28 | = `false`
29 |
30 | **type**: `D`
31 |
32 | Whether the button is disabled.
33 |
34 | ## Triggered when: `click`
35 |
36 | **data type**: `void` _(Can be `MouseEvent`)_
37 |
38 | When the button is clicked, the event will be emitted.
39 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/checkbox.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `Checkbox` Component
7 |
8 | The main function of the `Checkbox` component is to toggle a boolean value.
9 |
10 | TriggerComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | if (_.xCheckbox(state, "Check me", disabled)) {
16 | alert(_.$ev ? "Checked!" : "Unchecked!");
17 | }
18 | ```
19 |
20 | ## Param: `state`
21 |
22 | **type**: `D`
23 |
24 | > ↑ `D` if indeterminate state is supported
25 |
26 | The state of the checkbox.
27 |
28 | ## Param: `label`
29 |
30 |
31 |
32 | **type**: `D`
33 |
34 | The label of the checkbox.
35 |
36 | ## Param: `disabled`
37 |
38 | = `false`
39 |
40 | **type**: `D`
41 |
42 | Whether the checkbox is disabled.
43 |
44 | ## Triggered when: `change`
45 |
46 | **data type**: `boolean`
47 |
48 | > ↑ `boolean|undefined` if indeterminate state is supported
49 |
50 | When the state of the checkbox is changed, the new state will be emitted.
51 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/introduction.md:
--------------------------------------------------------------------------------
1 | # 标准组件
2 |
3 | 本章节描述了 Refina 定义的标准组件。
4 |
5 | Refina 的标准组件是常用的,且_有一定复杂度_的组件。 不能保证所有组件库都会包含所有这些标准组件。 但组件库应当尽可能地覆盖标准组件,并符合标准组件的接口。
6 |
7 | :::warning
8 |
9 | 标准组件在不同的 UI 库中可能有不同的实现。
10 |
11 | 一些特性在有些 UI 风格中不可用,而有些 UI 风格会提供一些专有的特性。
12 |
13 | :::
14 |
15 | :::tip
16 |
17 | 在本文档的示例中,我们使用 `x` 前缀来表示标准组件。 比如,`_.xButton`。 比如,`_.xButton`。
18 |
19 | 但是,在实际开发中,需要加上你所使用的组件库的前缀。 但是,在实际开发中,需要加上你所使用的组件库的前缀。 比如,MdUI 中的按钮是 `_.mdButton`,而在 FluentUI 中是 `_.fButton`。
20 |
21 | 由 `@refina/basic-components` 提供的组件没有前缀。 比如 `_.button`。 比如 `_.button`。
22 |
23 | :::
24 |
25 | :::info
26 |
27 | 一些过于简单和显然的不属于标准组件的范畴。 比如,`_.span` 与 `_.mdIcon`。
28 |
29 | 一些只在少数组件库中包含的组件也不属于标准组件的范畴。 比如,`_.mdAppBarTitle`。
30 |
31 | :::
32 |
33 | ## 目前可用的组件库
34 |
35 | 以下组件库目前已经可用:
36 |
37 | [](https://www.npmjs.com/package/@refina/basic-components)
38 |
39 | [](https://www.npmjs.com/package/@refina/mdui)
40 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/list.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `List` Component
7 |
8 | Render a list of items.
9 |
10 | OutputComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | _.xList(data, bySelf, item => {
16 | _.span(item);
17 | _.xButton("Remove");
18 | });
19 | ```
20 |
21 | ## Generic: `T`
22 |
23 | The type of the data of one row.
24 |
25 | ## Param: `data`
26 |
27 | **type**: `D>`
28 |
29 | The data to render.
30 |
31 | ## Param: `key`
32 |
33 | **type**: `LoopKey`
34 |
35 | The key generator of the list. See [The Key Generator](../guide/essentials/list.md#key-generator).
36 |
37 | ## Param: `body`
38 |
39 | **type**: `(item: T, index: number) => void`
40 |
41 | The body of rows of the list.
42 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/nav-rail.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `NavRail` Component
7 |
8 | The `NavRail` component is a navigation rail. It is usually used to navigate between pages.
9 |
10 | It is displayed as a vertical list of buttons on the left side of the screen.
11 |
12 | StatusComponent
13 |
14 | **例子**
15 |
16 | ```ts
17 | const currentPage = _.mdNavRail([
18 | ["Lobby", "home"],
19 | ["About", "info"],
20 | ]);
21 | _.embed(pages[currentPage]);
22 | ```
23 |
24 | ## Generic: `Value`
25 |
26 | **extends**: `string`
27 |
28 | The item value type.
29 |
30 | ## Param: `items`
31 |
32 | **type**: `DReadonlyArray<[value: Value, iconName?: string]><[value: Value, iconName?: string]>`
33 |
34 | The item' values (which is displayed as texts by default) and icon names.
35 |
36 | ## Param: `contentOverride`
37 |
38 | = `{}`
39 |
40 | **type**: `DPartialRecord`
41 |
42 | By default, the text of each item is the value itself. You can override it by passing a record mapping each value to its content.
43 |
44 | ## Status: Active item
45 |
46 | **type**: `Value`
47 |
48 | The value of the active item.
49 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/slider.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `Slider` Component
7 |
8 | Use the `Slider` component to select a value from a range.
9 |
10 | TriggerComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | if (_.xSlider(value, disabled, step, min, max)) {
16 | alert(`New value: ${_.$ev}`);
17 | }
18 | ```
19 |
20 | ## Param: `value`
21 |
22 | **type**: `D`
23 |
24 | The percentage of the slider, from 0 to 100.
25 |
26 | ## Param: `disabled`
27 |
28 | = `false`
29 |
30 | **type**: `D`
31 |
32 | Whether the slider is disabled.
33 |
34 | ## Param: `step`
35 |
36 | = `1`
37 |
38 | **type**: `D`
39 |
40 | The step of the slider.
41 |
42 | ## Param: `min`
43 |
44 | = `0`
45 |
46 | **type**: `D`
47 |
48 | The minimum value of the slider.
49 |
50 | ## Param: `max`
51 |
52 | = `100`
53 |
54 | **type**: `D`
55 |
56 | The maximum value of the slider.
57 |
58 | ## Triggered when: `change`
59 |
60 | **data type**: `number`
61 |
62 | When the value of the slider is changed, the new value will be emitted.
63 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/switch.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `Switch` Component
7 |
8 | The main function of the `Switch` component is to toggle a boolean value.
9 |
10 | TriggerComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | if (_.xSwitch(checked, "Switch me", disabled)) {
16 | alert(_.$ev ? "On!" : "Off!");
17 | }
18 | ```
19 |
20 | ## Param: `checked`
21 |
22 | **type**: `D`
23 |
24 | The state of the switch.
25 |
26 | ## Param: `label`
27 |
28 |
29 |
30 | **type**: `D`
31 |
32 | The label of the switch.
33 |
34 | ## Param: `disabled`
35 |
36 | = `false`
37 |
38 | **type**: `D`
39 |
40 | Whether the switch is disabled.
41 |
42 | ## Triggered when: `change`
43 |
44 | **data type**: `boolean`
45 |
46 | When the state of the switch is changed, the new state will be emitted.
47 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/table.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `Table` Component
7 |
8 | Render a table.
9 |
10 | OutputComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | _.mdTable(
16 | data,
17 | [_ => _.mdIcon("person"), "Age", "Action"],
18 | "name",
19 | ({ name, age }) => {
20 | _.mdTableCell(name);
21 | _.mdTableCell(age);
22 | _.mdTableCell(_ => _.mdButton("Open") && alert(name));
23 | },
24 | );
25 | ```
26 |
27 | ## Generic: `T`
28 |
29 | The type of the data of one row.
30 |
31 | ## Param: `data`
32 |
33 | **type**: `D>`
34 |
35 | The data to render.
36 |
37 | ## Param `head`
38 |
39 | **type**: `DArray | D`
40 |
41 | The head of the table.
42 |
43 | If it is an array, each item will be rendered as a ``. Otherwise, it is the content of the ``.
44 |
45 | ## Param: `key`
46 |
47 | **type**: `LoopKey`
48 |
49 | The key generator of the table. See [The Key Generator](../guide/essentials/list.md#key-generator).
50 |
51 | ## Param: `row`
52 |
53 | **type**: `(item: T, index: number) => void`
54 |
55 | The body of rows of the table.
56 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/tabs.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `Tabs` Component
7 |
8 | Displays a set of tabs, and only one tab is visible at a time.
9 |
10 | TriggerComponent
11 |
12 | **例子**
13 |
14 | ```ts
15 | _.xTabs(
16 | "Tab 1",
17 | _ => _._p({}, "Content 1"),
18 | "Tab 2",
19 | _ => _._p({}, "Content 2"),
20 | "Tab 3",
21 | _ => _._p({}, "Content 3"),
22 | );
23 | ```
24 |
25 | ## Params: `...tabs`
26 |
27 | **type**: `RepeatedTuple<[name: D, content: D]>` (An alternating list of `D` and `D`).
28 |
29 | The tab names and contents.
30 |
31 | For the odd indices, the value is the tab name.
32 |
33 | For the even indices, the value is the tab content.
34 |
35 | ## Triggered when: `change`
36 |
37 | **data type**: `string`
38 |
39 | When the active tab changes, the new tab name will be emitted.
40 |
--------------------------------------------------------------------------------
/docs/zh/std-comps/text-field.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # The `TextField` Component
7 |
8 | Use the `TextField` component to input text.
9 |
10 | Different from the `Textarea` component, the `TextField` component is a single-line input.
11 |
12 | TriggerComponent
13 |
14 | **例子**
15 |
16 | ```ts
17 | if (_.xTextField(value, "Username", disabled)) {
18 | console.log(`New value: ${_.$ev}`);
19 | }
20 | ```
21 |
22 | ## Param: `value`
23 |
24 | **type**: `D`
25 |
26 | The value of the text field.
27 |
28 | ## Param: `label`
29 |
30 |
31 |
32 | **type**: `D`
33 |
34 | > ↑ `D` if HTML label is supported.
35 |
36 | The label of the text field.
37 |
38 | ## Param: `placeholder`
39 |
40 |
41 |
42 | **type**: `D`
43 |
44 | The placeholder of the text field.
45 |
46 | > May be combined with `label`.
47 |
48 | ## Param: `disabled`
49 |
50 | = `false`
51 |
52 | **type**: `D`
53 |
54 | Whether the text field is disabled.
55 |
56 | ## Triggered when: `input`
57 |
58 | **data type**: `string`
59 |
60 | When the value of the text field is changed, the new value will be emitted.
61 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | page: true
3 | title: 教程
4 | sidebar: false
5 | aside: false
6 | footer: false
7 | returnToTop: false
8 | ---
9 |
10 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-1/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | $app([Basics], _ => {
4 | _.h1("👋 Hello, Refina!");
5 | });
6 | declare module "refina" {
7 | interface Plugins {
8 | Basics: typeof Basics;
9 | }
10 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-1/description.md:
--------------------------------------------------------------------------------
1 | # 开始 {#getting-started}
2 |
3 | 欢迎来到 Refina 互动教程!
4 |
5 | 本教程的目标是让你在浏览器中快速体验使用 Refina 是什么感受, 因此它不会太过深入解释所有细节,如果有些东西你一时无法完全理解,也不必担心。 但是,在完成本教程之后,请务必阅读指南,以确保你对涉及的话题有更深入、完整的理解。
6 |
7 | ## 前置要求 {#prerequisites}
8 |
9 | 本教程假定你基本熟悉 HTML、CSS 和 JavaScript。 对于前端开发来说,一个完全的新手也许并不适合上手就学习框架——最好是掌握了基础知识再回来。 如果之前有其他框架的经验会很有帮助,但也不是必须的。
10 |
11 | ## 如何使用本教程 {#how-to-use-this-tutorial}
12 |
13 | 你可以编辑右侧下方的代码,并立即看到结果更新。 教程每一步都会介绍一个 Refina 的核心功能,并期望你能够补全代码,让 demo 运行起来。 如果你卡住了,会有一个“看答案!”按钮,点击它,会为你揭晓能够运行的代码。 试着不要太依赖该按钮——自己解决会学得更快。
14 |
15 | 准备好了吗? 点击“下一步”按钮开始吧。
16 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-10/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | import JSConfetti from "js-confetti";
4 | const confetti = new JSConfetti();
5 | confetti.addConfetti();
6 | $app([Basics], _ => {
7 | _.$css`
8 | all: unset;
9 | font-size: xx-large;
10 | font-weight: bold;
11 | text-align: center;
12 | margin: 3em 30%;`;
13 | _.button("🎉 Congratulations!") && confetti.addConfetti();
14 | });
15 | declare module "refina" {
16 | interface Plugins {
17 | Basics: typeof Basics;
18 | }
19 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-10/description.md:
--------------------------------------------------------------------------------
1 | # 你做到了!
2 |
3 | 你已经完成了整个教程!
4 |
5 | 现在,你应该大致明白使用 Refina 开发应用是怎样的感觉了。 虽然我们飞快地介绍了许多东西,但也因此忽略了大量的细节,所以千万别这样就满足了! 接下来,你可以:
6 |
7 | - 参考[快速上手](../../../guide/quick-start),在你的机器上创建一个真实的 Refina 项目。
8 | - 阅读[指南](../../../guide/essentials/application)——它更详细地探讨了我们目前学过的所有话题,并且还有许多其他更深入的内容。
9 |
10 | 我们很期待看到你能用 Refina 打造出怎样的作品!
11 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-10/import-map.json:
--------------------------------------------------------------------------------
1 | {
2 | "imports": {
3 | "js-confetti": "https://cdn.jsdelivr.net/npm/js-confetti/+esm"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-2/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | // Build your app here
5 |
6 | declare module "refina" {
7 | interface Plugins {
8 | // Add your plugins here
9 | }
10 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-2/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | $app([Basics], _ => {
4 | _.h1("Hello, world!");
5 | _.p(_ => {
6 | _.t`This is a `;
7 | _.a("link", "https://refina.dev");
8 | _.t`.`;
9 | });
10 | });
11 | declare module "refina" {
12 | interface Plugins {
13 | Basics: typeof Basics;
14 | }
15 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-3/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | $app([Basics], _ => {
4 | _.h1("Make me styled!");
5 | });
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-3/App/styles.css:
--------------------------------------------------------------------------------
1 | .my-class {
2 | /* 在此处编写 CSS */
3 | }
4 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-3/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | $app([Basics], _ => {
4 | _.$cls`my-class`;
5 | _.$css`color:red;font-size:2rem`;
6 | _.h1("Make me styled!");
7 | });
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-3/_hint/App/styles.css:
--------------------------------------------------------------------------------
1 | .my-class {
2 | text-transform: uppercase;
3 | }
4 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-3/description.md:
--------------------------------------------------------------------------------
1 | # 添加样式与类名
2 |
3 | 在上一步中,我没学习了如何使用上下文对象中的**组件函数**来渲染组件。 上下文对象中还有**指令**。 在这一步中,我们将学习使用两个指令:`_.$cls` 和 `_.$css`。
4 |
5 | `_.$cls` 用于向下一个渲染的组件添加类名。 它既可以是普通函数也可以是标签函数:
6 |
7 | ```ts
8 | _.$cls`foo`;
9 | _.div();
10 | //
11 | ```
12 |
13 | `_.$css` 用于向下一个渲染的组件添加样式(相当于元素的 `style` 属性)。 它和 `_.$cls` 用法一致:
14 |
15 | ```ts
16 | _.$css`color:red`;
17 | _.div();
18 | //
19 | ```
20 |
21 | 你还可以一起使用 `_.$cls` 和 `_.$css`,并可以连续使用多次:
22 |
23 | ```ts
24 | _.$cls`foo`;
25 | _.$cls`bar`;
26 | _.$css`color:red`;
27 | _.div();
28 | //
29 | ```
30 |
31 | 现在,试着给编辑器中的 `` 元素添加一些样式和类名。
32 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-4/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 |
4 | // Should declare states here?
5 |
6 | $app([Basics], _ => {
7 | // Or here?
8 |
9 | _.p("Count is 0!");
10 | });
11 | declare module "refina" {
12 | interface Plugins {
13 | Basics: typeof Basics;
14 | }
15 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-4/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | let count = 0;
4 | $app([Basics], _ => {
5 | // Not here!
6 |
7 | _.p(`Count is ${count}!`);
8 | });
9 | declare module "refina" {
10 | interface Plugins {
11 | Basics: typeof Basics;
12 | }
13 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-4/description.md:
--------------------------------------------------------------------------------
1 | # 状态
2 |
3 | Refina 中的状态是普通的 JavaScript 变量。 你可以使用状态来存储数据,并渲染动态的内容。
4 |
5 | ```ts
6 | let message = "Hello World!";
7 | let person = {
8 | id: 1,
9 | name: "John Doe",
10 | };
11 |
12 | $app([Basics], _ => {
13 | _.h1(message);
14 | _.p(`Hello ${person.name}!`);
15 | });
16 | ```
17 |
18 | :::warning States should be declared outside the main function.
19 |
20 | Variables declared inside the main function are not states. They are just local temporary variables, which are re-created every time the main function is called.
21 |
22 | :::
23 |
24 | 就像在 JSX 中那样,你可以使用 `if` 语句或者其他运算符来根据条件渲染组件:
25 |
26 | ```ts
27 | let cond = true;
28 | let value: number | null | undefined;
29 |
30 | $app([Basics], _ => {
31 | if (cond) {
32 | _.h1("Hello World!");
33 | } else {
34 | _.h1("Hello Refina!");
35 | }
36 |
37 | cond && _.p("cond is truthy.");
38 | value === 1 && _.p("value is 1.");
39 | value ?? _.p("value is null or undefined.");
40 | });
41 | ```
42 |
43 | 现在,请尝试自己创建一个 "count" 状态,并用它来渲染 `_.p` 组件的内容。
44 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-5/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | $app([Basics], _ => {
4 | _.h1(`You have clicked the button 0 times.`);
5 | _.button("Click me!");
6 | });
7 | declare module "refina" {
8 | interface Plugins {
9 | Basics: typeof Basics;
10 | }
11 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-5/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | let count = 0;
4 | $app([Basics], _ => {
5 | _.h1(`You have clicked the button ${count} times.`);
6 | if (_.button("Click me!")) {
7 | count++;
8 | }
9 | });
10 | declare module "refina" {
11 | interface Plugins {
12 | Basics: typeof Basics;
13 | }
14 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-5/description.md:
--------------------------------------------------------------------------------
1 | # 事件处理
2 |
3 | 我们可以通过检查组件函数的返回值来接收事件:
4 |
5 | ```ts {2-4}
6 | $app([Basics], _ => {
7 | if (_.button("Click me!")) {
8 | console.log("Clicked!", _.$ev);
9 | }
10 | });
11 | ```
12 |
13 | 只有在按钮被按下后,对应的 `_.button` 函数调用会返回 `true`,然后 `if` 内部的语句会被执行。 这些语句就是处理事件的代码。
14 |
15 | `_.$ev` 是事件所携带的数据。 对于 `_.button`,它是一个 `MouseEvent` 对象。 `_.$ev` 只在事件处理部分可用。
16 |
17 | 你可以在事件处理代码中更新状态。
18 |
19 | ```ts {4}
20 | let click = false;
21 |
22 | $app([Basics], _ => {
23 | _.button("Click me!") && (clicked = true);
24 | clicked && _.p("Clicked!");
25 | });
26 | ```
27 |
28 |
46 |
47 | 在所有事件被处理之后,Refina 会自动更新页面。
48 |
49 | 现在,试着自己实现一个显示点击按钮的次数的计数器。
50 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-6/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | let name = "";
4 | $app([Basics], _ => {
5 | if (_.textInput(name)) {
6 | name = _.$ev;
7 | }
8 | _.p(`Hello ${name}!`);
9 | });
10 | declare module "refina" {
11 | interface Plugins {
12 | Basics: typeof Basics;
13 | }
14 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-6/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app, model } from "refina";
2 | import Basics from "@refina/basic-components";
3 | const name = model("");
4 | $app([Basics], _ => {
5 | _.textInput(name);
6 | if (name.value.length > 0) {
7 | _.button("Clear") && (name.value = "");
8 | }
9 | _.p(`Hello ${name}!`);
10 | });
11 | declare module "refina" {
12 | interface Plugins {
13 | Basics: typeof Basics;
14 | }
15 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-6/description.md:
--------------------------------------------------------------------------------
1 | # 获取用户输入
2 |
3 | 通过接收事件,我们可用获取用户输入并据此更新状态:
4 |
5 | ```ts
6 | let name = "";
7 |
8 | $app([Basics], _ => {
9 | if (_.textInput(name)) {
10 | name = _.$ev;
11 | }
12 | _.p(`Hello ${name}!`);
13 | });
14 | ```
15 |
16 | 试着在 "PREVIEW" 栏中的输入框中输入字符,你会看见 `` 中的文字同步更新。
17 |
18 | 还有一个更加简单的方式可以将用户输入直接保存到一个状态,而无需手动处理事件以保存输入:
19 |
20 | ```ts
21 | import { $app, model } from "refina";
22 |
23 | const name = model("");
24 |
25 | $app([Basics], _ => {
26 | _.textInput(name);
27 |
28 | _.p(`Hello ${name}!`);
29 | // equivalent to:
30 | // _.p(`Hello ${name.value}!`);
31 | });
32 | ```
33 |
34 | `model` 函数创建了一个 `JustModel` 对象。它简单地把原始值包裹为对象,于是组件可以“引用”到值并直接更新它。
35 |
36 | `textInput` 组件(以及一些类似的组件)接收 类型为 `Model` 的值作为它的第一个参数,即,既可以是原始值也可以是 model。 当用户在输入框中输入时,组件会更新 model 的值。
37 |
38 | 现在,试着使用 `model` 重构编辑器中的代码。 并且在当输入框不为空时显示一个可以清除输入内容的按钮。
39 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-7/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app, model } from "refina";
2 | import Basics from "@refina/basic-components";
3 | let id = 0;
4 | let newTodo = model("");
5 | let todos = [{
6 | id: id++,
7 | text: "Learn HTML"
8 | }, {
9 | id: id++,
10 | text: "Learn JavaScript"
11 | }, {
12 | id: id++,
13 | text: "Learn Refina"
14 | }];
15 | function remove(id: number) {
16 | todos = todos.filter(todo => todo.id !== id);
17 | }
18 | $app([Basics], _ => {
19 | _.textInput(newTodo);
20 | _.button("Add Todo") && todos.push({
21 | id: id++,
22 | text: newTodo.value
23 | });
24 |
25 | // Finish the following code to render the list of todos.
26 | // _.ul(todos, ...);
27 | });
28 | declare module "refina" {
29 | interface Plugins {
30 | Basics: typeof Basics;
31 | }
32 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-7/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app, model } from "refina";
2 | import Basics from "@refina/basic-components";
3 | let id = 0;
4 | let newTodo = model("");
5 | let todos = [{
6 | id: id++,
7 | text: "Learn HTML"
8 | }, {
9 | id: id++,
10 | text: "Learn JavaScript"
11 | }, {
12 | id: id++,
13 | text: "Learn Refina"
14 | }];
15 | function remove(id: number) {
16 | todos = todos.filter(todo => todo.id !== id);
17 | }
18 | $app([Basics], _ => {
19 | _.textInput(newTodo);
20 | _.button("Add Todo") && todos.push({
21 | id: id++,
22 | text: newTodo.value
23 | });
24 | _.ul(todos, "id", item => _.li(_ => {
25 | _.span(item.text);
26 | _.button("❌") && remove(item.id);
27 | }));
28 | });
29 | declare module "refina" {
30 | interface Plugins {
31 | Basics: typeof Basics;
32 | }
33 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-7/description.md:
--------------------------------------------------------------------------------
1 | # 列表渲染
2 |
3 | 使用 `_.for` 组件来渲染列表一个基于数组的列表:
4 |
5 | ```ts
6 | const todos = [
7 | { id: 1, text: "Buy milk" },
8 | { id: 2, text: "Do homework" },
9 | { id: 3, text: "Read a book" },
10 | ];
11 |
12 | $app([Basics], _ => {
13 | _.for(todos, "id", item => {
14 | _.p(item.text);
15 | });
16 | });
17 | ```
18 |
19 | 注意我们还通过 `_.for` 的第二个参数给每个 todo 对象设置了唯一的 key。 这允许 Refina 能够精确的移动每个 ``,以匹配对应的对象在数组中的位置。
20 |
21 | 这个 key 可以是列表元素的一个键名,或者一个返回 key 的函数。 Refina 还提供了 `bySelf` 和 `byIndex` 这两个可以作为 key 的函数。
22 |
23 | 一些组件自身也具备 `_.for` 的用法和功能,比如 `_.ul` 与 `_.ol`。
24 |
25 | 现在,尝试完成这个 todo 列表。
26 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-8/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | $app([Basics], _ => {
4 | _.h1("Title");
5 | _.div(_ => {
6 | _.h2("Part 1");
7 | _.p("Content 1");
8 | _.a("share", "#");
9 | });
10 | _.div(_ => {
11 | _.h2("Part 2");
12 | _.p("Content 2");
13 | _.a("share", "#");
14 | });
15 | _.div(_ => {
16 | _.h2("Part 3");
17 | _.p("Content 3");
18 | _.a("share", "#");
19 | });
20 | _.div(_ => {
21 | _.h2("Part 4");
22 | _.p("Content 4");
23 | _.a("share", "#");
24 | });
25 | });
26 | declare module "refina" {
27 | interface Plugins {
28 | Basics: typeof Basics;
29 | }
30 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-8/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import Basics from "@refina/basic-components";
3 | import PartView from "./PartView";
4 | $app([Basics], _ => {
5 | _.h1("Title");
6 | _(PartView)("Part 1", "Content 1");
7 | _(PartView)("Part 2", "Content 2");
8 | _(PartView)("Part 3", "Content 3");
9 | _(PartView)("Part 4", "Content 4");
10 | });
11 | declare module "refina" {
12 | interface Plugins {
13 | Basics: typeof Basics;
14 | }
15 | }
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-8/_hint/App/PartView.ts:
--------------------------------------------------------------------------------
1 | import { $view, Content, _ } from "refina";
2 | export default $view((title: string, content: Content) => {
3 | _.h1(title);
4 | _.p(content);
5 | });
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-8/description.md:
--------------------------------------------------------------------------------
1 | # 视图
2 |
3 | 视图是一个函数,它渲染一部分页面。
4 |
5 | 他被用来将页面分割为相对独立的几个部分,并且可以复用。
6 |
7 | 使用 `$view` 函数来定义视图:
8 |
9 | ```ts
10 | import { $view, _ } from "refina";
11 |
12 | export default $view((id: number) => {
13 | _.h1(`Card ${id}`);
14 | });
15 | ```
16 |
17 | To use a view, just call the context object:
18 |
19 | ```ts
20 | import { $app } from "refina";
21 | import CardView from "./CardView";
22 |
23 | $app([], _ => {
24 | _(CardView)("1");
25 | _(CardView)("2");
26 | _(CardView)("3");
27 | });
28 | ```
29 |
30 | 现在,试着将编辑器中重复的代码提炼为一个视图,并使用该视图来渲染页面。
31 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-9/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 |
3 | /*
4 | Rewrite the following HTML using low-level rendering functions.
5 |
6 |
10 | */
11 |
12 | $app([], _ => {});
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-9/_hint/App/App.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 |
3 | /*
4 | Rewrite the following HTML using low-level rendering functions.
5 |
6 |
10 | */
11 |
12 | $app([], _ => {
13 | _.$css`position:fixed;`;
14 | _._svgSvg({
15 | id: "svg1",
16 | width: 100,
17 | height: 100
18 | }, _ => {
19 | _._svgPath({
20 | d: "M 10 10 H 90 V 90 H 10 Z",
21 | fill: "red"
22 | });
23 | _._svgCircle({
24 | cx: 50,
25 | cy: 50,
26 | r: 40,
27 | stroke: "blue",
28 | fill: "none"
29 | });
30 | });
31 | });
--------------------------------------------------------------------------------
/docs/zh/tutorial/src/step-9/description.md:
--------------------------------------------------------------------------------
1 | # 底层渲染
2 |
3 | 在之前的步骤中,我们使用组件来渲染页面。 但是,有时你想要渲染的东西并没有对应的组件。
4 |
5 | 底层渲染函数允许你渲染任何的 DOM 元素。 以及,组件归根结底也是通过这些底层渲染函数实现的。
6 |
7 | 下面是一个示例:
8 |
9 | ```ts
10 | let count = 0;
11 |
12 | const app = $app([], _ => {
13 | _.$cls`my-button`; // _.$cls and _.$css are also available
14 | _._button(
15 | // Attributes (optional)
16 | {
17 | id: "my-div",
18 | onclick() {
19 | count++;
20 | app.update(); // Update the application
21 | },
22 | },
23 | // Content (optional)
24 | _ => _.span(`Count is ${count}`),
25 | // Event listeners (optional)
26 | {
27 | hover: {
28 | mousemove(ev) {
29 | console.log(ev.clientX, ev.clientY);
30 | },
31 | capture: true,
32 | },
33 | },
34 | );
35 | });
36 | ```
37 |
38 | 这比使用组件要不那么方便。 当你觉得需要使用底层渲染函数,而不是使用组件是,请考虑这是否是必须的:底层渲染函数提供的额外功能真的值得花时间事先吗? 往往一些细枝末节且特别的功能会耗费开发者大量的时间,却并没有成比例的用处。
39 |
40 | 底层渲染函数的名字遵循这样的规律:
41 |
42 | - **HTML**: `_._div` 是 ` `
43 | - **Web components**: `_._custom_element` 是 ` `
44 | - **SVG**: `_._svgCircle` 是 ``
45 |
46 | 现在,试着将注释中的 HTML 代码通过底层渲染函数重写。
47 |
--------------------------------------------------------------------------------
/docs/zh/tutorial/tutorial.data.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import { createMarkdownRenderer } from "vitepress";
3 | import { readExamples } from "../../helpers/loader";
4 | import type { ExampleData } from "../../helpers/utils";
5 | export declare const data: Record;
6 | export default {
7 | watch: "./src/**",
8 | async load() {
9 | const md = await createMarkdownRenderer(process.cwd(), undefined, "/");
10 | const files = readExamples(path.resolve(__dirname, "./src"));
11 | for (const step in files) {
12 | const stepFiles = files[step];
13 | const desc = (stepFiles["description.md"] as string);
14 | if (desc) {
15 | stepFiles["description.md"] = md.render(desc);
16 | }
17 | }
18 | return files;
19 | }
20 | };
--------------------------------------------------------------------------------
/packages/basic-components/README.md:
--------------------------------------------------------------------------------
1 | # `@refina/basic-components`
2 |
3 | [](https://www.npmjs.com/package/@refina/basic-components)
4 |
5 | The basic components of Refina.
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [Getting Started](https://refina.vercel.app/guide/introduction.html)
11 | - [GitHub Repository](https://github.com/refinajs/refina).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
14 | ## Usage
15 |
16 | ### Install the Plugin to App
17 |
18 | ```ts
19 | import Basics from "@refina/basic-components";
20 |
21 | $app([Basics], _ => {
22 | // ...
23 | });
24 | ```
25 |
26 | ### Use the Components
27 |
28 | All the components of this package doesn't have a prefix.
29 |
30 | ```ts
31 | if (_.button("button")) {
32 | console.log("Button clicked");
33 | }
34 | ```
35 |
36 | ## Included Components
37 |
38 | - `a`
39 | - `br`
40 | - `button`
41 | - `div`
42 | - `h1`
43 | - `h2`
44 | - `h3`
45 | - `h4`
46 | - `h5`
47 | - `h6`
48 | - `img`
49 | - `input`
50 | - `textInput`
51 | - `passwordInput`
52 | - `checkbox`
53 | - `label`
54 | - `li`
55 | - `ol`
56 | - `p`
57 | - `span`
58 | - `table`
59 | - `th`
60 | - `td`
61 | - `textarea`
62 | - `ul`
63 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/a.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicA extends Component {
4 | $main(children: Content, href: string, target?: string): void {
5 | _._a({ href, target }, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/br.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 |
3 | export class BasicBr extends Component {
4 | $main(): void {
5 | _._br();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/button.ts:
--------------------------------------------------------------------------------
1 | import { Content, TriggerComponent, _ } from "refina";
2 |
3 | export class BasicButton extends TriggerComponent {
4 | $main(
5 | children: Content,
6 | disabled?: boolean,
7 | ): this is {
8 | $ev: MouseEvent;
9 | } {
10 | _._button(
11 | {
12 | onclick: this.$fire,
13 | disabled,
14 | type: "button",
15 | },
16 | children,
17 | );
18 | return this.$fired;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/checkbox.ts:
--------------------------------------------------------------------------------
1 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
2 |
3 | export class BasicCheckbox extends TriggerComponent {
4 | inputRef = elementRef<"input">();
5 | $main(checked: Model): this is {
6 | $ev: boolean;
7 | } {
8 | _.$ref(this.inputRef);
9 | _._input({
10 | type: "checkbox",
11 | checked: unwrap(checked),
12 | onchange: () => {
13 | const newChecked = this.inputRef.current!.node.checked;
14 | this.$updateModel(checked, newChecked);
15 | this.$fire(newChecked);
16 | },
17 | });
18 | return this.$fired;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/div.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicDiv extends Component {
4 | $main(children?: Content): void {
5 | _._div({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/h.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicH1 extends Component {
4 | $main(children: Content): void {
5 | _._h1({}, children);
6 | }
7 | }
8 |
9 | export class BasicH2 extends Component {
10 | $main(children: Content): void {
11 | _._h2({}, children);
12 | }
13 | }
14 |
15 | export class BasicH3 extends Component {
16 | $main(children: Content): void {
17 | _._h3({}, children);
18 | }
19 | }
20 |
21 | export class BasicH4 extends Component {
22 | $main(children: Content): void {
23 | _._h4({}, children);
24 | }
25 | }
26 |
27 | export class BasicH5 extends Component {
28 | $main(children: Content): void {
29 | _._h5({}, children);
30 | }
31 | }
32 |
33 | export class BasicH6 extends Component {
34 | $main(children: Content): void {
35 | _._h6({}, children);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/img.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 |
3 | export class BasicImg extends Component {
4 | $main(src: string, alt?: string): void {
5 | _._img({
6 | src,
7 | alt,
8 | });
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./a";
2 | export * from "./br";
3 | export * from "./button";
4 | export * from "./checkbox";
5 | export * from "./div";
6 | export * from "./h";
7 | export * from "./img";
8 | export * from "./input";
9 | export * from "./label";
10 | export * from "./li";
11 | export * from "./ol";
12 | export * from "./p";
13 | export * from "./span";
14 | export * from "./table";
15 | export * from "./textarea";
16 | export * from "./ul";
17 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/input.ts:
--------------------------------------------------------------------------------
1 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
2 |
3 | export class BasicInput extends TriggerComponent {
4 | inputRef = elementRef<"input">();
5 | type = "text";
6 | $main(
7 | value: Model,
8 | disabled?: boolean,
9 | placeholder?: string,
10 | ): this is {
11 | $ev: string;
12 | } {
13 | _.$ref(this.inputRef);
14 | _._input({
15 | type: this.type,
16 | disabled,
17 | placeholder,
18 | value: unwrap(value),
19 | oninput: () => {
20 | const newValue = this.inputRef.current!.node.value;
21 | this.$updateModel(value, newValue);
22 | this.$fire(newValue);
23 | },
24 | });
25 | return this.$fired;
26 | }
27 | }
28 |
29 | export class BasicTextInput extends BasicInput {
30 | type = "text";
31 | }
32 |
33 | export class BasicPasswordInput extends BasicInput {
34 | type = "password";
35 | }
36 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/label.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicLabel extends Component {
4 | $main(children: Content): void {
5 | _._label({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/li.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicLi extends Component {
4 | $main(children: Content): void {
5 | _._li({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/ol.ts:
--------------------------------------------------------------------------------
1 | import { Component, LoopKey, _ } from "refina";
2 |
3 | export class BasicOl extends Component {
4 | $main(
5 | data: Iterable,
6 | key: LoopKey,
7 | itemView: (item: T, index: number) => void,
8 | ): void {
9 | _._ol({}, _ =>
10 | _.for(data, key, (item, index) => {
11 | itemView(item, index);
12 | }),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/p.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicP extends Component {
4 | $main(children: Content): void {
5 | _._p({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/span.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class BasicSpan extends Component {
4 | $main(children?: Content): void {
5 | _._span({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/table.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, LoopKey, _, byIndex } from "refina";
2 |
3 | export class BasicTable extends Component {
4 | $main(
5 | data: Iterable,
6 | head: Content[] | Content,
7 | key: LoopKey,
8 | row: (item: T, index: number) => void,
9 | ): void {
10 | _._table({}, _ => {
11 | _._thead({}, _ => {
12 | if (Array.isArray(head)) {
13 | _._tr({}, _ =>
14 | _.for(head, byIndex, item => {
15 | _._th({}, item);
16 | }),
17 | );
18 | } else {
19 | _.embed(head);
20 | }
21 | });
22 | _._tbody({}, _ => {
23 | _.for(data, key, (item, index) => {
24 | _._tr({}, _ => {
25 | row(item, index);
26 | });
27 | });
28 | });
29 | });
30 | }
31 | }
32 |
33 | export class BasicTh extends Component {
34 | $main(children: Content): void {
35 | _._th({}, children);
36 | }
37 | }
38 |
39 | export class BasicTd extends Component {
40 | $main(children: Content): void {
41 | _._td({}, children);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/textarea.ts:
--------------------------------------------------------------------------------
1 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
2 |
3 | export class BasicTextarea extends TriggerComponent {
4 | inputRef = elementRef<"textarea">();
5 | $main(
6 | value: Model,
7 | disabled?: boolean,
8 | placeholder?: string,
9 | ): this is {
10 | $ev: string;
11 | } {
12 | _.$ref(this.inputRef);
13 | _._textarea({
14 | disabled,
15 | placeholder,
16 | value: unwrap(value),
17 | oninput: () => {
18 | const newValue = this.inputRef.current!.node.value;
19 | this.$updateModel(value, newValue);
20 | this.$fire(newValue);
21 | },
22 | });
23 | return this.$fired;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/basic-components/src/components/ul.ts:
--------------------------------------------------------------------------------
1 | import { Component, LoopKey, _ } from "refina";
2 |
3 | export class BasicUl extends Component {
4 | $main(
5 | data: Iterable,
6 | key: LoopKey,
7 | itemView: (item: T, index: number) => void,
8 | ): void {
9 | _._ul({}, _ =>
10 | _.for(data, key, (item, index) => {
11 | itemView(item, index);
12 | }),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/basic-components/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "refina";
2 | import * as c from "./components";
3 |
4 | export default {
5 | name: "basic-components",
6 | components: {
7 | a: c.BasicA,
8 | br: c.BasicBr,
9 | button: c.BasicButton,
10 | checkbox: c.BasicCheckbox,
11 | div: c.BasicDiv,
12 | h1: c.BasicH1,
13 | h2: c.BasicH2,
14 | h3: c.BasicH3,
15 | h4: c.BasicH4,
16 | h5: c.BasicH5,
17 | h6: c.BasicH6,
18 | img: c.BasicImg,
19 | input: c.BasicInput,
20 | textInput: c.BasicTextInput,
21 | passwordInput: c.BasicPasswordInput,
22 | label: c.BasicLabel,
23 | li: c.BasicLi,
24 | ol: c.BasicOl,
25 | p: c.BasicP,
26 | span: c.BasicSpan,
27 | table: c.BasicTable,
28 | td: c.BasicTd,
29 | th: c.BasicTh,
30 | textarea: c.BasicTextarea,
31 | ul: c.BasicUl,
32 | },
33 | } satisfies Plugin;
34 |
35 | export * from "./components";
36 |
--------------------------------------------------------------------------------
/packages/basic-components/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/basic-components/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { RefinaLib } from "vite-plugin-refina";
3 |
4 | export default defineConfig({
5 | plugins: [RefinaLib()],
6 | });
7 |
--------------------------------------------------------------------------------
/packages/bundles/README.md:
--------------------------------------------------------------------------------
1 | # `@refina/bundles`
2 |
3 | [](https://www.npmjs.com/package/@refina/bundles)
4 |
5 | The bundled version of Refina libs.
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [Getting Started](https://refina.vercel.app/guide/introduction.html)
11 | - [GitHub Repository](https://github.com/refinajs/refina).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
14 | ## Usage
15 |
16 | ### Via CDN
17 |
18 | ```html
19 |
20 | ```
21 |
22 | **Note:** Replace `` with the name of the lib you want to use.
23 |
24 | Available libs:
25 |
26 | - `core`
27 | - `basic-components`
28 | - `mdui`
29 | - `fluentui`
30 | - `transformer`
31 |
--------------------------------------------------------------------------------
/packages/bundles/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/bundles",
3 | "version": "0.6.1",
4 | "description": "The bundled version of Refina libs.",
5 | "keywords": [
6 | "refina",
7 | "bundle"
8 | ],
9 | "files": [
10 | "dist",
11 | "README.md"
12 | ],
13 | "type": "module",
14 | "exports": {
15 | "./*": "./dist/*"
16 | },
17 | "scripts": {
18 | "dev": "vite",
19 | "build": "vite build",
20 | "prepublishOnly": "npm run build"
21 | },
22 | "author": "_Kerman",
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/KermanX/refina"
26 | },
27 | "readme": "https://github.com/KermanX/refina#readme",
28 | "bugs": "https://github.com/KermanX/refina/issues",
29 | "license": "MIT",
30 | "devDependencies": {
31 | "@types/node": "^20.11.26",
32 | "terser": "^5.29.1",
33 | "vite": "^4.5.2",
34 | "vite-plugin-refina": "workspace:^"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/bundles/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { dirname, resolve } from "node:path";
2 | import { fileURLToPath } from "node:url";
3 | import { defineConfig } from "vite";
4 | import Refina, { RefinaTransformer } from "vite-plugin-refina";
5 |
6 | const libs = {
7 | core: "core/dist/index.js",
8 | "basic-components": "basic-components/dist/index.js",
9 | mdui: "mdui/dist/index.js",
10 | fluentui: "fluentui/dist/index.js",
11 | transformer: "transformer/src/index.ts",
12 | };
13 |
14 | const __dirname = dirname(fileURLToPath(import.meta.url));
15 |
16 | const transformer = new RefinaTransformer();
17 | transformer.ckeyPrefix = "b_"; // means "bundle"
18 |
19 | export default defineConfig({
20 | plugins: [
21 | Refina({
22 | transformer,
23 | }) as any,
24 | ],
25 | build: {
26 | lib: {
27 | // Could also be a dictionary or array of multiple entry points
28 | entry: Object.fromEntries(
29 | Object.entries(libs).map(([k, v]) => [k, resolve(__dirname, "..", v)]),
30 | ),
31 | formats: ["es"],
32 | },
33 | minify: "terser",
34 | outDir: "dist",
35 | emptyOutDir: true,
36 | sourcemap: true,
37 | },
38 | });
39 |
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 | # The Refina Core
2 |
3 | [](https://www.npmjs.com/package/refina)
4 |
5 | This package contains the core of Refina.
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [GitHub Repository](https://github.com/refinajs/refina).
11 | - [Why Refina](https://refina.vercel.app/guide/why.html).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "refina",
3 | "version": "0.6.1",
4 | "description": "An extremely refined web framework.",
5 | "keywords": [
6 | "refina",
7 | "framework",
8 | "web",
9 | "typescript"
10 | ],
11 | "files": [
12 | "src",
13 | "dist",
14 | "tsconfig.json",
15 | "README.md"
16 | ],
17 | "type": "module",
18 | "main": "./dist/index.js",
19 | "types": "./dist/index.d.ts",
20 | "exports": {
21 | ".": {
22 | "types": "./dist/index.d.ts",
23 | "import": "./dist/index.js",
24 | "default": "./dist/index.js"
25 | },
26 | "./*": "./*"
27 | },
28 | "scripts": {
29 | "check": "tsc --noEmit",
30 | "build": "vite build",
31 | "dev": "vite build --watch",
32 | "prepublishOnly": "npm run check && npm run build"
33 | },
34 | "author": "_Kerman",
35 | "repository": {
36 | "type": "git",
37 | "url": "git+https://github.com/KermanX/refina"
38 | },
39 | "readme": "https://github.com/KermanX/refina#readme",
40 | "bugs": "https://github.com/KermanX/refina/issues",
41 | "license": "MIT",
42 | "devDependencies": {
43 | "@refina/tsconfig": "workspace:^",
44 | "typescript": "^5.4.2",
45 | "vite": "^4.5.2",
46 | "vite-plugin-refina": "workspace:^"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/core/src/app/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./app";
2 | export * from "./hooks";
3 | export * from "./plugin";
4 |
--------------------------------------------------------------------------------
/packages/core/src/component/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./component";
2 | export * from "./trigger";
3 |
--------------------------------------------------------------------------------
/packages/core/src/component/trigger.ts:
--------------------------------------------------------------------------------
1 | import { _ } from "../context";
2 | import { Component } from "./component";
3 |
4 | /**
5 | * The base class of all trigger components.
6 | *
7 | * A trigger component is a component that can fire events with data.
8 | * The return value of the context function is whether the event is fired.
9 | */
10 | export abstract class TriggerComponent extends Component {
11 | private $_fired = false;
12 | private $_firedData: Ev;
13 |
14 | /**
15 | * Fire an event with data.
16 | *
17 | * @param data The data of the event.
18 | */
19 | protected readonly $fire = (data: Ev) => {
20 | this.$_fired = true;
21 | this.$_firedData = data;
22 | this.$app.recv();
23 | };
24 |
25 | /**
26 | * Create a function that fires an event with data.
27 | *
28 | * @param data The data of the event.
29 | * @returns A function that fires the event.
30 | */
31 | protected readonly $fireWith = (data: Ev) => () => this.$fire(data);
32 |
33 | protected get $fired() {
34 | if (_.$recvContext && this.$_fired) {
35 | this.$_fired = false;
36 | (_ as any).$ev = this.$_firedData;
37 | return true;
38 | } else {
39 | return false;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/core/src/constants.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The state of the app.
3 | */
4 | export enum AppState {
5 | /**
6 | * The app is not running.
7 | */
8 | IDLE = "idle",
9 |
10 | /**
11 | * The app is running to update DOM.
12 | */
13 | UPDATE = "update",
14 |
15 | /**
16 | * The app is running to receive events.
17 | */
18 | RECV = "recv",
19 | }
20 |
21 | // Add the `DEV` property to `import.meta.env`.
22 | // This property is used to check if the app is running in development mode.
23 | // See https://vitejs.dev/guide/env-and-mode.html#env-variables
24 | declare global {
25 | interface ImportMetaEnv {
26 | /**
27 | * Whether the app is running in development mode.
28 | *
29 | * In development mode, Refina will print detailed runtime information to the console,
30 | * and extra checks will be performed to ensure the app is running correctly.
31 | */
32 | DEV: boolean;
33 | }
34 |
35 | interface ImportMeta {
36 | readonly env: ImportMetaEnv;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/core/src/context/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./base";
2 | export * from "./recv";
3 | export * from "./update";
4 |
--------------------------------------------------------------------------------
/packages/core/src/data/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./model";
2 | export * from "./propModel";
3 | export * from "./ref";
4 |
--------------------------------------------------------------------------------
/packages/core/src/data/propModel.ts:
--------------------------------------------------------------------------------
1 | import { toModelValue, JustModel } from "./model";
2 |
3 | /**
4 | * Create a model that references a property of an object.
5 | *
6 | * @param obj The object of the property to reference.
7 | * @param key The key of the property to reference.
8 | * @returns The model.
9 | */
10 | export function propModel(obj: T, key: K) {
11 | return {
12 | obj,
13 | key,
14 |
15 | get value() {
16 | return this.obj[this.key];
17 | },
18 | set value(v: T[K]) {
19 | this.obj[this.key] = v;
20 | },
21 |
22 | [toModelValue]() {
23 | return this.value;
24 | },
25 | [Symbol.toPrimitive]() {
26 | return this.value;
27 | },
28 | } as JustModel & { obj: T; key: K };
29 | }
30 |
--------------------------------------------------------------------------------
/packages/core/src/dom/body.ts:
--------------------------------------------------------------------------------
1 | import { HTMLElementComponent } from "./element";
2 |
3 | /**
4 | * The body component that manages classes, styles and event listeners of the `` element.
5 | */
6 | export class DOMBodyComponent extends HTMLElementComponent {
7 | updateDOM(): null {
8 | this.applyCls();
9 | this.applyCss();
10 | this.applyEventListeners();
11 | return null;
12 | }
13 |
14 | insertAfter(_node: ChildNode): never {
15 | throw new Error("Cannot insert body component after another node.");
16 | }
17 |
18 | prependTo(_parent: Element): never {
19 | throw new Error("Cannot prepend body component to DOM tree.");
20 | }
21 |
22 | removeFrom(_parent: Element): never {
23 | throw new Error("Cannot remove body component from DOM tree.");
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/core/src/dom/content.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "../context";
2 | import { View } from "./view";
3 |
4 | /**
5 | * Allowed content types of DOM element components.
6 | */
7 | export type Content =
8 | | string
9 | | number
10 | | View;
11 |
12 | /**
13 | * Bind arguments to content if the content is a view function.
14 | *
15 | * @example
16 | * ```ts
17 | * const content: Content<[string]> = ...;
18 | * const boundContent: Content = bindArgsToContent(content, "Hello, world!");
19 | * _.div(boundContent);
20 | * ```
21 | *
22 | * @param content The content to bind arguments to.
23 | * @param args The arguments to bind.
24 | * @returns The bound content.
25 | */
26 | export function bindArgsToContent(
27 | content: Content,
28 | ...args: Args
29 | ): Content {
30 | if (typeof content === "function") {
31 | return () => content(...args);
32 | }
33 | return content;
34 | }
35 |
--------------------------------------------------------------------------------
/packages/core/src/dom/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./body";
2 | export * from "./content";
3 | export * from "./element";
4 | export * from "./node";
5 | export * from "./portal";
6 | export * from "./root";
7 | export * from "./text";
8 | export * from "./view";
9 | export * from "./window";
10 |
--------------------------------------------------------------------------------
/packages/core/src/dom/text.ts:
--------------------------------------------------------------------------------
1 | import { DOMNodeComponent } from "./node";
2 |
3 | /**
4 | * Component that contains a text node.
5 | */
6 | export class TextNodeComponent extends DOMNodeComponent {
7 | updateDOM(): null {
8 | return null;
9 | }
10 | }
11 |
12 | // Add text node function to context.
13 | declare module ".." {
14 | interface ContextFuncs {
15 | /**
16 | * Render a text node.
17 | *
18 | * @example
19 | * ```ts
20 | * _.t`Hello, world!`;
21 | * _.t(message);
22 | * ```
23 | */
24 | t(template: TemplateStringsArray, ...args: unknown[]): void;
25 | t(text: string): void;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/core/src/dom/view.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "../context";
2 |
3 | export type View = (
4 | ...args: Args
5 | ) => void;
6 |
7 | export function $view(
8 | view: View,
9 | ) {
10 | return view;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/core/src/dom/window.ts:
--------------------------------------------------------------------------------
1 | import { DOMElementComponent } from "./element";
2 |
3 | /**
4 | * The window component that manages event listeners of Window.
5 | *
6 | * **Note**: Window is not a DOM element in fact,
7 | * but we just treat it as a DOM element to reuse the code to manage event listeners.
8 | */
9 | export class DOMWindowComponent extends DOMElementComponent {
10 | updateDOM(): null {
11 | this.applyEventListeners();
12 | return null;
13 | }
14 |
15 | addCls(_classes: string): void {
16 | throw new Error("Cannot add classes to window.");
17 | }
18 |
19 | addCss(_style: string) {
20 | throw new Error("Cannot add styles to window.");
21 | }
22 |
23 | addAttrs(_attrs: Partial): void {
24 | throw new Error("Cannot add attrs to window.");
25 | }
26 |
27 | insertAfter(_node: ChildNode): never {
28 | throw new Error("Cannot insert window component after another node.");
29 | }
30 |
31 | prependTo(_parent: Element): never {
32 | throw new Error("Cannot prepend window component to DOM tree.");
33 | }
34 |
35 | removeFrom(_parent: Element): never {
36 | throw new Error("Cannot remove window component from DOM tree.");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./app";
2 | export * from "./component";
3 | export * from "./constants";
4 | export * from "./context";
5 | export * from "./data";
6 | export * from "./dom";
7 | export * from "./patch";
8 | export * from "./prelude";
9 |
--------------------------------------------------------------------------------
/packages/core/src/patch/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./patch";
2 | export * from "./parser";
3 |
--------------------------------------------------------------------------------
/packages/core/src/patch/parser.ts:
--------------------------------------------------------------------------------
1 | import { PatchTarget } from "./patch";
2 |
3 | export type SelectorNode = (name: string) => false | SelectorNode | PatchTarget;
4 |
5 | export function parseSelector(
6 | selector: string,
7 | patchTarget: PatchTarget,
8 | ): SelectorNode {
9 | if (!/^(>([a-z_][a-z0-9_]*|\*)(:(\d+|\*))?)+$/i.test(selector)) {
10 | throw new Error(`Invalid selector: ${selector}`);
11 | }
12 | const parts = selector.split(">").slice(1);
13 | let node: SelectorNode | undefined;
14 | for (let i = parts.length - 1; i >= 0; i--) {
15 | const part = parts[i];
16 | const next = node ?? patchTarget;
17 | const [name, index] = part.split(":");
18 | if (index === "*") {
19 | if (name === "*") {
20 | node = () => next;
21 | } else {
22 | node = v => v === name && next;
23 | }
24 | } else {
25 | let n = index ? parseInt(index) : 1;
26 | if (name === "*") {
27 | node = () => {
28 | n--;
29 | return n === 0 ? next : false;
30 | };
31 | } else {
32 | node = v => {
33 | if (v !== name) return false;
34 | n--;
35 | return n === 0 ? next : false;
36 | };
37 | }
38 | }
39 | }
40 | return () => node!;
41 | }
42 |
--------------------------------------------------------------------------------
/packages/core/src/prelude/documentTitle.ts:
--------------------------------------------------------------------------------
1 | import { $contextFunc, _ } from "../context";
2 |
3 | export const documentTitle = $contextFunc(_ckey =>
4 | /**
5 | * Set the document title.
6 | *
7 | * **Warning**: The document title will be overwritten by the last call to this function.
8 | */
9 | (title: string) => {
10 | if (_.$updateContext) {
11 | document.title = title;
12 | }
13 | return true as const;
14 | },
15 | );
16 |
--------------------------------------------------------------------------------
/packages/core/src/prelude/index.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "../app";
2 | import { _await } from "./await";
3 | import { documentTitle } from "./documentTitle";
4 | import { AsyncEmbed, Embed } from "./embed";
5 | import { _for, forTimes } from "./loop";
6 | import { portal } from "./portal";
7 | import { provide } from "./provide";
8 | import { now, setInterval } from "./timing";
9 | import { useModel } from "./useModel";
10 |
11 | const components = {
12 | asyncEmbed: AsyncEmbed,
13 | embed: Embed,
14 | };
15 |
16 | const contextFuncs = {
17 | await: _await,
18 | documentTitle,
19 | for: _for,
20 | forTimes,
21 | portal,
22 | provide,
23 | now,
24 | setInterval,
25 | useModel,
26 | };
27 |
28 | /**
29 | * The plugin that is always installed on the app.
30 | */
31 | export const Prelude = {
32 | name: "prelude",
33 | components,
34 | contextFuncs,
35 | onError(error: unknown) {
36 | console.error(error);
37 | },
38 | } satisfies Plugin;
39 |
40 | declare module ".." {
41 | interface Plugins {
42 | prelude: typeof Prelude;
43 | }
44 | }
45 |
46 | export type { AsyncContentLoader } from "./embed";
47 | export { byIndex, bySelf, type LoopKey } from "./loop";
48 |
--------------------------------------------------------------------------------
/packages/core/src/prelude/portal.ts:
--------------------------------------------------------------------------------
1 | import { $contextFunc, _ } from "../context";
2 | import { Content, DOMPortalComponent } from "../dom";
3 |
4 | export const portal = $contextFunc(ckey =>
5 | /**
6 | * Render content to the end of the root element.
7 | *
8 | * This is usefull when you want to render a dialog or a tooltip
9 | * that should not be affected by the parent element's styles.
10 | */
11 | (children: Content): void => {
12 | let portal = _.$lowlevel.$$currentRefNode[ckey] as
13 | | DOMPortalComponent
14 | | undefined;
15 | if (!portal) {
16 | portal = new DOMPortalComponent(_.$app.root.node);
17 | _.$lowlevel.$$currentRefNode[ckey] = portal;
18 | }
19 |
20 | const updateContext = _.$updateContext?.$lowlevel;
21 | if (updateContext) {
22 | updateContext.$$fulfillRef(portal);
23 |
24 | updateContext.$app.root.pendingPortals.push(portal);
25 |
26 | updateContext.$$updateDOMContent(portal, children);
27 | } else {
28 | const recvContext = _.$recvContext!.$lowlevel;
29 |
30 | recvContext.$$processDOMElement(ckey, children);
31 | }
32 | },
33 | );
34 |
--------------------------------------------------------------------------------
/packages/core/src/prelude/timing.ts:
--------------------------------------------------------------------------------
1 | import { $contextFunc, _ } from "../context";
2 |
3 | export const now = $contextFunc(ckey =>
4 | /**
5 | * Get the current time in milliseconds.
6 | *
7 | * For every `precisionMs`, an `UPDATE` call will be scheduled to refresh the time.
8 | *
9 | * @param precisionMs The precision of the time in milliseconds.
10 | */
11 | (precisionMs = 1000): number => {
12 | const refTreeNode = _.$lowlevel.$$currentRefNode;
13 | if (_.$updateContext && !refTreeNode[ckey]) {
14 | refTreeNode[ckey] = true;
15 | setTimeout(() => {
16 | delete refTreeNode[ckey];
17 | _.$app.update();
18 | }, precisionMs);
19 | }
20 | return Date.now();
21 | },
22 | );
23 |
24 | export const setInterval = $contextFunc(ckey =>
25 | /**
26 | * Schedule a callback to be called every `interval` milliseconds.
27 | */
28 | (callback: () => void, interval: number): void => {
29 | const refTreeNode = _.$lowlevel.$$currentRefNode;
30 | if (_.$updateContext && !refTreeNode[ckey]) {
31 | refTreeNode[ckey] = true;
32 | setTimeout(() => {
33 | delete refTreeNode[ckey];
34 | callback();
35 | _.$app.update();
36 | }, interval);
37 | }
38 | },
39 | );
40 |
--------------------------------------------------------------------------------
/packages/core/src/prelude/useModel.ts:
--------------------------------------------------------------------------------
1 | import { $contextFunc, _ } from "../context";
2 | import { JustModel, model } from "../data";
3 |
4 | export const useModel = $contextFunc(ckey =>
5 | /**
6 | * Create a model.
7 | *
8 | * @param init The initial value.
9 | */
10 | (init: T): JustModel => {
11 | const refTreeNode = _.$lowlevel.$$currentRefNode;
12 | if (!refTreeNode[ckey]) {
13 | refTreeNode[ckey] = model(init);
14 | }
15 | return refTreeNode[ckey] as JustModel;
16 | },
17 | );
18 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/core/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { RefinaLib } from "vite-plugin-refina";
3 |
4 | export default defineConfig({
5 | plugins: [RefinaLib()],
6 | });
7 |
--------------------------------------------------------------------------------
/packages/creator/README.md:
--------------------------------------------------------------------------------
1 | # `@refina/creator`
2 |
3 | [](https://www.npmjs.com/package/create-refina)
4 |
5 | The recommended way to start a Vite-powered Refina.js project
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [Getting Started](https://refina.vercel.app/guide/introduction.html)
11 | - [GitHub Repository](https://github.com/refinajs/refina).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
14 | ## Usage
15 |
16 | ```sh
17 | npm create refina@latest
18 | ```
19 |
--------------------------------------------------------------------------------
/packages/creator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-refina",
3 | "version": "0.6.0",
4 | "description": "An easy way to start a Refina project.",
5 | "keywords": [
6 | "refina",
7 | "creator",
8 | "template"
9 | ],
10 | "bin": {
11 | "create-refina": "dist/index.cjs"
12 | },
13 | "files": [
14 | "dist",
15 | "README.md"
16 | ],
17 | "engines": {
18 | "node": ">=v18"
19 | },
20 | "type": "module",
21 | "scripts": {
22 | "dev": "tsup --watch",
23 | "build": "tsup",
24 | "check": "tsc --noEmit",
25 | "prepublishOnly": "npm run build"
26 | },
27 | "author": "_Kerman",
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/KermanX/refina"
31 | },
32 | "readme": "https://github.com/KermanX/refina#readme",
33 | "bugs": "https://github.com/KermanX/refina/issues",
34 | "license": "MIT",
35 | "devDependencies": {
36 | "@types/node": "^20.11.26",
37 | "@types/prompts": "^2.4.9",
38 | "chalk": "^5.3.0",
39 | "latest-version": "^7.0.0",
40 | "prompts": "^2.4.2",
41 | "tsup": "^8.0.2",
42 | "typescript": "^5.4.2"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/app/basics+mdui.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean) => `/**
2 | * Refina.js + MdUI + Basic-components
3 | */
4 |
5 | import { $app, model } from "refina";
6 | import Basics from "@refina/basic-components";
7 | import MdUI from "@refina/mdui";
8 | import "./styles.css";
9 |
10 | let count = 0;
11 | const username = model("Refina");
12 |
13 | $app([Basics, MdUI], _ => {${tailwind ? `\n _.$cls\`underline\`;` : ""}
14 | _.h1(\`Hello \${username}!\`);
15 | _.mdButton(\`Count is: \${count}\`) && count++;
16 | _.mdTextField(username, "Username");
17 | });
18 |
19 | declare module "refina" {
20 | interface Plugins {
21 | Basics: typeof Basics;
22 | MdUI: typeof MdUI;
23 | }
24 | }
25 | `;
26 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/app/basics.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean) => `/**
2 | * Refina.js + Basic-components
3 | */
4 |
5 | import { $app } from "refina";
6 | import Basics from "@refina/basic-components";
7 | import "./styles.css";
8 |
9 | $app([Basics], _ => {${tailwind ? `\n _.$cls\`text-xl font-bold p-4\`;` : ""}
10 | _.h1("Hello, Refina!");
11 | _.p(_ => {${tailwind ? `\n _.$cls\`block p-2 hover:bg-gray-200\`;` : ""}
12 | _.a("Visit Refina on GitHub", "https://github.com/refinajs/refina");${
13 | tailwind
14 | ? `\n _.$cls\`block p-2 hover:bg-gray-200\`;`
15 | : "\n _._br();"
16 | }
17 | _.a("Visit Refina's documentation", "https://refina.vercel.app");
18 | });
19 | });
20 | `;
21 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/app/intrinsic.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean) => `/**
2 | * Refina.js with no component library
3 | */
4 |
5 | import { $app } from "refina";
6 | import "./styles.css";
7 |
8 | $app([], _ => {${tailwind ? `\n _.$cls\`text-xl font-bold p-4\`;` : ""}
9 | _._h1({}, "Hello, Refina!");
10 | _._p({}, _ => {${
11 | tailwind ? `\n _.$cls\`block p-2 hover:bg-gray-200\`;` : ""
12 | }
13 | _._a(
14 | {
15 | href: "https://github.com/refinajs/refina",
16 | },
17 | "Visit Refina on GitHub",
18 | );${
19 | tailwind
20 | ? `\n _.$cls\`block p-2 hover:bg-gray-200\`;`
21 | : "\n _._br();"
22 | }
23 | _._a(
24 | {
25 | href: "https://refina.vercel.app",
26 | },
27 | "Visit Refina's documentation",
28 | );
29 | });
30 | });
31 | `;
32 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/app/mdui.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean) => `/**
2 | * Refina.js + MdUI
3 | */
4 |
5 | import { $app, model } from "refina";
6 | import MdUI from "@refina/mdui";
7 | import "./styles.css";
8 |
9 | let count = 0;
10 | const username = model("Refina");
11 |
12 | $app([MdUI], _ => {${tailwind ? `\n _.$cls\`underline\`;` : ""}
13 | _._h1({}, \`Hello \${username}!\`);
14 | _.mdButton(\`Count is: \${count}\`) && count++;
15 | _.mdTextField(username, "Username");
16 | });
17 |
18 | declare module "refina" {
19 | interface Plugins {
20 | MdUI: typeof MdUI;
21 | }
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/extensions.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean, prettier: boolean) => {
2 | if (!tailwind && !prettier) return null;
3 |
4 | const recommendations: string[] = [];
5 |
6 | if (tailwind) {
7 | recommendations.push("bradlc.vscode-tailwindcss");
8 | }
9 |
10 | if (prettier) {
11 | recommendations.push("esbenp.prettier-vscode");
12 | }
13 |
14 | return JSON.stringify(
15 | {
16 | recommendations,
17 | },
18 | null,
19 | 2,
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/gitignore.ts:
--------------------------------------------------------------------------------
1 | export default `# Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .idea
22 | *.suo
23 | *.ntvs*
24 | *.njsproj
25 | *.sln
26 | *.sw?
27 |
28 | *.tsbuildinfo`;
29 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/html/base.ts:
--------------------------------------------------------------------------------
1 | export default (head: string) => `
2 |
3 | ${head}
4 |
5 |
6 |
7 |
8 |
9 |
10 | `;
11 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/html/mdui.ts:
--------------------------------------------------------------------------------
1 | export default `
2 | `;
6 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/postcssConfig.ts:
--------------------------------------------------------------------------------
1 | export default `export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 | `;
8 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/prettierrc.ts:
--------------------------------------------------------------------------------
1 | export default `{
2 | "arrowParens": "avoid"
3 | }
4 | `;
5 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/settings.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean, prettier: boolean) =>
2 | JSON.stringify(
3 | {
4 | "editor.guides.bracketPairs": "active",
5 | "editor.quickSuggestions": tailwind
6 | ? {
7 | strings: "on",
8 | }
9 | : undefined,
10 | "tailwindCSS.experimental.classRegex": tailwind
11 | ? [
12 | ["\\$cls`([\\s\\S]*?)`", "(\\S+)"],
13 | ["\\$cls\\(([^\\)]*)\\)", '\\"(.*?)\\"'],
14 | ["\\$cls\\(([^\\)]*)\\)", "`(.*?)`"],
15 | ["addCls\\(([^\\)]*)\\)", '\\"(.*?)\\"'],
16 | ["addCls\\(([^\\)]*)\\)", "`(.*?)`"],
17 | ["\\$clsFunc`([\\s\\S]*?)`", "(\\S+)"],
18 | ["\\$clsFunc\\(([^\\)]*)\\)", '\\"(.*?)\\"'],
19 | ["\\$clsStr`([\\s\\S]*?)`", "(\\S+)"],
20 | ["\\$clsStr\\(([^\\)]*)\\)", '\\"(.*?)\\"'],
21 | ]
22 | : undefined,
23 | "editor.defaultFormatter": prettier
24 | ? "esbenp.prettier-vscode"
25 | : undefined,
26 | },
27 | null,
28 | 2,
29 | );
30 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/styles.ts:
--------------------------------------------------------------------------------
1 | export default (tailwind: boolean, mdui: boolean) =>
2 | (mdui ? `@import url(@refina/mdui/styles.css);\n` : ``) +
3 | (tailwind
4 | ? (mdui ? `` : `@tailwind base;\n`) +
5 | `@tailwind components;\n@tailwind utilities;\n`
6 | : `\n`);
7 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/tailwindConfig.ts:
--------------------------------------------------------------------------------
1 | export default `import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: ["./index.html", "./src/**/*.{js,ts}"],
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [],
9 | } satisfies Config;
10 | `;
11 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/tsconfig.ts:
--------------------------------------------------------------------------------
1 | export default `{
2 | "extends": "@refina/tsconfig",
3 | "include": ["src"]
4 | }
5 | `;
6 |
--------------------------------------------------------------------------------
/packages/creator/src/templates/viteConfig.ts:
--------------------------------------------------------------------------------
1 | export default `import { defineConfig } from "vite";
2 | import Refina from "vite-plugin-refina";
3 |
4 | export default defineConfig({
5 | plugins: [Refina()],
6 | });
7 | `;
8 |
--------------------------------------------------------------------------------
/packages/creator/src/utils/banner.ts:
--------------------------------------------------------------------------------
1 | import chalk from "chalk";
2 |
3 | const text = `Refina.js - An extremely refined web framework`;
4 | const startColor = [254, 204, 143];
5 | const endColor = [255, 85, 0];
6 |
7 | function getColor(index: number) {
8 | const percent = index / text.length;
9 | const color = startColor.map((start, i) => {
10 | const end = endColor[i];
11 | const value = Math.round(start + (end - start) * percent);
12 | return value;
13 | });
14 | return color as [number, number, number];
15 | }
16 |
17 | function getColoredText() {
18 | return text
19 | .split("")
20 | .map((char, index) => {
21 | const color = getColor(index);
22 | return chalk.rgb(...color)(char);
23 | })
24 | .join("");
25 | }
26 |
27 | export const banner = getColoredText();
28 |
--------------------------------------------------------------------------------
/packages/creator/src/utils/pkgManager.ts:
--------------------------------------------------------------------------------
1 | const userAgent = process.env.npm_config_user_agent ?? "";
2 | export const packageManager = /pnpm/.test(userAgent)
3 | ? "pnpm"
4 | : /yarn/.test(userAgent)
5 | ? "yarn"
6 | : "npm";
7 |
8 | export const runCommand = packageManager === "npm" ? "npm run" : packageManager;
9 |
--------------------------------------------------------------------------------
/packages/creator/src/utils/pkgName.ts:
--------------------------------------------------------------------------------
1 | // Copied from: https://github.com/vuejs/create-vue/blob/main/index.ts
2 |
3 | export function isValidPackageName(projectName: string) {
4 | return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(
5 | projectName,
6 | );
7 | }
8 |
9 | export function toValidPackageName(projectName: string) {
10 | return projectName
11 | .trim()
12 | .toLowerCase()
13 | .replace(/\s+/g, "-")
14 | .replace(/^[._]/, "")
15 | .replace(/[^a-z0-9-~]+/g, "-");
16 | }
17 |
--------------------------------------------------------------------------------
/packages/creator/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src"],
3 | "compilerOptions": {
4 | "target": "ES2019",
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "lib": ["ES2019"],
8 | "strict": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/creator/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from "tsup";
2 |
3 | export const tsup: Options = {
4 | entry: ["./src/index.ts"],
5 | format: ["cjs"],
6 | noExternal: ["chalk", "prompts", "lastest-version"],
7 | target: "es2019",
8 | splitting: false,
9 | };
10 |
--------------------------------------------------------------------------------
/packages/examples/fluentui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/examples/fluentui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/example-fluentui",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "dev": "vite --open",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "tsc --noEmit"
11 | },
12 | "exports": {
13 | ".": "./src/app.ts"
14 | },
15 | "devDependencies": {
16 | "@refina/tsconfig": "workspace:^",
17 | "typescript": "^5.4.2",
18 | "vite": "^4.5.2",
19 | "vite-plugin-inspect": "^0.7.42",
20 | "vite-plugin-refina": "workspace:^"
21 | },
22 | "dependencies": {
23 | "@refina/fluentui": "workspace:^",
24 | "@refina/fluentui-icons": "workspace:^",
25 | "refina": "workspace:^"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/examples/fluentui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/examples/fluentui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, Plugin } from "vite";
2 | import Inspect from "vite-plugin-inspect";
3 | import Refina from "vite-plugin-refina";
4 |
5 | export default defineConfig({
6 | plugins: [Inspect(), Refina() as Plugin[]],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/examples/gallery/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/examples/gallery/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/gallery",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "dev": "vite --open",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "tsc --noEmit"
11 | },
12 | "devDependencies": {
13 | "@refina/tsconfig": "workspace:^",
14 | "autoprefixer": "^10.4.18",
15 | "postcss": "^8.4.35",
16 | "tailwindcss": "^3.4.1",
17 | "typescript": "^5.4.2",
18 | "vite": "^4.5.2",
19 | "vite-plugin-ms-clarity": "^1.0.0",
20 | "vite-plugin-refina": "workspace:^"
21 | },
22 | "dependencies": {
23 | "@refina/basic-components": "workspace:^",
24 | "@refina/example-fluentui": "workspace:^",
25 | "@refina/example-gh-login": "workspace:^",
26 | "@refina/example-mdui": "workspace:^",
27 | "refina": "workspace:^"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/examples/gallery/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/examples/gallery/src/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/packages/examples/gallery/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/examples/gallery/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { Plugin, defineConfig } from "vite";
2 | import Refina from "vite-plugin-refina";
3 | import MsClarity from "vite-plugin-ms-clarity";
4 |
5 | export default defineConfig({
6 | plugins: [Refina() as Plugin[], MsClarity("k9vjx99oj1")],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/assets/github-mark-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/example-gh-login",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "dev": "vite --open",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "tsc --noEmit"
11 | },
12 | "exports": {
13 | ".": "./src/app.ts"
14 | },
15 | "devDependencies": {
16 | "@refina/tsconfig": "workspace:^",
17 | "autoprefixer": "^10.4.18",
18 | "postcss": "^8.4.35",
19 | "tailwindcss": "^3.4.1",
20 | "typescript": "^5.4.2",
21 | "vite": "^4.5.2",
22 | "vite-plugin-inspect": "^0.7.42",
23 | "vite-plugin-refina": "workspace:^"
24 | },
25 | "dependencies": {
26 | "@refina/basic-components": "workspace:^",
27 | "refina": "workspace:^"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/src/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/examples/gh-login/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import Inspect from "vite-plugin-inspect";
3 | import Refina from "vite-plugin-refina";
4 |
5 | export default defineConfig({
6 | plugins: [Inspect(), Refina()],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/examples/mdui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/examples/mdui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/example-mdui",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "dev": "vite --open",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "tsc --noEmit"
11 | },
12 | "exports": {
13 | ".": "./src/app.ts"
14 | },
15 | "devDependencies": {
16 | "@refina/tsconfig": "workspace:^",
17 | "typescript": "^5.4.2",
18 | "vite": "^4.5.2",
19 | "vite-plugin-inspect": "^0.7.42",
20 | "vite-plugin-refina": "workspace:^"
21 | },
22 | "dependencies": {
23 | "@refina/mdui": "workspace:^",
24 | "refina": "workspace:^"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/examples/mdui/src/styles.css:
--------------------------------------------------------------------------------
1 | @import url(@refina/mdui/styles.css);
2 |
--------------------------------------------------------------------------------
/packages/examples/mdui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/examples/mdui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import Inspect from "vite-plugin-inspect";
3 | import Refina from "vite-plugin-refina";
4 |
5 | export default defineConfig({
6 | plugins: [Inspect(), Refina()],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/fluentui-icons/README.md:
--------------------------------------------------------------------------------
1 | # `@refina/fluentui-icons`
2 |
3 | [](https://www.npmjs.com/package/@refina/fluentui-icons)
4 |
5 | The [FluentUI Icons](https://react.fluentui.dev/?path=/docs/icons-catalog--page) bundle for Refina.
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [Getting Started](https://refina.vercel.app/guide/introduction.html)
11 | - [GitHub Repository](https://github.com/refinajs/refina).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
--------------------------------------------------------------------------------
/packages/fluentui-icons/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./generator.ts"],
3 | "exclude": ["./dist"],
4 | "compilerOptions": {
5 | "strict": true,
6 | "moduleResolution": "NodeNext",
7 | "module": "NodeNext",
8 | "target": "ESNext",
9 | "lib": ["ESNext"],
10 | "composite": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/fluentui/README.md:
--------------------------------------------------------------------------------
1 | # `@refina/fluentui`
2 |
3 | [](https://www.npmjs.com/package/@refina/fluentui)
4 |
5 | The [FluentUI](https://react.fluentui.dev) components for Refina.
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [Getting Started](https://refina.vercel.app/guide/introduction.html)
11 | - [GitHub Repository](https://github.com/refinajs/refina).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/accordion/accordion/item.styles.ts:
--------------------------------------------------------------------------------
1 | import { defineStyles } from "@refina/griffel";
2 |
3 | export const accordionItemClassNames = {
4 | root: "fui-AccordionItem",
5 | } as const;
6 |
7 | export default () =>
8 | defineStyles({
9 | root: [accordionItemClassNames.root],
10 | });
11 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/accordion/accordionPanel/index.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 | import useStyles from "./styles";
3 |
4 | export class FAccordionPanel extends Component {
5 | $main(children: Content): void {
6 | const styles = useStyles();
7 |
8 | styles.root;
9 | _._div({}, children);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/accordion/accordionPanel/styles.ts:
--------------------------------------------------------------------------------
1 | import { tokens } from "@fluentui/tokens";
2 | import { defineStyles, makeStyles, shorthands } from "@refina/griffel";
3 |
4 | export const accordionPanelClassNames = {
5 | root: "fui-AccordionPanel",
6 | } as const;
7 |
8 | /**
9 | * Styles for the root slot
10 | */
11 | const styles = makeStyles({
12 | root: {
13 | ...shorthands.margin(0, tokens.spacingHorizontalM),
14 | },
15 | });
16 |
17 | export default () =>
18 | defineStyles({
19 | root: [accordionPanelClassNames.root, styles.root],
20 | });
21 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/accordion/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./accordion";
2 | export * from "./accordionPanel";
3 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/avatar/colors.ts:
--------------------------------------------------------------------------------
1 | import { FAvatarNamedColor } from "./types";
2 |
3 | const avatarColors: FAvatarNamedColor[] = [
4 | "dark-red",
5 | "cranberry",
6 | "red",
7 | "pumpkin",
8 | "peach",
9 | "marigold",
10 | "gold",
11 | "brass",
12 | "brown",
13 | "forest",
14 | "seafoam",
15 | "dark-green",
16 | "light-teal",
17 | "teal",
18 | "steel",
19 | "blue",
20 | "royal-blue",
21 | "cornflower",
22 | "navy",
23 | "lavender",
24 | "purple",
25 | "grape",
26 | "lilac",
27 | "pink",
28 | "magenta",
29 | "plum",
30 | "beige",
31 | "mink",
32 | "platinum",
33 | "anchor",
34 | ];
35 |
36 | const getHashCode = (str: string): number => {
37 | let hashCode = 0;
38 | for (let len: number = str.length - 1; len >= 0; len--) {
39 | const ch = str.charCodeAt(len);
40 | const shift = len % 8;
41 | hashCode ^= (ch << shift) + (ch >> (8 - shift)); // eslint-disable-line no-bitwise
42 | }
43 |
44 | return hashCode;
45 | };
46 |
47 | export function getColor(name: string) {
48 | return avatarColors[getHashCode(name) % avatarColors.length];
49 | }
50 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/avatar/types.ts:
--------------------------------------------------------------------------------
1 | export type FAvatarShape = "circular" | "square";
2 | export type FAvatarActive = "active" | "inactive" | "unset";
3 | export type FAvatarNamedColor =
4 | | "dark-red"
5 | | "cranberry"
6 | | "red"
7 | | "pumpkin"
8 | | "peach"
9 | | "marigold"
10 | | "gold"
11 | | "brass"
12 | | "brown"
13 | | "forest"
14 | | "seafoam"
15 | | "dark-green"
16 | | "light-teal"
17 | | "teal"
18 | | "steel"
19 | | "blue"
20 | | "royal-blue"
21 | | "cornflower"
22 | | "navy"
23 | | "lavender"
24 | | "purple"
25 | | "grape"
26 | | "lilac"
27 | | "pink"
28 | | "magenta"
29 | | "plum"
30 | | "beige"
31 | | "mink"
32 | | "platinum"
33 | | "anchor";
34 | export type FAvatarColor = "neutral" | "brand" | "colorful" | FAvatarNamedColor;
35 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/breadcrumb/divider.styles.ts:
--------------------------------------------------------------------------------
1 | import { defineStyles, makeStyles } from "@refina/griffel";
2 |
3 | export const breadcrumbDividerClassNames = {
4 | root: "fui-BreadcrumbDivider",
5 | };
6 |
7 | /**
8 | * Styles for the root slot
9 | */
10 | const styles = makeStyles({
11 | root: {
12 | display: "flex",
13 | },
14 | });
15 |
16 | export default () =>
17 | defineStyles({
18 | root: [breadcrumbDividerClassNames.root, styles.root],
19 | });
20 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/breadcrumb/item.styles.ts:
--------------------------------------------------------------------------------
1 | import { tokens } from "@fluentui/tokens";
2 | import { defineStyles, makeResetStyles } from "@refina/griffel";
3 |
4 | export const breadcrumbItemClassNames = {
5 | root: "fui-BreadcrumbItem",
6 | };
7 |
8 | const breadcrumbItemResetStyles = makeResetStyles({
9 | display: "flex",
10 | alignItems: "center",
11 | color: tokens.colorNeutralForeground2,
12 | boxSizing: "border-box",
13 | textWrap: "nowrap",
14 | });
15 |
16 | export default () =>
17 | defineStyles({
18 | root: [breadcrumbItemClassNames.root, breadcrumbItemResetStyles],
19 | });
20 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/breadcrumb/styles.ts:
--------------------------------------------------------------------------------
1 | import { defineStyles, makeResetStyles } from "@refina/griffel";
2 |
3 | export const breadcrumbClassNames = {
4 | root: "fui-Breadcrumb",
5 | list: "fui-Breadcrumb__list",
6 | };
7 |
8 | const listClassName = makeResetStyles({
9 | listStyleType: "none",
10 | display: "flex",
11 | alignItems: "center",
12 | margin: 0,
13 | padding: 0,
14 | });
15 |
16 | export default () =>
17 | defineStyles({
18 | root: [breadcrumbClassNames.root],
19 | list: [listClassName, breadcrumbClassNames.list],
20 | });
21 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/button/types.ts:
--------------------------------------------------------------------------------
1 | export type FButtonShape = "circular" | "rounded" | "square";
2 | export type FButtonApperance =
3 | | "outline"
4 | | "primary"
5 | | "secondary"
6 | | "subtle"
7 | | "transparent";
8 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/checkbox/types.ts:
--------------------------------------------------------------------------------
1 | export type FCheckboxState = true | false | "mixed";
2 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/checkbox/utils.ts:
--------------------------------------------------------------------------------
1 | import type { FCheckboxState } from "./types";
2 |
3 | /**
4 | * @returns the mixed state if any of the sources is mixed, true if any of the sources is true and none is false, false otherwise
5 | */
6 | export function calcMixedCheckboxState(
7 | sources: (boolean | FCheckboxState)[],
8 | ): FCheckboxState {
9 | const sourcesSet = new Set(sources);
10 | return sourcesSet.has("mixed")
11 | ? "mixed"
12 | : sourcesSet.has(true)
13 | ? sourcesSet.has(false)
14 | ? "mixed"
15 | : true
16 | : false;
17 | }
18 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dialog/constants.ts:
--------------------------------------------------------------------------------
1 | export const MEDIA_QUERY_BREAKPOINT_SELECTOR =
2 | "@media screen and (max-width: 480px)";
3 | export const SURFACE_PADDING = "24px";
4 | export const DIALOG_GAP = "8px";
5 | export const SURFACE_BORDER_WIDTH = "1px";
6 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dialog/dialogBody/body.styles.ts:
--------------------------------------------------------------------------------
1 | import { defineStyles, makeResetStyles, shorthands } from "@refina/griffel";
2 | import {
3 | DIALOG_GAP,
4 | MEDIA_QUERY_BREAKPOINT_SELECTOR,
5 | SURFACE_PADDING,
6 | } from "../constants";
7 |
8 | export const dialogBodyClassNames = {
9 | root: "fui-DialogBody",
10 | } as const;
11 |
12 | /**
13 | * Styles for the root slot
14 | */
15 | const resetStyles = makeResetStyles({
16 | ...shorthands.overflow("unset"),
17 | ...shorthands.gap(DIALOG_GAP),
18 | display: "grid",
19 | maxHeight: `calc(100vh - 2 * ${SURFACE_PADDING})`,
20 | boxSizing: "border-box",
21 | gridTemplateRows: "auto 1fr",
22 | gridTemplateColumns: "1fr 1fr auto",
23 |
24 | [MEDIA_QUERY_BREAKPOINT_SELECTOR]: {
25 | maxWidth: "100vw",
26 | gridTemplateRows: "auto 1fr auto",
27 | },
28 | });
29 |
30 | export default () =>
31 | defineStyles({
32 | root: [dialogBodyClassNames.root, resetStyles],
33 | });
34 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dialog/dialogBody/content.styles.ts:
--------------------------------------------------------------------------------
1 | import { tokens, typographyStyles } from "@fluentui/tokens";
2 | import { defineStyles, makeResetStyles, shorthands } from "@refina/griffel";
3 |
4 | export const dialogContentClassNames = {
5 | root: "fui-DialogContent",
6 | } as const;
7 |
8 | /**
9 | * Styles for the root slot
10 | */
11 | const styles = makeResetStyles({
12 | ...shorthands.padding(tokens.strokeWidthThick),
13 | ...shorthands.margin(`calc(${tokens.strokeWidthThick} * -1)`),
14 | ...typographyStyles.body1,
15 | overflowY: "auto",
16 | minHeight: "32px",
17 | boxSizing: "border-box",
18 | gridRowStart: 2,
19 | gridRowEnd: 2,
20 | gridColumnStart: 1,
21 | gridColumnEnd: 4,
22 | });
23 |
24 | export default () =>
25 | defineStyles({
26 | root: [dialogContentClassNames.root, styles],
27 | });
28 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dialog/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./dialog";
2 | export * from "./dialogBody";
3 | export * from "./dialogSurface";
4 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/divider/index.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 | import useStyles from "./styles";
3 | import { DividerContentAlignment } from "./types";
4 |
5 | export class FDivider extends Component {
6 | $main(
7 | children: Content | undefined,
8 | alignContent: DividerContentAlignment = "center",
9 | ): void {
10 | const styles = useStyles(alignContent, false, children == undefined);
11 |
12 | styles.root();
13 | _._div({}, _ => styles.wrapper() && _._div({}, children));
14 | }
15 | }
16 |
17 | export * from "./types";
18 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/divider/types.ts:
--------------------------------------------------------------------------------
1 | export type DividerContentAlignment = "start" | "end" | "center";
2 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dropdown/listbox.styles.ts:
--------------------------------------------------------------------------------
1 | import { tokens } from "@fluentui/tokens";
2 | import { defineStyles, makeStyles, shorthands } from "@refina/griffel";
3 |
4 | export const listboxClassNames = {
5 | root: "fui-Listbox",
6 | } as const;
7 |
8 | const styles = makeStyles({
9 | root: {
10 | backgroundColor: tokens.colorNeutralBackground1,
11 | boxSizing: "border-box",
12 | display: "flex",
13 | flexDirection: "column",
14 | minWidth: "160px",
15 | overflowY: "auto",
16 | ...shorthands.outline("1px", "solid", tokens.colorTransparentStroke),
17 | ...shorthands.padding(tokens.spacingHorizontalXS),
18 | rowGap: tokens.spacingHorizontalXXS,
19 | },
20 | });
21 |
22 | export default () =>
23 | defineStyles({
24 | root: [listboxClassNames.root, styles.root],
25 | });
26 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dropdown/tokens.ts:
--------------------------------------------------------------------------------
1 | export const iconSizes = {
2 | small: "16px",
3 | medium: "20px",
4 | large: "24px",
5 | } as const;
6 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/dropdown/types.ts:
--------------------------------------------------------------------------------
1 | export type FDropdownAppearance =
2 | | "filled-darker"
3 | | "filled-lighter"
4 | | "outline"
5 | | "underline";
6 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/field/types.ts:
--------------------------------------------------------------------------------
1 | export type FFieldValidationState = "error" | "warning" | "success" | "none";
2 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./accordion";
2 | export * from "./avatar";
3 | export * from "./breadcrumb";
4 | export * from "./button";
5 | export * from "./checkbox";
6 | export * from "./dialog";
7 | export * from "./divider";
8 | export * from "./dropdown";
9 | export * from "./field";
10 | export * from "./input";
11 | export * from "./label";
12 | export * from "./popover";
13 | export * from "./portal";
14 | export * from "./progressBar";
15 | export * from "./slider";
16 | export * from "./switch";
17 | export * from "./tab";
18 | export * from "./textarea";
19 | export * from "./tooltip";
20 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/input/types.ts:
--------------------------------------------------------------------------------
1 | export type FInputAppearance = "outline" | "underline" | "filled";
2 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/label/index.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 | import useStyles from "./styles";
3 |
4 | export class FLabel extends Component {
5 | $main(
6 | content: Content,
7 | required: Content | boolean = false,
8 | disabled = false,
9 | ): void {
10 | const requiredContent = typeof required === "boolean" ? "*" : required;
11 |
12 | const styles = useStyles(disabled);
13 |
14 | styles.root();
15 | _._label({}, _ => {
16 | _._span({}, content);
17 | if (required !== false) {
18 | styles.required();
19 | _._span({}, requiredContent);
20 | }
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/popover/constants.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @internal
3 | * The default value of the tooltip's border radius (borderRadiusMedium).
4 | *
5 | * Unfortunately, Popper requires it to be specified as a variable instead of using CSS.
6 | * While we could use getComputedStyle, that adds a performance penalty for something that
7 | * will likely never change.
8 | */
9 | export const popoverSurfaceBorderRadius = 4;
10 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/portal/index.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 | import useStyles from "./styles";
3 |
4 | export class FPortal extends Component {
5 | $main(children: Content): void {
6 | const styles = useStyles();
7 | _.portal(_ => styles.root() && _._div({}, children));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/portal/styles.ts:
--------------------------------------------------------------------------------
1 | import { defineStyles, makeStyles } from "@refina/griffel";
2 |
3 | const portalMountNodeStyles = makeStyles({
4 | root: {
5 | // Creates new stacking context to prevent z-index issues
6 | // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context
7 | //
8 | // Also keeps a portal on top of a page to prevent scrollbars from appearing
9 | position: "absolute",
10 | top: 0,
11 | left: 0,
12 | right: 0,
13 |
14 | zIndex: 1000000,
15 | },
16 | });
17 |
18 | export default () =>
19 | defineStyles({
20 | root: [portalMountNodeStyles.root],
21 | });
22 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/progressBar/index.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 | import useStyles from "./styles";
3 | import { FProgressBarColor, FProgressBarValue } from "./types";
4 |
5 | export class FProgressBar extends Component {
6 | $main(value: FProgressBarValue, color?: FProgressBarColor): void {
7 | const styles = useStyles(value, color);
8 |
9 | styles.root();
10 | _._div({}, _ => {
11 | styles.bar();
12 | if (value !== "indertermine")
13 | _.$css(`width: ${Math.min(100, Math.max(0, value * 100))}%`);
14 | _._div();
15 | });
16 | }
17 | }
18 |
19 | export * from "./types";
20 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/progressBar/types.ts:
--------------------------------------------------------------------------------
1 | export type FProgressBarValue = number | "indertermine";
2 | export type FProgressBarColor = "brand" | "success" | "warning" | "error";
3 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/switch/index.ts:
--------------------------------------------------------------------------------
1 | import { FiCircleFilled } from "@refina/fluentui-icons/circle";
2 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
3 | import { FLabel } from "../label";
4 | import useStyles from "./styles";
5 |
6 | export class FSwitch extends TriggerComponent {
7 | inputRef = elementRef<"input">();
8 | $main(
9 | label: string,
10 | state: Model,
11 | disabled = false,
12 | ): this is {
13 | $ev: boolean;
14 | } {
15 | const stateValue = unwrap(state);
16 |
17 | const styles = useStyles();
18 |
19 | styles.root();
20 | _._div(
21 | {
22 | onclick: () => {
23 | if (!disabled) {
24 | const newState = !stateValue;
25 | this.$updateModel(state, newState);
26 | this.$fire(newState);
27 | }
28 | },
29 | },
30 | _ => {
31 | styles.input();
32 | _.$ref(this.inputRef);
33 | _._input({
34 | type: "checkbox",
35 | disabled: disabled,
36 | checked: stateValue,
37 | });
38 | styles.indicator();
39 | _._div({}, _ => _(FiCircleFilled)());
40 | styles.label();
41 | _(FLabel)(label);
42 | },
43 | );
44 | return this.$fired;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/tab/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./tab";
2 | export * from "./tabList";
3 | export * from "./tabs";
4 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/tab/tab/index.ts:
--------------------------------------------------------------------------------
1 | import { Content, TriggerComponent, _ } from "refina";
2 | import useStyles from "./styles";
3 |
4 | export class FTab extends TriggerComponent {
5 | $main(
6 | selected: boolean,
7 | content: Content,
8 | disabled = false,
9 | animating = false,
10 | ): this is {
11 | $ev: void;
12 | } {
13 | const styles = useStyles(disabled, selected, animating, false, false);
14 |
15 | styles.root();
16 | _._button(
17 | {
18 | onclick: this.$fireWith(),
19 | disabled: disabled,
20 | },
21 | _ => {
22 | styles.content();
23 | _._span({}, content);
24 |
25 | styles.contentReservedSpace();
26 | _._span({}, content);
27 | },
28 | );
29 | return this.$fired;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/tab/tabList/styles.ts:
--------------------------------------------------------------------------------
1 | import { defineStyles, makeStyles } from "@refina/griffel";
2 |
3 | export const tabListClassNames = {
4 | root: "fui-TabList",
5 | } as const;
6 |
7 | /**
8 | * Styles for the root slot
9 | */
10 | const styles = makeStyles({
11 | root: {
12 | display: "flex",
13 | flexDirection: "row",
14 | flexShrink: 0,
15 | flexWrap: "nowrap",
16 | position: "relative",
17 | },
18 | horizontal: {
19 | alignItems: "stretch",
20 | flexDirection: "row",
21 | },
22 | vertical: {
23 | alignItems: "stretch",
24 | flexDirection: "column",
25 | },
26 | });
27 |
28 | /**
29 | * Apply styling to the TabList slots based on the state
30 | */
31 | export default (vertical: boolean) =>
32 | defineStyles({
33 | root: [
34 | tabListClassNames.root,
35 | styles.root,
36 | vertical ? styles.vertical : styles.horizontal,
37 | ],
38 | });
39 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/tab/tabs/styles.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * WARNING: This style file is non-standard.
3 | * Copied from https://react.fluentui.dev/?path=/docs/components-tablist--default#with-panels
4 | */
5 | import { defineStyles, makeStyles, shorthands } from "@refina/griffel";
6 |
7 | const styles = makeStyles({
8 | root: {
9 | alignItems: "flex-start",
10 | display: "flex",
11 | flexDirection: "column",
12 | justifyContent: "flex-start",
13 | ...shorthands.padding("50px", "20px"),
14 | rowGap: "20px",
15 | },
16 | panels: {
17 | ...shorthands.padding(0, "10px"),
18 | "& th": {
19 | textAlign: "left",
20 | ...shorthands.padding(0, "30px", 0, 0),
21 | },
22 | },
23 | });
24 |
25 | export default () =>
26 | defineStyles({
27 | root: [styles.root],
28 | panels: [styles.panels],
29 | });
30 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/textarea/types.ts:
--------------------------------------------------------------------------------
1 | export type FTextareaAppearance =
2 | | "outline"
3 | | "filled-darker"
4 | | "filled-lighter";
5 |
6 | export type FTextareaResize = "none" | "horizontal" | "vertical" | "both";
7 |
--------------------------------------------------------------------------------
/packages/fluentui/src/components/tooltip/constants.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The height of the tooltip's arrow in pixels.
3 | */
4 | export const arrowHeight = 6;
5 |
6 | /**
7 | * The default value of the tooltip's border radius (borderRadiusMedium).
8 | *
9 | * Unfortunately, Popper requires it to be specified as a variable instead of using CSS.
10 | * While we could use getComputedStyle, that adds a performance penalty for something that
11 | * will likely never change.
12 | */
13 | export const tooltipBorderRadius = 4;
14 |
--------------------------------------------------------------------------------
/packages/fluentui/src/focus/constants.ts:
--------------------------------------------------------------------------------
1 | export const KEYBOARD_NAV_ATTRIBUTE = "data-keyboard-nav" as const;
2 | export const KEYBOARD_NAV_SELECTOR =
3 | `:global([${KEYBOARD_NAV_ATTRIBUTE}])` as const;
4 |
5 | /**
6 | * @internal
7 | */
8 | export const FOCUS_VISIBLE_ATTR = "data-fui-focus-visible";
9 |
10 | /**
11 | * @internal
12 | */
13 | export const FOCUS_WITHIN_ATTR = "data-fui-focus-within";
14 | export const defaultOptions = {
15 | style: {},
16 | selector: "focus",
17 | customizeSelector: (selector: string) => selector,
18 | } as const;
19 |
--------------------------------------------------------------------------------
/packages/fluentui/src/focus/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./createCustomFocusIndicatorStyle";
2 | export * from "./createFocusOutlineStyle";
3 | export * from "./focusVisiblePolyfill";
4 | export * from "./focusWithinPolyfill";
5 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/constants.ts:
--------------------------------------------------------------------------------
1 | export const DATA_POSITIONING_INTERSECTING = "data-popper-is-intersecting";
2 | export const DATA_POSITIONING_ESCAPED = "data-popper-escaped";
3 | export const DATA_POSITIONING_HIDDEN = "data-popper-reference-hidden";
4 | export const DATA_POSITIONING_PLACEMENT = "data-popper-placement";
5 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./constants";
2 | export * from "./createArrowStyles";
3 | export * from "./createSlideStyles";
4 | export * from "./types";
5 | export * from "./utils";
6 | export * from "./usePositioning";
7 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/middleware/flip.ts:
--------------------------------------------------------------------------------
1 | import { flip as baseFlip, Middleware, Placement } from "@floating-ui/dom";
2 | import type { PositioningOptions } from "../types";
3 | import { resolvePositioningShorthand } from "../utils/resolvePositioningShorthand";
4 | import { toFloatingUIPlacement } from "../utils/toFloatingUIPlacement";
5 |
6 | export interface FlipMiddlewareOptions
7 | extends Pick {
8 | hasScrollableElement?: boolean;
9 | container: HTMLElement | null;
10 | isRtl?: boolean;
11 | }
12 |
13 | export function flip(options: FlipMiddlewareOptions): Middleware {
14 | const { hasScrollableElement, fallbackPositions = [], isRtl } = options;
15 |
16 | const fallbackPlacements = fallbackPositions.reduce(
17 | (acc, shorthand) => {
18 | const { position, align } = resolvePositioningShorthand(shorthand);
19 | const placement = toFloatingUIPlacement(align, position, isRtl);
20 | if (placement) {
21 | acc.push(placement);
22 | }
23 | return acc;
24 | },
25 | [],
26 | );
27 |
28 | return baseFlip({
29 | ...(hasScrollableElement && { boundary: "clippingAncestors" }),
30 | fallbackStrategy: "bestFit",
31 | ...(fallbackPlacements.length && { fallbackPlacements }),
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/middleware/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./flip";
2 | export * from "./offset";
3 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/middleware/offset.ts:
--------------------------------------------------------------------------------
1 | import { Middleware, offset as baseOffset } from "@floating-ui/dom";
2 | import type { PositioningOptions } from "../types";
3 | import { getFloatingUIOffset } from "../utils/getFloatingUIOffset";
4 |
5 | /**
6 | * Wraps floating UI offset middleware to to transform offset value
7 | */
8 | export function offset(offsetValue: PositioningOptions["offset"]): Middleware {
9 | const floatingUIOffset = getFloatingUIOffset(offsetValue);
10 | return baseOffset(floatingUIOffset);
11 | }
12 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./fromFloatingUIPlacement";
2 | export * from "./mergeArrowOffset";
3 | export * from "./parseFloatingUIPlacement";
4 | export * from "./resolvePositioningShorthand";
5 | export * from "./toFloatingUIPlacement";
6 |
--------------------------------------------------------------------------------
/packages/fluentui/src/positioning/utils/parseFloatingUIPlacement.ts:
--------------------------------------------------------------------------------
1 | import type { Side, Placement, Alignment } from "@floating-ui/dom";
2 |
3 | /**
4 | * Parses Floating UI placement and returns the different components
5 | * @param placement - the floating ui placement (i.e. bottom-start)
6 | *
7 | * @returns side and alignment components of the placement
8 | */
9 | export function parseFloatingUIPlacement(placement: Placement): {
10 | side: Side;
11 | alignment: Alignment;
12 | } {
13 | const tokens = placement.split("-");
14 | return {
15 | side: tokens[0] as Side,
16 | alignment: tokens[1] as Alignment,
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/packages/fluentui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/fluentui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { RefinaLib } from "vite-plugin-refina";
3 |
4 | export default defineConfig({
5 | plugins: [RefinaLib()],
6 | });
7 |
--------------------------------------------------------------------------------
/packages/griffel/README.md:
--------------------------------------------------------------------------------
1 | # `@refina/griffel`
2 |
3 | [](https://www.npmjs.com/package/@refina/griffel)
4 |
5 | The [Griffel](https://griffel.js.org/) integration for Refina.
6 |
7 | To learn more about Refina, please visit:
8 |
9 | - [Documentation](https://refina.vercel.app).
10 | - [Getting Started](https://refina.vercel.app/guide/introduction.html)
11 | - [GitHub Repository](https://github.com/refinajs/refina).
12 | - [Examples](https://gallery.refina.vercel.app).
13 |
--------------------------------------------------------------------------------
/packages/griffel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/griffel",
3 | "version": "0.6.0",
4 | "description": "Griffel.js support for Refina framework.",
5 | "keywords": [
6 | "refina",
7 | "griffel"
8 | ],
9 | "files": [
10 | "./src",
11 | "README.md",
12 | "tsconfig.json"
13 | ],
14 | "type": "module",
15 | "main": "./src/index.ts",
16 | "types": "./src/index.ts",
17 | "exports": {
18 | ".": {
19 | "import": {
20 | "types": "./src/index.ts",
21 | "default": "./src/index.ts"
22 | }
23 | }
24 | },
25 | "scripts": {},
26 | "author": "_Kerman",
27 | "repository": {
28 | "type": "git",
29 | "url": "git+https://github.com/KermanX/refina"
30 | },
31 | "readme": "https://github.com/KermanX/refina#readme",
32 | "bugs": "https://github.com/KermanX/refina/issues",
33 | "license": "MIT",
34 | "devDependencies": {
35 | "@refina/tsconfig": "workspace:^",
36 | "typescript": "^5.4.2"
37 | },
38 | "dependencies": {
39 | "@griffel/core": "^1.15.2"
40 | },
41 | "peerDependencies": {
42 | "refina": "workspace:^"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/griffel/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/hmr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/hmr",
3 | "version": "0.0.0",
4 | "description": "The HMR compiler for Refina framework.",
5 | "keywords": [
6 | "refina",
7 | "hmr"
8 | ],
9 | "files": [
10 | "src"
11 | ],
12 | "type": "module",
13 | "main": "./src/index.ts",
14 | "types": "./src/index.ts",
15 | "exports": {
16 | ".": "./src/index.ts"
17 | },
18 | "scripts": {
19 | "check": "tsc --noEmit"
20 | },
21 | "author": "_Kerman",
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/KermanX/refina"
25 | },
26 | "readme": "https://github.com/KermanX/refina#readme",
27 | "bugs": "https://github.com/KermanX/refina/issues",
28 | "license": "MIT",
29 | "dependencies": {
30 | "@babel/parser": "^7.24.0",
31 | "@babel/types": "^7.24.0",
32 | "magic-string": "^0.30.8"
33 | },
34 | "devDependencies": {
35 | "typescript": "^5.4.2"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/hmr/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const localsObjId = "__locals__";
2 | export function getLocalsAccessor(id: string) {
3 | return `(${localsObjId}.${id})`;
4 | }
5 |
6 | export const mainFuncId = "__main__";
7 |
8 | export const appInstDefaultId = "__app__";
9 |
10 | export const initFuncId = "__init__";
11 |
12 | export const mainUrlSuffix = "?refina-app-main";
13 |
--------------------------------------------------------------------------------
/packages/hmr/src/cutSrc.ts:
--------------------------------------------------------------------------------
1 | import t from "@babel/types";
2 | import MagicString from "magic-string";
3 |
4 | export function cutSrc(src: string, stmtsToRemove: t.Statement[]) {
5 | const s = new MagicString(src);
6 | for (const statement of stmtsToRemove) {
7 | s.remove(statement.start!, statement.end!);
8 | }
9 | return s;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/hmr/src/index.ts:
--------------------------------------------------------------------------------
1 | import { RefinaDescriptor, compile } from "./compile";
2 |
3 | export class RefinaHmr {
4 | cache = new Map();
5 |
6 | transform(id: string, src: string) {
7 | const descriptor = this.cache.get(id) ?? compile(id, src);
8 | this.cache.set(id, descriptor);
9 | return descriptor;
10 | }
11 |
12 | /**
13 | * @returns Can perform HMR.
14 | */
15 | update(id: string, newSrc: string) {
16 | const oldDescriptor = this.cache.get(id);
17 | const newDescriptor = compile(id, newSrc);
18 | this.cache.set(id, newDescriptor);
19 | if (!oldDescriptor || !newDescriptor) return false;
20 | if (oldDescriptor.locals.code !== newDescriptor.locals.code) return false;
21 | return true;
22 | }
23 | }
24 |
25 | export * from "./constants";
26 |
--------------------------------------------------------------------------------
/packages/hmr/src/wrapMain.ts:
--------------------------------------------------------------------------------
1 | import MagicString from "magic-string";
2 | import {
3 | appInstDefaultId,
4 | initFuncId,
5 | localsObjId,
6 | mainFuncId,
7 | } from "./constants";
8 | import { ParseResult } from "./parser";
9 |
10 | export function wrapMain(
11 | { appStmt: appCallAst, mainFuncExpr: mainFuncAst, appInstName }: ParseResult,
12 | mainSrc: MagicString,
13 | ) {
14 | const appId = appInstName ?? appInstDefaultId;
15 |
16 | mainSrc.update(
17 | appCallAst.start!,
18 | mainFuncAst.start!,
19 | `export const ${mainFuncId} = (${localsObjId}) => (`,
20 | );
21 |
22 | mainSrc.append(`
23 | let ${appId}, ${localsObjId};
24 |
25 | export function ${initFuncId}(__app_param__, __locals_param__) {
26 | ${appId} = __app_param__;
27 | ${localsObjId} = __locals_param__;
28 | }
29 |
30 | import.meta.hot?.accept(async (__new_main_mod__) => {
31 | __new_main_mod__.${initFuncId}(${appId}, ${localsObjId});
32 | const newMain = __new_main_mod__.${mainFuncId}(${localsObjId});
33 | if(${appId}.state !== "idle") {
34 | await ${appId}.promises.mainExecuted;
35 | }
36 | ${appId}.main = newMain;
37 | ${appId}.update();
38 | });
39 | `);
40 | }
41 |
--------------------------------------------------------------------------------
/packages/hmr/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["./src"],
3 | "compilerOptions": {
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "lib": ["ESNext"],
8 | "strict": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/avatar.ts:
--------------------------------------------------------------------------------
1 | import { Avatar } from "mdui";
2 | import { Component, _ } from "refina";
3 |
4 | export class MdAvatar extends Component {
5 | $main(src: string, fit?: Avatar["fit"]): void {
6 | _._mdui_avatar({
7 | src,
8 | fit,
9 | });
10 | }
11 | }
12 |
13 | export class MdIconAvatar extends Component {
14 | $main(iconName: string): void {
15 | _._mdui_avatar({
16 | icon: iconName,
17 | });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/badge.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class MdBadge extends Component {
4 | $main(children?: Content | undefined): void {
5 | if (children === undefined) {
6 | _._mdui_badge({
7 | variant: "small",
8 | });
9 | } else {
10 | _._mdui_badge(
11 | {
12 | variant: "large",
13 | },
14 | children,
15 | );
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/bottomAppBar.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class MdBottomAppBar extends Component {
4 | $main(children: Content): void {
5 | _._mdui_bottom_app_bar({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/button.ts:
--------------------------------------------------------------------------------
1 | import { Button } from "mdui";
2 | import { Content, TriggerComponent, _ } from "refina";
3 |
4 | export class MdButton extends TriggerComponent {
5 | variant: Button["variant"] = "filled";
6 | $main(
7 | children: Content,
8 | disabled: boolean = false,
9 | ): this is {
10 | $ev: void;
11 | } {
12 | _._mdui_button(
13 | {
14 | disabled,
15 | onclick: this.$fireWith(),
16 | variant: this.variant,
17 | },
18 | children,
19 | );
20 | return this.$fired;
21 | }
22 | }
23 |
24 | export class MdTonalButton extends MdButton {
25 | variant = "tonal" as const;
26 | }
27 |
28 | export class MdOutlinedButton extends MdButton {
29 | variant = "outlined" as const;
30 | }
31 |
32 | export class MdTextButton extends MdButton {
33 | variant = "text" as const;
34 | }
35 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/checkbox.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Content,
3 | Model,
4 | TriggerComponent,
5 | _,
6 | elementRef,
7 | unwrap,
8 | } from "refina";
9 |
10 | export type MdCheckboxState = boolean | undefined;
11 |
12 | export class MdCheckbox extends TriggerComponent {
13 | checkboxRef = elementRef<"mdui-checkbox">();
14 | $main(
15 | state: Model,
16 | label?: Content,
17 | disabled = false,
18 | ): this is {
19 | $ev: MdCheckboxState;
20 | } {
21 | const stateValue = unwrap(state);
22 | const checked = stateValue;
23 | const indeterminate = stateValue === undefined;
24 |
25 | _.$ref(this.checkboxRef);
26 | _._mdui_checkbox(
27 | {
28 | checked,
29 | indeterminate,
30 | disabled,
31 | onchange: () => {
32 | const node = this.checkboxRef.current!.node;
33 | const newState = node.indeterminate ? undefined : node.checked;
34 | this.$updateModel(state, newState);
35 | this.$fire(newState);
36 | },
37 | },
38 | label,
39 | );
40 | return this.$fired;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/circularProgress.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 |
3 | export class MdCircularProgress extends Component {
4 | $main(percentage?: number | undefined): void {
5 | _._mdui_circular_progress({
6 | value: percentage,
7 | });
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/collapse.ts:
--------------------------------------------------------------------------------
1 | import { Content, TriggerComponent, _, elementRef } from "refina";
2 |
3 | export class MdCollapse extends TriggerComponent {
4 | icon?: string;
5 | collapseRef = elementRef<"mdui-collapse">();
6 | $main(
7 | header: Content,
8 | body: Content,
9 | disabled = false,
10 | ): this is {
11 | $ev: boolean;
12 | } {
13 | _.$ref(this.collapseRef);
14 | _._mdui_collapse(
15 | {
16 | disabled,
17 | accordion: true,
18 | onchange: () => {
19 | this.$fire(
20 | (this.collapseRef.current!.node.value as string[]).length > 0,
21 | );
22 | },
23 | },
24 | _ =>
25 | _._mdui_collapse_item(
26 | {
27 | value: "item",
28 | },
29 | () => {
30 | _._mdui_list_item(
31 | {
32 | slot: "header",
33 | icon: this.icon,
34 | },
35 | header,
36 | );
37 | if (this.icon) {
38 | _.$css`margin-left: 2.5rem`;
39 | }
40 | _._div({}, _ => _._mdui_list_item({}, body));
41 | },
42 | ),
43 | );
44 | return this.$fired;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/divider.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 |
3 | export class MdDivider extends Component {
4 | $main(): void {
5 | _._mdui_divider();
6 | }
7 | }
8 |
9 | export class MdVerticalDivider extends Component {
10 | $main(): void {
11 | _._mdui_divider({
12 | vertical: true,
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/fab.ts:
--------------------------------------------------------------------------------
1 | import { Fab } from "mdui";
2 | import { Content, TriggerComponent, _ } from "refina";
3 |
4 | export type MdFabVariant = Fab["variant"];
5 |
6 | export class MdFab extends TriggerComponent {
7 | varient: MdFabVariant = "primary";
8 | $main(
9 | icon: string,
10 | disabled = false,
11 | extendedContent: Content | undefined = undefined,
12 | ): this is {
13 | $ev: void;
14 | } {
15 | _._mdui_fab(
16 | {
17 | icon,
18 | disabled,
19 | extended: extendedContent !== undefined,
20 | onclick: this.$fireWith(),
21 | variant: this.varient,
22 | },
23 | extendedContent,
24 | );
25 | return this.$fired;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/icon.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 |
3 | export class MdIcon extends Component {
4 | $main(name: string): void {
5 | _._mdui_icon({
6 | name,
7 | });
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/iconButton.ts:
--------------------------------------------------------------------------------
1 | import { ButtonIcon } from "mdui";
2 | import { TriggerComponent, _ } from "refina";
3 |
4 | export type MdIconButtonVariant = ButtonIcon["variant"];
5 |
6 | export class MdIconButton extends TriggerComponent {
7 | variant: MdIconButtonVariant = "standard";
8 | $main(
9 | icon: string,
10 | disabled = false,
11 | variant = "standard",
12 | ): this is {
13 | $ev: void;
14 | } {
15 | _._mdui_button_icon({
16 | icon,
17 | disabled,
18 | onclick: this.$fireWith(),
19 | variant: this.variant,
20 | });
21 | return this.$fired;
22 | }
23 | }
24 |
25 | export class MdFilledIconButton extends MdIconButton {
26 | variant = "filled" as const;
27 | }
28 |
29 | export class MdTonalIconButton extends MdIconButton {
30 | variant = "tonal" as const;
31 | }
32 |
33 | export class MdOutlinedIconButton extends MdIconButton {
34 | variant = "outlined" as const;
35 | }
36 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./avatar";
2 | export * from "./badge";
3 | export * from "./bottomAppBar";
4 | export * from "./button";
5 | export * from "./checkbox";
6 | export * from "./chip";
7 | export * from "./circularProgress";
8 | export * from "./collapse";
9 | export * from "./dialog";
10 | export * from "./divider";
11 | export * from "./fab";
12 | export * from "./icon";
13 | export * from "./iconButton";
14 | export * from "./layout";
15 | export * from "./layoutMain";
16 | export * from "./linearProgress";
17 | export * from "./list";
18 | export * from "./navBar";
19 | export * from "./navDrawer";
20 | export * from "./navRail";
21 | export * from "./prose";
22 | export * from "./radioGroup";
23 | export * from "./rangeSlider";
24 | export * from "./segmentedButton";
25 | export * from "./select";
26 | export * from "./slider";
27 | export * from "./switch";
28 | export * from "./table";
29 | export * from "./tabs";
30 | export * from "./textField";
31 | export * from "./tooltip";
32 | export * from "./topAppBar";
33 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/layout.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class MdLayout extends Component {
4 | $main(children: Content): void {
5 | _._mdui_layout({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/layoutMain.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class MdLayoutMain extends Component {
4 | $main(children: Content): void {
5 | _._mdui_layout_main({}, children);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/linearProgress.ts:
--------------------------------------------------------------------------------
1 | import { Component, _ } from "refina";
2 |
3 | export class MdLinearProgress extends Component {
4 | $main(percentage?: number | undefined): void {
5 | _._mdui_linear_progress({
6 | value: percentage,
7 | });
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/list.ts:
--------------------------------------------------------------------------------
1 | import { Component, LoopKey, _ } from "refina";
2 |
3 | export class MdList extends Component {
4 | $main(
5 | data: Iterable,
6 | key: LoopKey,
7 | body: (item: T, index: number) => void,
8 | ): void {
9 | _._mdui_list({}, _ =>
10 | _.for(data, key, (item, index) =>
11 | _._mdui_list_item({}, _ => {
12 | body(item, index);
13 | }),
14 | ),
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/prose.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class MdProse extends Component {
4 | $main(children: Content): void {
5 | _.$cls`mdui-prose`;
6 | _._div({}, children);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/rangeSlider.ts:
--------------------------------------------------------------------------------
1 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
2 |
3 | export class MdRangeSlider extends TriggerComponent {
4 | sliderRef = elementRef<"mdui-range-slider">();
5 | $main(
6 | lowValue: Model,
7 | highValue: Model,
8 | disabled = false,
9 | step = 1,
10 | min = 0,
11 | max = 100,
12 | ): this is {
13 | $ev: [low: number, high: number];
14 | } {
15 | _.$ref(this.sliderRef);
16 | _._mdui_range_slider({
17 | disabled,
18 | min,
19 | max,
20 | step,
21 | oninput: () => {
22 | const [newLow, newHigh] = this.sliderRef.current!.node.value;
23 | this.$updateModel(lowValue, newLow);
24 | this.$updateModel(highValue, newHigh);
25 | this.$fire([newLow, newHigh]);
26 | },
27 | });
28 |
29 | // TODO: remove this hack
30 | setTimeout(() => {
31 | this.sliderRef.current!.node.value = [
32 | unwrap(lowValue),
33 | unwrap(highValue),
34 | ];
35 | });
36 | return this.$fired;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/segmentedButton.ts:
--------------------------------------------------------------------------------
1 | import { Content, TriggerComponent, _, byIndex } from "refina";
2 |
3 | export class MdSegmentedButton extends TriggerComponent {
4 | icons?: (string | undefined)[];
5 | endIcons?: (string | undefined)[];
6 | $main(
7 | contents: Content[],
8 | disabled: readonly boolean[] | boolean = false,
9 | ): this is {
10 | $ev: number;
11 | } {
12 | const groupDisabled = disabled === true;
13 | const optionsDisabled = typeof disabled === "boolean" ? [] : disabled;
14 |
15 | _._mdui_segmented_button_group(
16 | {
17 | disabled: groupDisabled,
18 | },
19 | _ =>
20 | _.for(contents, byIndex, (content, index) =>
21 | _._mdui_segmented_button(
22 | {
23 | disabled: optionsDisabled[index],
24 | icon: this.icons?.[index],
25 | endIcon: this.endIcons?.[index],
26 | onclick: this.$fireWith(index),
27 | },
28 | content,
29 | ),
30 | ),
31 | );
32 | return this.$fired;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/slider.ts:
--------------------------------------------------------------------------------
1 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
2 |
3 | export class MdSlider extends TriggerComponent {
4 | sliderRef = elementRef<"mdui-slider">();
5 | $main(
6 | value: Model,
7 | disabled = false,
8 | step = 1,
9 | min = 0,
10 | max = 100,
11 | ): this is {
12 | $ev: number;
13 | } {
14 | _.$ref(this.sliderRef);
15 | _._mdui_slider({
16 | value: unwrap(value),
17 | disabled,
18 | min,
19 | max,
20 | step,
21 | oninput: () => {
22 | const newValue = this.sliderRef.current!.node.value;
23 | this.$updateModel(value, newValue);
24 | this.$fire(newValue);
25 | },
26 | });
27 | return this.$fired;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/switch.ts:
--------------------------------------------------------------------------------
1 | import { Model, TriggerComponent, _, elementRef, unwrap } from "refina";
2 |
3 | export class MdSwitch extends TriggerComponent {
4 | switchRef = elementRef<"mdui-switch">();
5 | $main(
6 | checked: Model,
7 | disabled = false,
8 | ): this is {
9 | $ev: boolean;
10 | } {
11 | _.$ref(this.switchRef);
12 | _._mdui_switch({
13 | checked: unwrap(checked),
14 | disabled,
15 | onchange: () => {
16 | const newState = this.switchRef.current!.node.checked;
17 | this.$updateModel(checked, newState);
18 | this.$fire(newState);
19 | },
20 | });
21 | return this.$fired;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/table.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, LoopKey, _, byIndex } from "refina";
2 |
3 | export class MdTable extends Component {
4 | $main(
5 | data: Iterable,
6 | head: Content[] | Content,
7 | key: LoopKey,
8 | row: (item: T, index: number) => void,
9 | ): void {
10 | _.$cls`mdui-table`;
11 | _._div({}, _ => {
12 | _._table({}, _ => {
13 | _._thead({}, _ => {
14 | if (Array.isArray(head)) {
15 | _.for(head, byIndex, item => {
16 | _._th({}, item);
17 | });
18 | } else {
19 | _.embed(head);
20 | }
21 | });
22 | _._tbody({}, _ => {
23 | _.for(data, key, (item, index) => {
24 | _._tr({}, _ => {
25 | row(item, index);
26 | });
27 | });
28 | });
29 | });
30 | });
31 | }
32 | }
33 |
34 | export class MdTableHeader extends Component {
35 | $main(children: Content): void {
36 | _._th({}, children);
37 | }
38 | }
39 |
40 | export class MdTableCell extends Component {
41 | $main(children: Content): void {
42 | _._td({}, children);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/tooltip.ts:
--------------------------------------------------------------------------------
1 | import { Content, TriggerComponent, _ } from "refina";
2 |
3 | export class MdTooltip extends TriggerComponent {
4 | $main(
5 | text: string,
6 | children: Content,
7 | ): this is {
8 | $ev: boolean;
9 | } {
10 | _._mdui_tooltip(
11 | {
12 | content: text,
13 | },
14 | children,
15 | {
16 | open: this.$fireWith(true),
17 | close: this.$fireWith(false),
18 | },
19 | );
20 | return this.$fired;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/mdui/src/components/topAppBar.ts:
--------------------------------------------------------------------------------
1 | import { Component, Content, _ } from "refina";
2 |
3 | export class MdTopAppBar extends Component {
4 | $main(children: Content, append?: Content): void {
5 | _._mdui_top_app_bar(
6 | {},
7 | append
8 | ? _ => {
9 | _.embed(children);
10 |
11 | _.$css`flex-grow: 1`;
12 | _._div();
13 |
14 | _.embed(append);
15 | }
16 | : children,
17 | );
18 | }
19 | }
20 |
21 | export class MdTopAppBarTitle extends Component {
22 | $main(children: Content): void {
23 | _._mdui_top_app_bar_title({}, children);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/mdui/src/theme/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./useColorScheme";
2 | export * from "./useTheme";
3 |
--------------------------------------------------------------------------------
/packages/mdui/src/theme/useColorScheme.ts:
--------------------------------------------------------------------------------
1 | import { setColorScheme } from "mdui";
2 | import type { CustomColor } from "mdui/functions/utils/colorScheme";
3 | import { $contextFunc } from "refina";
4 |
5 | let currentColorSchemeHex: string = "$unset";
6 |
7 | export const useMdColorScheme = $contextFunc(
8 | () => (hex: string, customColors?: CustomColor[]) => {
9 | if (hex !== currentColorSchemeHex || customColors) {
10 | setColorScheme(hex, { customColors });
11 | currentColorSchemeHex = hex;
12 | }
13 | },
14 | );
15 |
--------------------------------------------------------------------------------
/packages/mdui/src/theme/useTheme.ts:
--------------------------------------------------------------------------------
1 | import { $contextFunc, _ } from "refina";
2 |
3 | export type MdUITheme = "light" | "dark" | "auto";
4 |
5 | export const useMdTheme = $contextFunc(
6 | () =>
7 | (theme: MdUITheme = "auto"): void => {
8 | if (_.$updateContext) {
9 | _.$body.addCls(`mdui-theme-${theme}`);
10 | }
11 | },
12 | );
13 |
--------------------------------------------------------------------------------
/packages/mdui/styles.css:
--------------------------------------------------------------------------------
1 | @import url(mdui/mdui.css);
2 |
--------------------------------------------------------------------------------
/packages/mdui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"],
4 | "compilerOptions": {
5 | "types": []
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/mdui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { RefinaLib } from "vite-plugin-refina";
3 |
4 | export default defineConfig({
5 | plugins: [RefinaLib()],
6 | });
7 |
--------------------------------------------------------------------------------
/packages/router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/router",
3 | "version": "0.0.0",
4 | "description": "The router for Refina framework.",
5 | "keywords": [
6 | "refina",
7 | "router",
8 | "components"
9 | ],
10 | "files": [
11 | "src"
12 | ],
13 | "type": "module",
14 | "main": "./src/index.ts",
15 | "types": "./src/index.ts",
16 | "exports": {
17 | ".": "./src/index.ts"
18 | },
19 | "scripts": {
20 | "check": "tsc --noEmit"
21 | },
22 | "author": "_Kerman",
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/KermanX/refina"
26 | },
27 | "readme": "https://github.com/KermanX/refina#readme",
28 | "bugs": "https://github.com/KermanX/refina/issues",
29 | "license": "MIT",
30 | "devDependencies": {
31 | "@refina/tsconfig": "workspace:^",
32 | "typescript": "^5.4.2"
33 | },
34 | "peerDependencies": {
35 | "refina": "workspace:^"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/router/src/beforeRoute.ts:
--------------------------------------------------------------------------------
1 | import { $contextFunc, _ } from "refina";
2 | import { BeforeRouteContext, getIncomingRoute } from "./router";
3 |
4 | export const beforeRoute = $contextFunc(
5 | () =>
6 | (): // @ts-expect-error
7 | this is BeforeRouteContext => {
8 | const incomingRoute = getIncomingRoute();
9 | if (incomingRoute) {
10 | // @ts-expect-error
11 | const pendingRoute = _.$ev as BeforeRouteContext;
12 | if (pendingRoute) {
13 | Object.assign(_, pendingRoute);
14 | return true;
15 | }
16 | }
17 | return false;
18 | },
19 | );
20 |
--------------------------------------------------------------------------------
/packages/router/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "refina";
2 | import { beforeRoute } from "./beforeRoute";
3 | import { route, routeNotFound } from "./route";
4 | import { Router } from "./router";
5 |
6 | export default {
7 | name: "router",
8 | contextFuncs: {
9 | beforeRoute,
10 | route,
11 | routeNotFound,
12 | },
13 | onInstall(app) {
14 | app.router = new Router(app);
15 | },
16 | initContext(context) {
17 | context.$router = this.router;
18 | },
19 | afterMain() {
20 | this.router.updateCurrentPath();
21 | },
22 | } satisfies Plugin;
23 |
24 | declare module "refina" {
25 | interface App {
26 | router: Router;
27 | }
28 | interface IntrinsicBaseContext {
29 | $router: Router;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/router/src/utils.ts:
--------------------------------------------------------------------------------
1 | export function matchPath(path: string, currentPath: string) {
2 | const params: Record = {};
3 | let j = 0;
4 | for (let i = 0; i < path.length; i++) {
5 | if (path[i] === ":") {
6 | let paramName = "";
7 | while (++i < path.length && path[i] !== "/") {
8 | paramName += path[i];
9 | }
10 |
11 | let paramValue = "";
12 | while (j < currentPath.length && currentPath[j] !== "/") {
13 | paramValue += currentPath[j];
14 | j++;
15 | }
16 |
17 | params[paramName] = paramValue;
18 | } else {
19 | if (path[i] !== currentPath[j]) {
20 | return false;
21 | }
22 | j++;
23 | }
24 | }
25 | if (j !== currentPath.length) {
26 | return false;
27 | }
28 | return params;
29 | }
30 |
--------------------------------------------------------------------------------
/packages/router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "include": ["src/**/*"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tests/core/__snapshots__/app.r.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`simple app > should render a simple app 1`] = `""`;
4 |
--------------------------------------------------------------------------------
/packages/tests/core/app.r.test.ts:
--------------------------------------------------------------------------------
1 | import { $app } from "refina";
2 | import { initRootElement } from "../utils";
3 |
4 | beforeAll(() => {
5 | initRootElement();
6 | });
7 |
8 | describe("simple app", () => {
9 | it("should render a simple app", async () => {
10 | const appInstance = $app([], _ => {
11 | _._h1({}, "Hello World!");
12 | _.$cls`main`;
13 | _._div({}, _ => {
14 | _.$css`color: red`;
15 | _._p({}, "This is a paragraph.");
16 | });
17 | });
18 |
19 | await appInstance.promises.DOMUpdated;
20 |
21 | expect(document.body.innerHTML).toMatchSnapshot();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/packages/tests/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/tests",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "check": "tsc --noEmit"
8 | },
9 | "devDependencies": {
10 | "@refina/tsconfig": "workspace:^",
11 | "typescript": "^5.4.2"
12 | },
13 | "dependencies": {
14 | "@refina/transformer": "workspace:^",
15 | "refina": "workspace:^"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/tests/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@refina/tsconfig",
3 | "compilerOptions": {
4 | "composite": true,
5 | "lib": ["ESNext", "DOM"],
6 | "types": ["vitest/globals"],
7 | "paths": {
8 | "refina": ["./packages/core/src"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/tests/utils/index.ts:
--------------------------------------------------------------------------------
1 | export function initRootElement() {
2 | document.body.innerHTML = ``;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/transformer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@refina/transformer",
3 | "version": "0.6.0",
4 | "description": "The code transformer for Refina framework.",
5 | "keywords": [
6 | "refina",
7 | "transformer"
8 | ],
9 | "files": [
10 | "src"
11 | ],
12 | "type": "module",
13 | "main": "./src/index.ts",
14 | "types": "./src/index.ts",
15 | "exports": {
16 | ".": "./src/index.ts"
17 | },
18 | "scripts": {
19 | "check": "tsc --noEmit"
20 | },
21 | "author": "_Kerman",
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/KermanX/refina"
25 | },
26 | "readme": "https://github.com/KermanX/refina#readme",
27 | "bugs": "https://github.com/KermanX/refina/issues",
28 | "license": "MIT",
29 | "dependencies": {
30 | "magic-string": "^0.30.8"
31 | },
32 | "devDependencies": {
33 | "typescript": "^5.4.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/transformer/src/patterns.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | TEXT_NODE_TAGFUNC: /(?)?\s*\(/g,
5 | DIRECT_CALL: /(? boolean;
28 | }
29 |
30 | export function getFilter(options: CommonOptions) {
31 | const idFilter = createFilter(
32 | options.include ?? /\.[tj]s(\?|$)/,
33 | options.exclude ?? [/\?(.*&)?raw/, /node_modules/],
34 | );
35 | const rawFilter = createFilter(
36 | /./,
37 | options.ignore ?? /^(((^|\n)\s*\/\/[^\n]*)|\n)*\s*\/\/\s*@refina-ignore/,
38 | );
39 | return (id: string, raw: string) => idFilter(id) && rawFilter(raw);
40 | }
41 |
--------------------------------------------------------------------------------
/packages/vite-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src"],
3 | "compilerOptions": {
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "lib": ["ESNext", "WebWorker"],
8 | "strict": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/vite-plugin/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import type { Options } from "tsup";
2 |
3 | export const tsup: Options = {
4 | entry: ["./src/index.ts"],
5 | format: ["cjs", "esm"],
6 | target: "es2019",
7 | noExternal: ["@refina/transformer", "@refina/hmr"],
8 | sourcemap: true,
9 | dts: true,
10 | splitting: false,
11 | };
12 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "docs"
3 | - "packages/**"
4 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 | import Refina from "./packages/vite-plugin";
3 |
4 | export default defineConfig({
5 | plugins: [Refina()],
6 | test: {
7 | include: ["packages/tests/**/*.test.ts"],
8 | globals: true,
9 | environment: "jsdom",
10 | coverage: {
11 | provider: "istanbul",
12 | reporter: ["text", "html", "lcov"],
13 | },
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
|