時間 | - {% for r in rooms %} - {% assign rstyle = tt.room_styles[r] %} + {% comment %} ルーム単位でヘッダーを描画 {% endcomment %} + {% for room in rooms %}- {{ r }} + style="--room-color: {{ room.style.color | default: '#c43b3b' }};"> + {{ room.name }} | {% endfor %}|||
---|---|---|---|---|
{{ h }}:{{ mf }} | - - {% for r in rooms %} - {% assign rstyle = tt.room_styles[r] %} - {% assign events_in_room = tt.events | where: 'room', r | sort: 'start' %} - - {% assign active_event = nil %} - {% assign active_event_start_index = nil %} - - {% for ev in events_in_room %} - {% assign s_h = ev.start | split: ':' | first | plus: 0 %} - {% assign s_m = ev.start | split: ':' | last | plus: 0 %} - {% assign e_h = ev.end | split: ':' | first | plus: 0 %} - {% assign e_m = ev.end | split: ':' | last | plus: 0 %} - {% assign s_min = s_h | times: 60 | plus: s_m %} - {% assign e_min = e_h | times: 60 | plus: e_m %} - - {% assign s_clamped = s_min %} - {% if s_clamped < day_start_min %}{% assign s_clamped = day_start_min %}{% endif %} - {% assign e_clamped = e_min %} - {% if e_clamped > day_end_min %}{% assign e_clamped = day_end_min %}{% endif %} - - {% assign span_minutes = e_clamped | minus: s_clamped %} - {% if span_minutes > 0 %} - {% assign numerator = span_minutes | plus: slot | minus: 1 %} - {% assign span_slots = numerator | divided_by: slot %} - {% assign s_index = s_clamped | minus: day_start_min | divided_by: slot %} - {% assign e_index = s_index | plus: span_slots %} - {% if i >= s_index and i < e_index %} - {% assign active_event = ev %} - {% assign active_event_start_index = s_index %} - {% endif %} - {% endif %} - {% endfor %} - - {% if active_event and i == active_event_start_index %} - {%- assign s_h = active_event.start | split: ':' | first | plus: 0 -%} - {%- assign s_m = active_event.start | split: ':' | last | plus: 0 -%} - {%- assign e_h = active_event.end | split: ':' | first | plus: 0 -%} - {%- assign e_m = active_event.end | split: ':' | last | plus: 0 -%} - {%- assign s_min = s_h | times: 60 | plus: s_m -%} - {%- assign e_min = e_h | times: 60 | plus: e_m -%} - {%- if s_min < day_start_min -%}{%- assign s_min = day_start_min -%}{%- endif -%} - {%- if e_min > day_end_min -%}{%- assign e_min = day_end_min -%}{%- endif -%} - {%- assign span_minutes = e_min | minus: s_min -%} - {%- assign numerator = span_minutes | plus: slot | minus: 1 -%} - {%- assign span_slots = numerator | divided_by: slot -%} - {%- assign accent = active_event.accent | default: rstyle.color -%} -
-
- {{ active_event.start }}–{{ active_event.end }}
- {{ active_event.title }}
- {% if active_event.subtitle %}
- {{ active_event.subtitle }}
- {% endif %}
- {% if active_event.badge %}
- {{ active_event.badge }}
- {% endif %}
+ {{ time_labels[slot] }} |
+
+ {% comment %} 各イベントを描画 {% endcomment %}
+ {% for room_index in (0..total_rooms) %}
+ {% assign event = events[slot][room_index] %}
+ {% assign room = rooms[room_index] %}
+
+ {% if event == 'continued' %}
+ {% comment %} イベント継続中 (rowspan で描画するため出力は不要) {% endcomment %}
+ {% elsif event %}
+ {% comment %} イベントを描画 {% endcomment %}
+ {% assign accent = event.accent | default: room.style.color | default: '#c43b3b' %}
+
+
+ |
- {% elsif active_event == nil %}
+ {% else %}
+ {% comment %} イベント無し {% endcomment %}
+
{{ event.start }}–{{ event.end }}
+ {{ event.title }}
+ {% if event.subtitle %}{{ event.subtitle }} {% endif %}
+ {% if event.badge %}{{ event.badge }}{% endif %}
+ {% if event.note %}{{ event.note }} {% endif %}
{% endif %}
{% endfor %}
diff --git a/_plugins/time_table_generator.rb b/_plugins/time_table_generator.rb
new file mode 100644
index 00000000..5c13dd18
--- /dev/null
+++ b/_plugins/time_table_generator.rb
@@ -0,0 +1,100 @@
+module Jekyll
+ module TimeTableGenerator
+ # タイムテーブル表を事前に計算してイベント表形式に変換
+ # これにより、Liquid テンプレートは単純な表示のみを担当
+ class Generator < Jekyll::Generator
+ safe true
+ priority :high
+
+ # デフォルト設定値
+ DEFAULT_SLOT_MINUTES = 15
+ DEFAULT_DAY_START_HOUR = 10 # 10:00
+ DEFAULT_DAY_END_HOUR = 16 # 16:00
+
+ def generate(site)
+ tt = site.data['time_table']
+ return unless tt
+
+ # 設定値を取得
+ slot_minutes = tt.fetch('slot_minutes', DEFAULT_SLOT_MINUTES)
+ day_start = tt.fetch('day_start_hour', DEFAULT_DAY_START_HOUR)
+ day_end = tt.fetch('day_end_hour', DEFAULT_DAY_END_HOUR)
+ rooms = tt.fetch('rooms', [])
+ events = tt.fetch('events', [])
+ room_styles = tt.fetch('room_styles', {})
+
+ # イベント情報を表形式で生成
+ time_table_events = create_event_table(events, rooms, room_styles, slot_minutes, day_start, day_end)
+
+ # 生成したイベント表データを Liquid に提供
+ site.data['time_table_events'] = time_table_events
+ end
+
+ private
+
+ def create_event_table(events, rooms, room_styles, slot_minutes, day_start, day_end)
+ total_slots = ((day_end - day_start) * 60 / slot_minutes).to_i
+
+ # 時間ラベルを生成
+ time_labels = (0...total_slots).map do |slot|
+ minutes = day_start * 60 + slot * slot_minutes
+ "#{minutes / 60}:%02d" % (minutes % 60)
+ end
+
+ # ルーム情報を生成(room.style でアクセス可能)
+ rooms_data = rooms.map do |room_name|
+ {
+ 'name' => room_name,
+ 'style' => room_styles[room_name] || {}
+ }
+ end
+
+ # イベント表を生成(2次元配列)
+ table = Array.new(total_slots) { Array.new(rooms.size) }
+
+ events.each do |event|
+ place_event(event, table, rooms, slot_minutes, day_start, total_slots)
+ end
+
+ {
+ 'events' => table,
+ 'rooms' => rooms_data,
+ 'time_labels' => time_labels,
+ 'total_slots' => total_slots - 1, # Liquidの (0..n) は inclusive なので -1
+ 'total_rooms' => rooms.size - 1, # Liquidの (0..n) は inclusive なので -1
+ }
+ end
+
+ def place_event(event, table, rooms, slot_minutes, day_start, total_slots)
+ room_index = rooms.index(event['room'])
+ return unless room_index
+
+ # 時間を分に変換して揃える
+ event_start = time_to_minutes(event['start'])
+ event_end = time_to_minutes(event['end'])
+
+ # スロット計算(分に揃える)
+ slot_start = [(event_start - day_start * 60) / slot_minutes, 0].max.to_i
+ slot_end = [(event_end - day_start * 60) / slot_minutes, total_slots].min.to_i
+ duration = slot_end - slot_start
+
+ return if slot_start >= total_slots || duration <= 0
+
+ # イベントの長さ情報 (duration) を追加
+ table[slot_start][room_index] = event.merge('duration' => duration)
+
+ # 継続スロットをマーク
+ (slot_start + 1...slot_end).each do |slot|
+ break if slot >= total_slots
+ table[slot][room_index] = 'continued'
+ end
+ end
+
+ def time_to_minutes(time_str)
+ return 0 unless time_str
+ hours, minutes = time_str.split(':').map(&:to_i)
+ hours * 60 + minutes
+ end
+ end
+ end
+end
| |