Здравствуйте, меня не зовут Дмитрий Карловский и я.. решил написать простенькие заметки на $mol в несколько итераций:
парой команд - это делается один раз под все проекты:
git clone https://github.com/hyoo-ru/mam.git
cd mam
npm install
Запускаем дев сервер:
npm start
Он заиграет на 9080 порту http://127.0.0.1:9080, а нам можно начинать смолить..
Создаём наш проект: в склонированной папке mam создаем папки habr/notes В нём создём точку входа habr/notes/index.html:
<!doctype html>
<html mol_view_root>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
</head>
<body mol_view_root>
<div mol_view_root="$habr_notes"></div>
<script src="web.js" charset="utf-8"></script>
</body>
</html>
Заметили $habr_notes
? Тут везде fqn обращения (имя = путь), а в mol_view_root
мы указываем наше приложение. Точнее, компонент - здесь это одно и то же по смыслу, только мы можем вкладывать компоненты друг в друга и делегировать/рекомпозировать/хакать их свойства между собой.
Что же, создадим сам компонент habr/notes/view.tree:
- это комментарий, ах да, следим за табами
$habr_notes $mol_page
Шпаргалка по синтаксису и страшная статья о view.tree
В браузере можем перейти по адресу http://127.0.0.1:9080/habr/notes и увидим зачаток нашего приложение:
Придадим форму, подправив файл view.tree:
- мы отнаследовали $habr_notes от $mol_page
$habr_notes $mol_page
- в свойство body мы положили массив /
body /
- в этот массив мы вложили компонент $mol_textarea и назвали его Input
<= Input $mol_textarea
О, теперь у нас появилось текстовое поле ввода:
Но наш введенный текст теряется при обновлении страницы, давайте это изменим.
в localStorage
Подправим наш view.tree файл до такого содержания:
$habr_notes $mol_page
body /
<= Input $mol_textarea
- переопределяем наше свойство value
value?val <=> text?val \
подробнее про свойства компонента $mol_textarea туть
Собственно на этом момент можно уже начать задаваться вопросом какого черта тут происходит и зачем нужны эти view.tree файлы.
view.tree - это DSL, транслирующийся в ts.
Пример выше преобразится в такой код:
namespace $ {
export class $habr_notes extends $mol_page {
/**
* ```tree
* body / <= input
* ```
*/
body() {
return [
this.input()
] as readonly any[]
}
/**
* ```tree
* text?val \
* ```
*/
@ $mol_mem
text(val?: any) {
if ( val !== undefined ) return val as never
return ""
}
/**
* ```tree
* Input $mol_textarea value?val <=> text?val
* ```
*/
@ $mol_mem
Input() {
const obj = new this.$.$mol_textarea()
obj.value = (val?: any) => this.text(val)
return obj
}
}
}
Вы можете сами поиграться в песочнице.
Зачем он нужен? Для разделения мух и котлет.
С помощью view.tree мы декларативно описываем наш интерфейс. С помощью view.tree.ts мы императивно описываем работу самой логики.
Из view.tree сборщик сгенерировал шаблонный код в файл habr/notes/-view.tree/view.tree.ts
При обновлении view.tree файла, файл выше обновляется сам.
Зачем он нужен? Для переопределения!
Можем заметить, что код выше сгенерировался в неймспейсе $
.
Свою логику мы будем описывать в неймспейсе $.$$
- то есть, "перекрывая" своей логикой.
Вернёмся же к нашему коду. Создаём файл habr/notes/view.tree.ts:
namespace $.$$ {
export class $habr_notes extends $.$habr_notes {
@ $mol_mem
text(val?: any) {
if ( val !== undefined ) return val as never
return ""
}
}
}
Тут я перенёс text(val?: any)
- функция-заглушка, сгенерированная из view.tree файла с навешенным декоратором @ $mol_mem
(от memoization)
Это канал - он возвращает значение, при вызове его без параметра, а при вызове с параметром он указывается в качестве значения.
Почему оно работает?
Мы открыли наше приложение, поле ввода запросило значение из функции text
, функция text
вернуло пустую строку.
Мы ввели первый символ, в функцию text
передалась нажатая клавиша, функция text
сохранила её.
Мы вводим последующие символы, они запоминаются и результат функции text
возвращается в поле ввода.
Ага, значит сюда и можно подвязать сохранение данных в localStorage.
В $mol есть модуль $mol_state_local - реактивная обертка над localStorage - ей и воспользуемся:
namespace $.$$ {
export class $habr_notes extends $.$habr_notes {
@ $mol_mem
text(val?: any) {
return $mol_state_local.value('note', val) || ""
}
}
}
Проверяем.. работает! После перезагрузки страницы наш введённый текст сохраняется.
Я создал репозиторий с веткой stage-1, действия выше воспроизведены в нём - можно сравниться.
Что же, осталось научиться шариться данными!
собственно,
заметками, используя децентрализованную криптографическую систему, ну а почему нет ¯\(ツ)/¯
Поправим view.tree файл:
$habr_notes $mol_page
- через синк клиент мы обмениваемся данными в сети
yard $hyoo_sync_client
peer <= Peer null
tools /
- через модуль онлайна мы подключаемся к пирам и синхронизируемся
<= Online $hyoo_sync_online
yard <= yard
body /
<= Input $mol_textarea
value?val <=> text?val \
enabled <= i_have_can_mod false
Опишем логику view.tree.ts:
namespace $.$$ {
export class $habr_notes extends $.$habr_notes {
@$mol_mem
text( val?: any ) {
return this.store().str( val ) || ""
}
@$mol_mem
store() {
// через модуль клиента мы обращаемся к хранилищу (миру),
// используем тип $hyoo_crowd_reg - он позволяет атомарно хранить значения (строки, числа, булы)
// запрашиваем участок с указанным id
return this.yard().world().Fund( $hyoo_crowd_reg ).Item( this.current_note_id() )
}
@$mol_mem
current_note_id() {
// мы считываем id заметки из урла, или, если пустой, указываем новый свой
const id = this.$.$mol_state_arg.value( '' ) || this.yard().land_grab().id()
// задаём в урл текущий id - чтобы мы могли его сразу же скопировать
this.$.$mol_state_arg.value( '', id )
return id as $mol_int62_string
}
@$mol_mem
i_have_can_mod() {
// проверка наличия прав на редактирование
return this.yard().land( this.current_note_id() ).allowed_mod()
}
}
}
Для сверки, ветка stage-2
Добавим поддержку тем тройкой строк кода:
$habr_notes $mol_page
plugins /
- включаем поддержку смены тем
<= Theme $mol_theme_auto
yard $hyoo_sync_client
tools /
<= Online $hyoo_sync_online
yard <= yard
- добавляем кнопку-переключалку
<= Lighter $mol_lights_toggle
body /
<= Input $mol_textarea
value?val <=> text?val \
enabled <= i_have_can_mod false
А может сделаем наше pwa offline-first? Пфф: создаем в той же директории файл view.meta.tree:
include \/mol/offline/install
Одной строчкой мы подключили Service Worker, который закеширует наше приложение и позволит работать ему при отсутствии интернетов. Зачем? CROWD, который мы используем, автоматически синхронизируется с внешним миром при потери и появлении интернета.
Для сверки, ветка stage-3
мы командой:
npm start habr/notes
Наш статический бандл расположится в папке habr/notes/-/
- его и можно хостить.
В ходе всех вышеописанных шагов можно заметить появившиеся папки:
-/
- наш бандл-view.tree/
- наш переопределяемый код
Тут продуманно, что весь генерируемый код с которым мы не взаимодействуем лежит в минусах, это сделано для упрощения работы с тем же гитом.
Например, файл .gitignore
будет таков:
-*
Отвлеклись, задеплоимся на какой-нибдь фапхаб с возможностью хостить статические странички - тот же Github Pages.
Выгрузим содержимое папки -
в ветку gh-pages и можем лицезреть результаты наших работ по ссылке:
https://koplenov.github.io/habr-notes/#!=sxktck_j3e02v
Схожее по теме:
- https://notes.hyoo.ru/ - Ведения личных заметок с напоминаниями. ** Сорцы: https://github.com/hyoo-ru/notes.hyoo.ru
- https://page.hyoo.ru/ - Сервис написания и публикации статей с совместным редактированием даже в оффлайне. ** Сорцы: https://github.com/hyoo-ru/page.hyoo.ru/
- Запись стрима, где мы делали таск менеджер и обкатывали реалтаймовость