diff --git a/_pages/time-table.html b/_pages/time-table.html index 23c2e82c..e87ea4b9 100644 --- a/_pages/time-table.html +++ b/_pages/time-table.html @@ -5,16 +5,17 @@ --- {% include navbar.html %} -{% assign tt = site.data.time_table %} -{% assign slot = tt.slot_minutes | default: 15 %} -{% assign rooms = tt.rooms %} -{% assign room_count = rooms | size %} +{% comment %} + Jekyll プラグインで事前計算されたタイムテーブル表を使用 + ロジックはプラグインで計算済み。Liquid は描画のみを担当 +{% endcomment %} -{% assign day_start_min = 600 %} -{% assign day_end_min = 960 %} -{% assign total_minutes = day_end_min | minus: day_start_min %} -{% assign slots_count = total_minutes | divided_by: slot %} -{% assign last_row = slots_count | minus: 1 %} +{% assign tte = site.data.time_table_events %} +{% assign events = tte.events %} +{% assign rooms = tte.rooms %} +{% assign time_labels = tte.time_labels %} +{% assign total_slots = tte.total_slots %} +{% assign total_rooms = tte.total_rooms %}

@@ -23,94 +24,55 @@

- +
- {% for r in rooms %} - {% assign rstyle = tt.room_styles[r] %} + {% comment %} ルーム単位でヘッダーを描画 {% endcomment %} + {% for room in rooms %} {% endfor %} - {% for i in (0..last_row) %} - {% assign row_min = i | times: slot | plus: day_start_min %} - {% assign h = row_min | divided_by: 60 %} - {% assign mf = row_min | modulo: 60 | plus: 0 | prepend: '0' | slice: -2, 2 %} - + {% comment %} スロット単位(行単位)でイベントを描画 {% endcomment %} + {% for slot in (0..total_slots) %} - - - - {% 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 -%} - + + {% 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 %} {% 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
- {{ tt.date | default: site.date_event }} のタイムテーブル + {{ site.date_event }} のタイムテーブル
時間 - {{ r }} + style="--room-color: {{ room.style.color | default: '#c43b3b' }};"> + {{ room.name }}
{{ h }}:{{ mf }} -
-
{{ 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] }} +
+
{{ event.start }}–{{ event.end }}
+
{{ event.title }}
+ {% if event.subtitle %}
{{ event.subtitle }}
{% endif %} + {% if event.badge %}{{ event.badge }}{% endif %} + {% if event.note %}
{{ event.note }}
{% endif %}