akio's blogger
2025年2月27日木曜日
2人目の育休をはじめて3ヶ月が経ちました
2023年12月18日月曜日
ReactのWebComponentsでslotされたものを使い回す
この記事は Uzabase Advent Calendar 2023の18日目 の記事です。
はじめに
UzabaseのProduct Divisionではプロダクトのフロントエンドコンポーネントを他のプロダクトでも再利用できるようWebComponentsを利用しています。WebComponentsにはスロットという機能があり、slotタグによって外部から要素を渡すことができます。
たとえば、ローディングスピナーを表示するspinner-slotという自作要素をlitで作ってみます。2つ上下にローディングスピナーは出ますが、下の方はslotによって外部から差し込まれたスピナーに置き換わっています。
次のように表示されます。
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>spinner slot</title> | |
<script type="module" src="/index.js"></script> | |
<style> | |
.another-spinner { | |
width: 32px; | |
height: 32px; | |
margin: 10px auto; | |
border: 4px #ddd solid; | |
border-top: 4px red solid; | |
border-radius: 50%; | |
animation: another-spinner-anime .45s infinite linear; | |
} | |
@keyframes another-spinner-anime { | |
100% { | |
transform: rotate(360deg); | |
} | |
} | |
</style> | |
</head> | |
<body style="display: flex; justify-content: center;"> | |
<div> | |
<spinner-slot><!-- <div class="another-spinner"></div> --></spinner-slot> | |
<spinner-slot><div class="another-spinner"></div></spinner-slot> | |
</div> | |
</body> | |
</html> |
import {LitElement, css, html} from 'lit-element'; | |
class MyElement extends LitElement { | |
static styles = css` | |
.spinner { | |
width: 32px; | |
height: 32px; | |
margin: 10px auto; | |
border: 4px #ddd solid; | |
border-top: 4px blue solid; | |
border-radius: 50%; | |
animation: spinner-anime 1.0s infinite linear; | |
} | |
@keyframes spinner-anime { | |
100% { | |
transform: rotate(360deg); | |
} | |
} | |
`; | |
render() { | |
return html` | |
<div> | |
<slot> | |
<div class="spinner"></div> | |
</slot> | |
</div> | |
`; | |
} | |
} | |
customElements.define('spinner-slot', MyElement); |
業務の中で、このスロット機能を利用して複数の場所で差し込まれた要素を使おうとしました。今回は名前付きslotを利用して、spinner-slotの中で2つの名前付きslot(another-icon)を表示するようにしてみます。
次のように表示されます。
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>spinner slot</title> | |
<script type="module" src="/index.js"></script> | |
<style> | |
.another-spinner { | |
width: 32px; | |
height: 32px; | |
margin: 10px auto; | |
border: 4px #ddd solid; | |
border-top: 4px red solid; | |
border-radius: 50%; | |
animation: another-spinner-anime .45s infinite linear; | |
} | |
@keyframes another-spinner-anime { | |
100% { | |
transform: rotate(360deg); | |
} | |
} | |
</style> | |
</head> | |
<body style="display: flex; justify-content: center;"> | |
<div> | |
<spinner-slot><!-- <div class="another-spinner"></div> --></spinner-slot> | |
<spinner-slot><div slot="another-icon" class="another-spinner"></div></spinner-slot> | |
</div> | |
</body> | |
</html> |
import {LitElement, css, html} from 'lit-element'; | |
class MyElement extends LitElement { | |
static styles = css` | |
.spinner { | |
width: 32px; | |
height: 32px; | |
margin: 10px auto; | |
border: 4px #ddd solid; | |
border-top: 4px blue solid; | |
border-radius: 50%; | |
animation: spinner-anime 1.0s infinite linear; | |
} | |
@keyframes spinner-anime { | |
100% { | |
transform: rotate(360deg); | |
} | |
} | |
`; | |
render() { | |
return html` | |
<div> | |
<slot name="another-icon"> | |
<div class="spinner"></div> | |
</slot> | |
<slot name="another-icon"> | |
<div class="spinner"></div> | |
<slot> | |
</div> | |
`; | |
} | |
} | |
customElements.define('spinner-slot', MyElement); |
あれれ、これはうまくいきません。どうやら子要素は1つのslotにしかアサインされないようです。ということは、another-icon-1のように数字をつけたりしてslot nameを変えて欲しいだけの要素を差し込むしかないみたいです。同じものを出したいだけなのにスッキリしないですよね。なので、どうにかして1つの要素を差し込むだけでOKにしてみます。
これが完成形だ!?
業務ではReactを使ってWebComponentを作っていたので、今回はReactで表現してみます。
isEvenという名前の要素を1つ差し込むだけで、slotにアサインされている要素をuseRefで参照しています。
Templateコンポーネントの中で、参照した要素をcloneNodeでコピーして表示するようにしました。
ただ、この方法でやりたいことはできましたが、描画の最初のほうでアサインされた元のslotが一瞬見えてしまう課題がでてきました。
おわりに
今回は1つの要素を差し込むだけで複数のslotに表示させてみました。ただし、課題も残っています。スクリプトをロードするまで描画しないようにしたら解消できるかもしれません。解決した方はコメントをお願いします。
ちなみに、atomicoというフレームワークで用意されているuseSlotを参考に作りました。そちらでは自前で実装する必要はなさそうです。atomicoもぜひご覧ください。
2023年10月13日金曜日
psqlとmysqlで接続を楽にしたいよね?しよっか
※記憶ベースで書いているところがほとんどなので、うまく行かない場合は参考情報をみていただけると大変助かります。
はじめに
運用・開発でデータベースに接続する機会は結構あるもので、いろんなところに接続しなきゃならないときがあります。でも、接続先とかユーザーとかパスワードとか覚えてられないですよね?接続を楽にしたいですよね?しましょう。
今回は僕がよく使っているMySQLとPostgreSQLで接続を楽してみます。
mysql
mysql_config_editorというものがあり、そこに接続プロファイルを書いてもらいます。プロファイルはホームディレクトリ下の~/.mylogin.confに難読化されて保存されます。
接続プロファイルを記録する
mysql_config_editor set -G ${profile-name} -h ${host} -P ${port} -u ${user} -p
mysql_config_editor set -G mydatabase-profile -h localhost -P 3306 -u akio -p
でパスワードを聞かれるので答えてください。これで記録されます。
使うときは
mysql --login-path=mydatabase-profile
で接続できるはずです。
mysql参考
より詳細なQiita記事はこちらにあります。
mysql_config_editorに使えるオプションはこちら。print -allでプロファイル一覧を出せます。
MySQL :: MySQL 8.0 リファレンスマニュアル :: 4.6.7 mysql_config_editor —
MySQL 構成ユーティリティー
psql
パスワードと接続プロファイルをホームディレクトリ下の2つのファイルに分けて管理します。
接続プロファイルは ~/.pg_service.conf に次のように書きます。
[mydatabase-profile]
host=localhost
port=5432
user=akio
dbname=mydb
パスワードは ~/.pgpass に次のように書きます。
localhost:5432:mydb:akio:mypassword
~/.pgpassは chmod 600 しておきます。これよりゆるくするとpsqlに読み取ってもらえなくなります。
接続するときは次のように書きます。
psql "service=mydatabase-profile" -w
-wでパスワードを聞かれなくなりますが、その代わり.pgpassを探しに行ってくれます。
ちなみに、.pgpassの中身をパスワード以外の部分で一致させて探すので少しでもserviceの内容と違うと取ってこれないかも...と思っています。ワイルドカードは使えそうなので適宜使うと良いのかもしれません。
psql参考
.pg_service.confにかける他パラメータはこちら。
.pgpassについて詳細なQiita記事はこちら。
pgpassについての公式はこちら。
psqlのオプションはこちら。
おわりに
mysqlとpsqlで接続プロファイルの保存方法に違いがあることが少々気になりました。mysqlは難読化している一方、psqlはファイル権限制御で読み取られにくくしています。目的は同じでも実装方法が異なる点が、どういう経緯でそうなったのか興味をひきました。誰か教えてください。(´・ω・`) <-(そこまで知りたいとは思っていない顔)
2023年8月28日月曜日
「くもんのうた200えほん」をカメレオンリーダーのような音声タッチペンで便利にする
こんにちは。
先日、妻が「カメレオンリーダー」というものを紹介してくれました。
今回の成果物の図 |
カメレオンリーダーとは
カメレオンリーダーと「くもんのうた200えほん」を組み合わせて、音声タッチペンで童謡がいつでも聴けるというのです。
それは便利だ、と思い「いいじゃん、買っちゃえば?」と言ったら
「でも、高い」と。
え、そうなの?と商品サイトを見てみると、シールセット + タッチペンで 18,700円!
確かに高い...。
でも、この仕組みはどこかで見たことあるなーと思い、類似品を検索すると「G-Talk」という製品があるらしいです。くもんやZ会にも同じような仕組みのペンがあるらしいのですが、だいたいこのG-Talkの系列とのこと。しかし、G-TalkもAmazonで12,100円と高い...。
もう少し調べると、Z会のエブリスピークを英語の絵本用に改造した人がいました。
エブリスピークは中古で流通しているようで、メルカリで1400円で買えました。また、シール自体はとても安く、Amazonで1500円(送料込み)で買えます。合計3000円弱で、カメレオンリーダーと同じことができるのです。
MaiYaPenが欲しかったけど高いのでZ会のエブリスピークで代用してみた – BeeLabo
音声タッチペンの作り方
まず、「くもんのうた200えほん」の楽曲データをダウンロードしてきます。Amazonで妻が買ったのですが、妻曰く、「Webで楽曲データを落とすのはバグがあって大変だからAmazon Musicのアプリで落とした方がいい」とのこと。Web版はどうもチェックボックスにバグがあって一括ダウンロードができないらしいです。
ダウンロードしてきたら、次はエブリスピークのmicroSDに楽曲データを入れます。これは所定のフォルダ名とファイル名にしないといけません。200曲全部を処理するのは大変なのでプログラムを組みました。こちらは自由にお使いください。Macを持っていたらPython3が入っているはずなので動かせると思います。ディレクトリはこのように配置すると良いです。
プログラムを動かすとこんな感じで作られます。
1シート目の127番目が「およげ、たいやきくん」になっていたら成功だと思います。
import glob | |
import re | |
import os | |
import math | |
import shutil | |
import sys | |
# 最大ファイルサイズ | |
MAX_FILE_SIZE = 128 | |
SHEET_PREFIX = "D231300" | |
class TargetDirExistException(Exception): | |
pass | |
class InvalidIdException(Exception): | |
pass | |
def atoi(text): | |
return int(text) if text.isdigit() else text | |
def natural_keys(text): | |
return [ atoi(c) for c in re.split(r'(\d+)', text) ] | |
def find_files(): | |
return sorted(glob.glob("./*/*.mp3"), key=natural_keys) | |
def create_dir(dir_path): | |
if not os.path.isdir(dir_path): | |
os.mkdir(dir_path) | |
return dir_path | |
else: | |
raise TargetDirExistException(f"想定外のフォルダが存在します。該当フォルダを移動または削除してください。: {dir_path}") | |
def create_seq_dirs(dir_id, files): | |
dir_count = math.ceil(len(files) / MAX_FILE_SIZE) | |
prefix = dir_id[:1] | |
first_id = int(dir_id[1:]) | |
dir_paths = [] | |
for i in range(dir_count): | |
dir_paths.append(create_dir(f"{prefix}{first_id + i}")) | |
return dir_paths | |
def place_files(files, seq_dirs): | |
dir_index = 0 | |
target_dir = seq_dirs[dir_index] | |
i = 0 | |
for file in files: | |
if i >= MAX_FILE_SIZE: | |
i = 0 | |
dir_index += 1 | |
target_dir = seq_dirs[dir_index] | |
continue | |
shutil.copy2(file, f"{target_dir}/DS{str(i).zfill(3)}.mp3") | |
i += 1 | |
def main(): | |
try: | |
print("楽曲フォルダを作成するプログラムです。いつでも Ctrl + C でプログラムは止められます。") | |
input_string = input(f"{SHEET_PREFIX}より下の桁のシートのIDを入力してください(例: D231300123450 -> 123450): ") | |
if len(input_string) != 6: | |
raise InvalidIdException("シートIDは12桁です。") | |
y_n = input(f"シート: {SHEET_PREFIX + input_string} から楽曲フォルダを作成しますか? (y/N): ") | |
if y_n == "y": | |
files = find_files() | |
seq_dirs = create_seq_dirs(SHEET_PREFIX + input_string, files) | |
place_files(files, seq_dirs) | |
print("作成完了") | |
sys.exit(0) | |
except TargetDirExistException as tdee: | |
print(tdee) | |
sys.exit(1) | |
except InvalidIdException as iie: | |
print(iie) | |
sys.exit(1) | |
if __name__ == "__main__": | |
main() |
最後に、シールを貼ったら完成です。妻が頑張りました。
細かいところは参考に上げたリンクをご覧ください。
MaiYaPenが欲しかったけど高いのでZ会のエブリスピークで代用してみた – BeeLabo
カメレオンリーダー vs エブリスピーク
一応、カメレオンリーダーと比較しておくと
1. かわいさはカメレオンリーダーの方がいい。
2. SDカードのキャップはネジで閉じられているエブリスピークの方がいい。
3. 音質は気にならない。(youtubeなどの紹介では録音のためか音が割れている気がする)
4. 乾電池(エネループも可)のエブリスピークの方が外出先で比較的楽だと思う。(ちなみに、カメレオンリーダーは充電式だけど、USB type-Cじゃないので使いにくそう)
というわけで、1歳になったばかりの娘とこれで遊びたいと思います。
2023年8月17日木曜日
子供が1才を迎えて変化したこと
哺乳瓶がいらなくなった
ベッドを我が物にする
お風呂が平気になる
大人でも食べられそうなご飯を食べる
2023年6月11日日曜日
離乳食のおやきを作りたくない妻の話
|
娘に食べられるために生まれてきたおやき群 |
2023年5月27日土曜日
スプレッドシートでWebサイトをスクレイピングしてセルの中できれいに整形する (IMPORTXML, TEXTJOIN)
はじめに
スプレッドシートでWebスクレイピング
![]() |
初手B2 |
Excelの新規データ作成するときの「初手」ってあるじゃないですか。私はB2が初手であることが多いんですが定石はA1ですか?? pic.twitter.com/SkiqN904zs
— 影総務部長@特定社会保険労務士 (@ShadowSR1976) May 10, 2023
![]() |
取得したい箇所を選ぶ |
![]() |
Copy XPathを選ぶ |