Skip to content

Commit

Permalink
JS 开发教程
Browse files Browse the repository at this point in the history
  • Loading branch information
linonetwo committed Mar 27, 2024
1 parent be97f7b commit 51fb509
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 3 deletions.
8 changes: 8 additions & 0 deletions tiddlers/JS微件.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
created: 20240327064109737
creator: 林一二
modified: 20240327064130227
modifier: 林一二
tags: 太微原理
title: JS微件

本主题解析几个例子,帮你理解 JavaScript 写的微件。
127 changes: 127 additions & 0 deletions tiddlers/wikitext-macros.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
created: 20240327063745039
creator: 林一二
modified: 20240327063811516
modifier: 林一二
tags: TiddlyWiki
title: wikitext-macros

\procedure activatePluginTab()
<$action-setfield $tiddler="$:/state/tab-1749438307" text="$:/core/ui/ControlPanel/Plugins"/>
<$action-navigate $to="$:/ControlPanel"/>
\end

\procedure activateTiddlerWindow()
<$action-sendmessage $message="tm-open-window" $param=<<currentTiddler>> windowTitle="Side by Side View" width="800" height="600" />
\end

\procedure controlPanel-plugin-link()
<$button actions=<<activatePluginTab>> class="tc-btn-invisible tc-tiddlylink">
{{$:/core/images/options-button}} ~ControlPanel
</$button>
\end

\procedure open-tiddler-in-window()
\whitespace notrim
<$button actions=<<activateTiddlerWindow>> class="tc-btn-invisible tc-tiddlylink">
open ''this'' tiddler in a new window
</$button>
\end

\procedure activateEditionWindow(url)
<$action-sendmessage $message="tm-open-external-window" $param=<<url>> windowName="_edition" windowFeatures="width=800 height=600" />
\end

\procedure open-external-window(url)
\whitespace notrim
<$button actions=<<activateEditionWindow <<url>> class="tc-btn-invisible tc-tiddlylink">
open the ''example edition'' in a new window
</$button>
\end


\procedure wikitext-example(src)
<div class="doc-example">
<$macrocall $name="copy-to-clipboard-above-right" src=<<src>>/>
<$codeblock code=<<src>>/>
<p>
That renders as:
</p>
<$transclude $variable="src" $mode="block"/>
<p>
... and the underlying HTML is:
</p>
<$wikify name="html" text=<<src>> output="html">
<$codeblock code=<<html>>/>
</$wikify>
</div>
\end

\procedure wikitext-example-without-html(src)
<div class="doc-example">
<$macrocall $name="copy-to-clipboard-above-right" src=<<src>>/>
<$codeblock code=<<src>>/>
<p>
That renders as:
</p>
<$transclude $variable="src" $mode="block"/>
</div>
\end

\procedure wikitext-example-table-header() <thead><tr><th/><th>wiki text</th><th>renders as</th></tr></thead>

\procedure wikitext-example-table-row(id, code)
<tr>
<th><<id>></th>
<td><$codeblock code=<<code>>/></td>
<td><<code>></td>
</tr>
\end

\procedure tw-code(tiddler)
<$codeblock language={{$tiddler$!!type}} code={{$tiddler$}}/>
\end

\procedure tw-code-link(tiddler)
[[$tiddler$]]:
<<tw-code $tiddler$>>
\end

\procedure flex-card(class,bordercolor:"",backgroundcolor:"",textcolor:"",imageField:"image",captionField:"caption",subtitle:"",descriptionField:"description",linkField:"link")
<$link class={{{ [<class>addprefix[tc-card ]] }}} to={{{ [<currentTiddler>get<linkField>else<currentTiddler>] }}}>
<div class="tc-card-accent" style.borderTop={{{ [<bordercolor>!is[blank]addprefix[5px solid ]] }}} style.background={{!!background}} style.backgroundColor=<<backgroundcolor>> style.color=<<textcolor>> style.fill=<<textcolor>>>
<$list filter="[<currentTiddler>has[ribbon-text]]" variable="ignore">
<div class="tc-card-ribbon-wrapper">
<div class="tc-card-ribbon" style.backgroundColor={{{ [<currentTiddler>get[ribbon-color]else[red]] }}}>
<div class="tc-card-ribbon-inner">
<$text text={{!!ribbon-text}}/>
</div>
</div>
</div>
</$list>
<$list filter="[<currentTiddler>has<imageField>]" variable="ignore">
<div class="tc-card-image">
<$image source={{{ [<currentTiddler>get<imageField>] }}}/>
</div>
</$list>
<div class="tc-card-title"><$transclude field=<<captionField>>><$view field="title"/></$transclude></div>
<$list filter="[<subtitle>!is[blank]]" variable="ignore">
<div class="tc-card-subtitle">
<$text text=<<subtitle>>/>
</div>
</$list>
<div class="tc-card-icon"><$transclude tiddler={{!!icon}}/></div>
<div class="tc-card-body-wrapper">
<div class="tc-card-body">
<$transclude field=<<descriptionField>> mode="block"/>
</div>
<div class="tc-card-body-clear">
</div>
</div>
<$list filter="[all[current]has[button-text]]" variable="ignore">
<div class="tc-card-button" style.background-color={{!!button-color}} style.border-color={{!!button-color}}>
<$text text={{!!button-text}}/>&#32;{{$:/core/images/chevron-right}}
</div>
</$list>
</div>
</$link>
\end
33 changes: 33 additions & 0 deletions tiddlers/在太微里使用JavaScript.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
created: 20240327063449945
creator: 林一二
modified: 20240327065810228
modifier: 林一二
tags: 插件开发
title: 在太微里使用JavaScript

!! 推荐的方式:包装成微件

例如最常见的动作微件:

<<wikitext-example """
<$button>
<$action-confirm $message="真的要让本条目自毁吗?" $prompt={{$:/state/promptUser}}>
<$action-deletefield $tiddler="在太微里使用JavaScript" $field="text"/>
</$action-confirm>
删除本条目
</$button>
""">>

详见[[弹框确认微件action-confirm]]里的解析,它就是对 `window.confirm` 这个 JS 调用的包装。

此外[[Modern.TiddlyDev|https://tiddly-gittly.github.io/Modern.TiddlyDev/]]的教程里也介绍了如何创建一个简单的按钮微件

!! 包装成事件监听器

看看消息是如何在[[消息发送微件action-sendmessage]]里发送的,以及是如何在[[微件收发信息addEventListener和dispatchEvent]]里接收的。

!! 直接放入 RawMarkup

详见[[SystemTag: $:/tags/RawMarkup|https://tiddlywiki.com/#SystemTag%3A%20%24%3A%2Ftags%2FRawMarkup]]

这适合要全局生效的脚本,不过这样的用法不多,这个功能一般是用于放 Google Analysis 之类的第三方服务,或添加字体 CSS 用([[如何添加在线字体]])
68 changes: 68 additions & 0 deletions tiddlers/弹框确认微件action-confirm.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
created: 20240327064131431
creator: 林一二
modified: 20240327064451603
modifier: 林一二
tags: JS微件
title: 弹框确认微件action-confirm


```ts
import { widget as Widget } from '$:/core/modules/widgets/widget.js';
import { IChangedTiddlers } from 'tiddlywiki';
import './index.css';

// 定义 ConfirmWidget 类,继承自 Widget
class ConfirmWidget extends Widget {
private message: string;
private prompt: boolean;

// 构造函数,初始化 ConfirmWidget
constructor(parseTreeNode: any, options: any) {
super(parseTreeNode, options);
this.execute();
}

// 计算微件的内部状态
execute() {
this.message = this.getAttribute('$message', $tw.language.getString('ConfirmAction'));
this.prompt = this.getAttribute('$prompt', 'yes') !== 'no';
this.makeChildWidgets();
}

// 将此微件渲染到 DOM 中
render(parent: Element, nextSibling: Element) {
this.parentDomNode = parent;
this.renderChildren(parent, nextSibling);
}

// 刷新微件,确保属性是最新的
refresh(changedTiddlers: IChangedTiddlers): boolean {
let changedAttributes = this.computeAttributes();
if (changedAttributes['$message'] || changedAttributes['$prompt']) {
this.refreshSelf();
return true;
}
return this.refreshChildren(changedTiddlers);
}

// 触发与此微件相关联的动作
invokeAction(triggeringWidget: Widget, event: Event): boolean {
let invokeActions = true;
if (this.prompt) {
invokeActions = window.confirm(this.message);
}
return invokeActions ? this.invokeActions(triggeringWidget, event) : false;
}

// 控制动作传播的方法
allowActionPropagation(): boolean {
return false;
}
}

// 将 ConfirmWidget 类导出为太微微件
declare let exports: {
[key: string]: typeof ConfirmWidget;
};
exports['action-confirm'] = ConfirmWidget;
```
88 changes: 88 additions & 0 deletions tiddlers/微件收发信息addEventListener和dispatchEvent.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
created: 20240327064646408
creator: 林一二
modified: 20240327065601795
modifier: 林一二
tags: JS微件
title: 微件收发信息addEventListener和dispatchEvent

[[消息发送微件action-sendmessage]]的原理是调用 `dispatchEvent`。

在微件的方法里调用 `dispatchEvent` 会实际调用 `Widget` 这个父类上的 `dispatchEvent` 方法,然后逐级顺着微件树向上传递,最终传到 `$tw.rootWidget` 上。

最终遇到通过 `$tw.rootWidget.addEventListener` 注册了的事件监听器,或中间某一级微件注册了监听器,就会开始执行监听器里的逻辑。

```ts
class Widget {
private eventListeners: EventListener = {};

// 向微件添加事件监听器
addEventListener(type: string, handler: string | ((event: any) => boolean)) {
const self = this;
if (typeof handler === 'string') {
// 如果 handler 是字符串,则认为是这个微件上的一个方法名
this.eventListeners[type] = function(event) {
return self[handler].call(self, event);
};
} else {
// 如果 handler 是函数,则直接作为事件处理函数
this.eventListeners[type] = function(event) {
return handler.call(self, event);
};
}
}

// 向微件派发事件
dispatchEvent(event: any) {
event.widget = event.widget || this;
// 如果这个微件处理了这个事件,则派发它
const listener = this.eventListeners[event.type];
if (listener) {
// 如果监听器返回 false,则不再向上传播事件
if (!listener(event)) {
return false;
}
}
// 否则,将事件派发给父级微件
if (this.parentWidget) {
return this.parentWidget.dispatchEvent(event);
}
return true;
}

// 其他类方法
}
```

使用方法

```ts
class MyWidget extends Widget {
constructor(parseTreeNode: any, options: any) {
super(parseTreeNode, options);
// 用法一:为 'myCustomEvent' 事件注册监听器
// 注意这只能捕获子微件,也就是被包在当前微件里的其它微件
this.addEventListener('myCustomEvent', this.handleMyCustomEvent);

// 用法二:在根微件上注册监听器,这可以捕获任何地方发送的消息
$tw.rootWidget.addEventListener("tm-scroll",function(event) {
$tw.pageScroller.handleEvent(event);
});
}

// 定义一个方法来处理事件
handleMyCustomEvent(event: any): boolean {
console.log('接收到事件:', event);
// 返回 false 停止事件传播
return false;
}

// 在微件的某个地方触发事件
someMethod() {
// 方法一:在自己身上发,然后让它逐级冒泡上去
this.dispatchEvent({ type: 'myCustomEvent', data: '一些数据' });

// 方法二:直接发到根微件上
$tw.rootWidget.dispatchEvent({type: "tm-auto-save-wiki"});
}
}
```
6 changes: 3 additions & 3 deletions tiddlers/插件开发.tid
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
created: 20211003091131848
creator: 林一二
modified: 20230207151252799
modified: 20240327063401379
modifier: 林一二
page-cover: https://tiddlywiki.com/talkytalky/images/TiddlyWiki%2520Architecture.svg
tags: 欢迎来到太微!
Expand All @@ -11,15 +11,15 @@ type: text/vnd.tiddlywiki

!!! WikiText 插件

只需要通读[[中文文档]]了解所有 WikiText 语法即可,然后推荐使用[[Gatha插件来开发|如何在单文件版里写一个TW插件]]。
只需要通读[[中文文档]]了解所有 WikiText 语法即可,然后对 JavaScript 不熟悉的用户推荐使用[[Gatha插件来开发|如何在单文件版里写一个TW插件]];对 JS 和 TS 熟悉的用户或想开发功能强大插件的则可以阅读[[Modern.TiddlyDev|https://tiddly-gittly.github.io/Modern.TiddlyDev/]]的教程,来学习功能更强大但有更多内容要学的高级开发方式

!!! JS 高级插件

可以阅读[[太微原理]]里的一些指南。

因为太微本身的 API 文档不完善,而且没有原生的 TS 类型。所以目前的学习主要靠阅读其它插件的代码,来了解写法。

小伙伴们也在把 API 用法整理到[[TW5-Typed|https://github.com/tiddly-gittly/TW5-Typed]]里,方便使用 TS 开发 JS 插件。
小伙伴们也在把 API 用法整理到[[TW5-Typed|https://tiddly-gittly.github.io/TW5-Typed/]]里,方便使用 TS 开发 JS 插件。

!!! 值得一读的插件

Expand Down
Loading

0 comments on commit 51fb509

Please sign in to comment.